-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlearning-notes
335 lines (247 loc) · 9.79 KB
/
learning-notes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
This will keep all the notes while I am learning coding with Flask
# create virtual environment for project
# purpose of using virtualenv:
##stand-alone env for each project, no affects for system python environment
## no affects to other projects
1. install virtualenv:
pip install virtualenv
2. create individual env for a project(environment folder usually stay in project folder):
virtualenv --system-site-packages --python=/usr/bin/python3.5 env_folder_name
3. delete/remove existing virtualenv:
sudo rm -rf env_folder
4. create a requirements.txt(in status of activate):
pip freeze > requirements.txt
5. install packages by requirements.txt(in status of activate):
pip install -r requirements.txt
# macro
1. in the same file(few macro involved)
1.1. create macro
{% macro input(name,value,type,placeholder) %}
input(name="{{ name }}",value="{{ vlaue }}",type="{{ type }}",placeholder="{{placeholder}}")
{% endmacro %}
1.2. using macro
{{input(type="text",placeholder="username")}}
2. store macros in a html file(many macros involved)
2.1. create macros
2.2. import one macro from a html file
{% from 'macro.html' import input as inp %}
2.3. import many macros from a html file
{% from 'macro.html' import input, render_content %}
# templates inherited
1. including file(not support editting of block content, including all the content from the parent file)
{% include 'base.html' %}
2. extends file(inherit from parent file and support editting block content)
{% extends 'base.html' %}
# block
1. define block
{% block block_name %}
content(optional)
{% endblock %}
2. using block(content must be put in the relative block defined in the parent file, or content will not rendered)
{% block block_name %}
custom content here
{% endblock %}
**message outside this block will not rendered!**
3. using super() in block
3.1. super(): add new content and keep old content
3.2. if super() not applied, new content will replace all the old content
# get relative path using url_for() function
1. using css/js/images(stored in "static" folder)
{{ url_for('static',filename='css/bootstrap.css') }}
2. get relative path of view function
{{ url_for('login') }} # return /login/
# filter
1. using filter for the value
{{ value|filter_name }} # built-in filters on site: http://jinja.pocoo.org/docs/templates/#builtin-filters
# Retrieving data from dict/list/object
1. {{ mydict['key']}}, {{ mylist['index'] }}, {{ myobj.myfunc() }}
# custom error page
1. define route for error page
@app.errorhandler(404)
def notFound(e)
return render_template('404.html', code=404)
2. customize error page in Blueprint
@blueprint_name.app_errorhandler(404)
def notFound(e):
return render_template('404.html', 404)
# flask_moment js extension
1. initiate moment.js
from flask_moment import Moment
moment = Moment(app)
2. call moment.js in html file
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.lang('en')}} # display in local language
{% endblock %}
3. display time with moment.js
{{ moment(datetime.utcnow()).format('LLL')}} # fromat: L-LLLL 0-4 levels time display from short type to long type
# flask_wtf forms
## IMPORTANT ##
All the fields must be put in the html file that you want to render, if any field missed, the function form.validate_on_submit
will ONLY RETURN False
ESPECIALLY FOR RadioField, it must be setup as a tuple list with strings:
CORRECT: choices=[('1','Male'),('2','Female')]
WRONG: choices=[(1,'Male'),(2,'Female')]
## IMPORTANT ##
1. create a form
from flask_wtf import FlaskForm
from wtforms.validator import Datarequired, Email
from wtforms import StringField,PasswordField
class myForm(FlaskForm):
name = StringField(label='username',validator=[Datarequired(), Email()])
2. call forms in other html file
<form method="post">
{{ myForm.hidden_tag() }} # csrf_token
{{ myForm.name.label }}:{{ myForm.name() }}
</form>
3. using forms in a more effective way
3.1. create wtf.py and create form in this file just as instructions above
3.2. call myForm from wtf.py in run_app.py(main file for all view funcs)
from wtf.py import myForm
3.3. create wtf.html and create macros
{% macro loginForm(myForm) %} # form will be passed through view func
{{ myForm.name.label }}:{{ myForm.name() }}
{% endmacro %}
{% macro signUp(myForm) %} # form will be passed through view func
{{ myForm.name.label }}:{{ myForm.name() }}
{% endmacro %}
3.4. import macro in another html file
{% import 'macro/wtf.html' as wtf %}
<form method="post">
{{ wtf.loginForm(myForm) }}
</form>
3.5. styling forms with bootstrap css
3.5.1. styling in form class:
name = StringField(validators=[Datarequired()],render_kw={'class':'form-control', 'autofocus':'autofocus'})
3.5.2. styling in html file:
{{ form.name(class='form-control',autofocus='autofocus') }}
# flask browser session manipulation
1. call session function
from flask import session
2. session is just the same as python dictionary, assign new var to session
session['name'] = form.name.data
3. retrive data saved in session
{{ session.get('name') }}
4. session will be terminated after client closed.
# flask_sqlalchemy
1. "1 to n" relationship
#for '1' side using db.relationship('model_name',backref='defined_ref_name')
#for 'n' side using db.Foreignkey('tablename.column_name')
2. 'n to 1' relationsip
#add db.Foreignkey() and db.relationship() to the 'n' side
3. convert '1 to n' relationship to '1 to 1' relationship
add 'uselist=False' in db.relationship()
4. 'n to n' relationship
#flask-migrate
1. simple usage of migrate:
1.1. manipulate database with class, objects instead of table and sql(like add/delete columns from db.Model)
2. setup migrate commands
1.1. import libs
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
1.2. initiate command
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
3. apply migrate command in console
step1: python app.py db init # this will create a 'migrations' folder and migrate scripts
step2: python app.py db migrate -m 'any msg' # this will create tables
step3: python app.py db upgrade/downgrade # apply the change for the db.Model
python app.py db --help # show up more command hints
#flask_mail
# DO NOT CONFIGURE SEND_MAIL FUNCTION IN MORE THAN 2 BLUEPRINTS, OR THERE WILL BE 'ImportError cannot import name send_mail'
1. mail configurations:
configuration options:
MAIL_SERVER : default ‘localhost’
MAIL_PORT : default 25
MAIL_USE_TLS : default False
MAIL_USE_SSL : default False
MAIL_DEBUG : default app.debug
MAIL_USERNAME : default None
MAIL_PASSWORD : default None
MAIL_DEFAULT_SENDER : default None
MAIL_MAX_EMAILS : default None
MAIL_SUPPRESS_SEND : default app.testing
MAIL_ASCII_ATTACHMENTS : default False
2. bulk mails in single connection(some mail sever might have mail limits, use MAIL_MAX_EMAILS = 100 to control)
with mail.connect() as conn:
for user in users:
msg = Message(subject='', html='',recipients=[user['email']])
conn.send(msg)
3. sent with attachment
with app.open_resource('image.png') as att:
msg.attch(filename='image.png',content_type='image/png', data=att.read())
mail.send(msg)
4. body and html message
if applying both body and html msg, only html content will be sent
5. speed up sending mails in asyncronized way:
from threading import Thread
def async_mail(app, msg):
with app.app_context():
mail.send(msg)
def send_mail(subject, recipients, template, **kwargs):
...
msg = ''
th = Thread(target=async_mail,args=(app, msg))
th.start()
# project structures
# a simple structure of a micro project
Project_folder
|app
|main
|__init__.py(Initiate Blueprint)
|errors.py
|forms.py
|views.py
|static
|css
|js
|images
|templates
|macro
|wtf.html
|mail
|new_user.html
|comment.html
|__init__.py(initiate all the extensions)
|mail.py
|models.py
|venv
|migrations
|config.py
|manage.py
|requirements.txt
# No solid structure for flask project, create structure according to specific project
# flask Blueprint extension
# main purpose: act as a middle man to construct or extend components or patterns globally in the application
1. inititate blueprint
from flask import Blueprint
auth = Blueprint('auth', __name__)
2. register blueprint to the application in app/__init__.py
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth') # optional variable url_prefix
# email confirmation functions
##import note: the action="" in relative form must not defined, if nominated a specific endpoint like 'auth.login',
when the request of confirmation was made, it will not redirect to the confirmation url(?/next=token_string),request.args.get('next')
will always return False
1. add new column 'confirmed=db.Column(db.Boolean, default=False)'
2. generate token
1.1. from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
s = Serializer(current_app.config['SECRET_KEY'])
token = s.dumps({'confirm':self.id}) # self.id == User.id
3. send token
{{ url_for('auth.confirm', token=token, _external=True) }} # _external tell the app to return a complete URL with http/https
4. confirm token(decode the Json string and update database)
def confirm_token(self, token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
if data.get('confirm') != self.id:
return False
else:
self.confirmed = True
db.session.add(self)
db.session.commit()
return True
except:
return False