學習筆記
request.files.getlist()的原理
request.files.getlist()
request.files 調用BaseRequest的files()cache_property屬性
flies()的內容中調用_load_from_data()給BaseRequest添加files屬性,
_load_from_data()中會調用parameter_storage_class而
parameter_storage_class = ImmutableMultiDict
ImmutableMultiDict又繼承子MulitDict, MultiDict中包含getlist方法.
該方法可以取出所有的值
jinja2 macro中關鍵字參數的傳遞
關於jinja2中的macros
macro中可以傳遞鍵值參數
直接在需要的地方加上**kwargs就可以了
舉個例子:
<!-- 定義 -->
{% macro form_field(field) %}
field
field(**kwargs)
{% endmacro%}
<!-- 定義 -->
{% macro form_field(field) %}
{{ field.label }}<br>
{% if field.flags.required %}
{{ field(required='required', **kwargs) }}<br>
{% else %}
{{ field(**kwargs) }}<br>
{% endif %}
{% if field.errors %}
{% for error in field.errors %}
<small class="error">{{ error }}</small><br>
{% endfor %}
{% endif %}
{% endmacro %}
<!-- 調用 -->
{{ form_field(form.body, rows=5, cols=50) }}
flask 添加shell上下文
註冊shell上下文處理函數
@app.shell_context_processor
def make_shell_context()
return dict(db=db, Note=Note)
shell_context_processor會將函數的返回值添加到shell_context_processors中, shell_context_processors是一個包含shel上下文的字典列表,
只包含由shell_context_processor註冊的上下文函數
SQLAlchemy中的一對多的class示例
class Author(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(70), unique=True)
phone = db.Column(db.String(20))
articles = db.relationship('Article')
def __repr__(self):
return '<Author %r>' % self.name
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(50), index=True)
body = db.Column(db.Text)
author_id = db.Column(db.Integer, db.ForeignKey('author.id'))
def __repr__(self):
return '<Article %r>' % self.title
class Writer(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(70), unique=True)
books = db.relationship('Book', back_populates='writer')
def __repr__(self):
return '<Writer %r>' % self.name
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Integer, index=True)
writer_id = db.Column(db.Integer, db.ForeignKey('writer.id'))
writer = db.relationship('Writer', back_populates='books')
def __repr__(self):
return '<Book %r>' % self.name
class Singer(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(70), unique=True)
songs = db.relationship('Song', backref='singer')
# songs = db.relationship('Song', backref=db.backref('singer', uselist=False))
def __repr__(self):
return '<Singer %r>' % self.name
class Song(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), index=True)
singer_id = db.Column(db.Integer, db.ForeignKey('singer.id'))
def __repr__(self):
return '<Song %s>' % self.name
# flask shell 上下文註冊
@app.shell_context_processor
def make_shell_context():
return dict(db=db, Note=Note, Author=Author, Article=Article, Writer=Writer, Book=Book, Singer=Singer, Song=Song)
* %(table_name)s - the name of the Table object associated with the constraint.
* %(referred_table_name)s - the name of the Table object associated with the referencing target of a ForeignKeyConstraint.
* %(column_0_name)s - the name of the Column at index position “0” within the constraint.
* %(column_0N_name)s - the name of all Column objects in order within the constraint, joined without a separator.
* %(column_0_N_name)s - the name of all Column objects in order within the constraint, joined with an underscore as a separator.
* %(column_0_label)s, %(column_0N_label)s, %(column_0_N_label)s - the label of either the zeroth Column or all Columns, separated with or without an underscore
* %(column_0_key)s, %(column_0N_key)s, %(column_0_N_key)s - the key of either the zeroth Column or all Columns, separated with or without an underscore
* %(referred_column_0_name)s, %(referred_column_0N_name)s %(referred_column_0_N_name)s, %(referred_column_0_key)s, %(referred_column_0N_key)s, … column tokens which render the names/keys/labels of columns that are referenced by a ForeignKeyConstraint.
* %(constraint_name)s - a special key that refers to the existing name given to the constraint. When this key is present, the Constraint object’s existing name will be replaced with one that is composed from template string that uses this token. When this token is present, it is required that the Constraint is given an explicit name ahead of time.
* user-defined: any additional token may be implemented by passing it along with a fn(constraint, table) callable to the naming_convention dictionary.
-
MetaData()中的name_convention:
column_0_label 即爲第一個column第0個位置的標籤, 一般爲表名加字段名 column_0_name 爲column的0位參數的name -> 字段名 column_0_key 和上面的一樣
例子:
"ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", "ck": "ck_%(table_name)s_%(constraint_name)s", "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "pk": "pk_%(table_name)s"
“fk”, “pk”, “ix”, “ck”, “uq”
基本上所有的名稱table_name referred_table_name column_0_name column_0N_name column_0_N_name column_0_label column_0N_label column_0_N_label column_0_key column_0N_key column_0_N_key referred_column_0_name referred_column_0N_name referred_column_0_N_name referred_column_0_label referred_column_0N_label referred_column_0_N_label referred_column_0_key referred_column_0N_key referred_column_0_N_key
relationship中的部分參數
- secondary:
作用: 多對多的環境下指定第三張參考的表
- priamryjoin的作用
class IPA(Base): __tablename__ = 'ip_address' id = Column(Integer, primary_key=True) v4address = Column(INET) network = relationship("Network", # 指定連接條件 primaryjoin="IPA.v4address.op('<<', is_comparison=True)(foreign(Network.v4representation))", viewonly=True ) class Network(Base): __tablename__ = 'network' id = Column(Integer, primary_key=True) v4representation = Column(CIDR)
- primaryjoin和secondaryjoin的作用:
from sqlalchemy import Integer, ForeignKey, String, Column, Table from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship Base = declarative_base() node_to_node = Table("node_to_node", Base.metadata, Column("left_node_id", Integer, ForeignKey("node.id"), primary_key=True), Column("right_node_id", Integer, ForeignKey("node.id"), primary_key=True) ) class Node(Base): __tablename__ = 'node' id = Column(Integer, primary_key=True) label = Column(String) right_nodes = relationship("Node", secondary=node_to_node, primaryjoin=id==node_to_node.c.left_node_id, secondaryjoin=id==node_to_node.c.right_node_id, backref="left_nodes" )
- 參數也可以使用字符串的格式
class Node(Base): __tablename__ = 'node' id = Column(Integer, primary_key=True) label = Column(String) right_nodes = relationship("Node", secondary="node_to_node", primaryjoin="Node.id==node_to_node.c.left_node_id", secondaryjoin="Node.id==node_to_node.c.right_node_id", backref="left_nodes" )
- cascade
cascsde設置爲cascade='all', 與cascade='all delete-orphan'的區別: all: 刪除父級內容, 子級內容跟着刪除, 解除關係的話就能救一命. all orphan 刪除父及內容, 子集跟着刪除.同時, 在父級解除關係的時候也會刪除自己內容.
flask-Migrate的使用
默認情況下使用下面的遷移代碼:
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('author', sa.Column('email', sa.String(length=20), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('author', 'email')
# ### end Alembic commands ###
不過也可以修改成這個樣子(可用於sqlite, move and copy 工作流, 創建, 轉移, 刪除):
注意要自己修改呦!!!
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('note') as branch_op:
branch_op.add_column(sa.Column('timestemp', sa.DateTime(), nullable=True))
# branch_op.add_column(...) 如此繼續下去
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('note') as branch_op:
branch_op.drop_column('timestemp')
# branch_op.drop_column('...') 同理
# ### end Alembic commands ###
SQLAlchemy中的event事件:
append:
@event.listens_for(SomeClass.some_attribute, 'append')
def receive_append(target, value, initiator):
"""
target
value
initiator
"""
pass
@event.listens_for(SomeClass.some_attribute, 'bulk_replace')
def receive_bulk_replace(target, values, initiator):
@db.event.listens_for(Draft.body, 'set')
def increment_edit_time(target, value, oldvalue, initiator):
if target.edit_time is not None:
target.edit_time += 1
註冊app控制命令
使用裝飾器@app.cli.commend()註冊命令, 使用@click.option()添加選項
option的部分參數:\
- is_flag: forces this option to act as a flag. The default is auto detection.
is_flag設置爲True可以將這個選項聲明爲boolean flag - prompt: if set to
True
or a non empty string then the user will be prompted for input.If set toTrue
the prompt will be the option name capitalized. 將選項設置爲必須參數, 如果沒有提供通過終端獲取 - default: click自動識別參數類型, 添加到help文檔中, 可以使用()設置位置參數,
commend的部分參數:
- name: 指定控制命令的名稱.
What does it look like? Here is an example of a simple Click program:
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name', help='The person to greet.')
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
if __name__ == '__main__':
hello()
And what it looks like when run:
$ python hello.py --count=3
Your name: John
Hello John!
Hello John!
Hello John!
$ python help.py
Your name: 張三
Hello 張三!
It automatically generates nicely formatted help pages:
$ python hello.py --help
Usage: hello.py [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--help Show this message and exit.
使用itsdangerous創建token簽名:
generate_token()
params中最好包含有操作類型, 我的意思是生成token的目的,這個目的最好量化成一些固定值
並保存起來, 然後按照目的傳入generate_token()中, 其次要有需要驗證的信息, 比如對於給註冊用戶發
送的郵箱驗證的token需要添加用戶的id.
validate_token() 根據上面的描述, 驗證函數必然包含的參數就清楚明瞭了, 應該有的內容是生成的token, 以及token中包含的信息, 還是已上一個例子來說明, 即應該包含id
serializer中的驗證的部分的過期時間不需要在校驗的序列化器中使用.
login_required裝飾器如何起作用:
- 對於使用了login(user=user, remember=remember…)的用戶, 即爲已登陸的用戶:
login_user方法將user_id remember保存到session中
並將user添加到_request_ctx_stack.top.user中
然後當使用current_user時就會從_request_ctx_stack.top中讀取到user
判斷user的is_authenticated(所有用戶的這個值都爲True)
這裏的語句是:if not current_user.is_authenticated: return current_app.login_manager.unauthorized() return func()
顯然就會調用視圖函數
- 對於沒有登陸的用戶
同樣的調用這次會執行 current_app.login_mamager.unauthorized()
這個方法返會登陸視圖, 如果沒有指定, 會拋出401的網頁錯誤
current_user = LocalProxy(lambda: _get_user())
_get_user
if has_request_context() and not hasattr(_request_ctx_stack.top, 'user'):
current_app.login_manager._load_user()
return getattr(_request_ctx_stack.top, 'user', None)
_load_user
config = current_app.config
if config.get('SESSION_PROTECTION', self.session_protection):
deleted = self._session_protection()
if deleted:
return self.reload_user()
is_missing_user_id = 'user_id' not in session
if is_missing_user_id:
cookie_name = config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME)
header_name = config.get('AUTH_HEADER_NAME', AUTH_HEADER_NAME)
has_cookie = (cookie_name in request.cookies and
session.get('remember') != 'clear')
if has_cookie:
return self._load_from_cookie(request.cookies[cookie_name])
elif self.request_callback:
return self._load_from_request(request)
elif header_name in request.headers:
return self._load_from_header(request.headers[header_name])
return self.reload_user()
_load_from_cookie
_load_from_header
_load_from_request
def _load_from_cookie(self, cookie):
user_id = decode_cookie(cookie)
if user_id is not None:
session['user_id'] = user_id
session['_fresh'] = False
self.reload_user()
if _request_ctx_stack.top.user is not None:
app = current_app._get_current_object()
user_loaded_from_cookie.send(app, user=_get_user())
def _load_from_header(self, header):
user = None
if self.header_callback:
user = self.header_callback(header)
if user is not None:
self.reload_user(user=user)
app = current_app._get_current_object()
user_loaded_from_header.send(app, user=_get_user())
else:
self.reload_user()
def _load_from_request(self, request):
user = None
if self.request_callback:
user = self.request_callback(request)
if user is not None:
self.reload_user(user=user)
app = current_app._get_current_object()
user_loaded_from_request.send(app, user=_get_user())
else:
self.reload_user()
reload_user(self, user=None)
ctx = _request_ctx_stack.top
if user is None:
user_id = session.get('user_id')
if user_id is None:
ctx.user = self.anonymous_user()
else:
if self.user_callback is None:
raise Exception(
"No user_loader has been installed for this "
"LoginManager. Refer to"
"https://flask-login.readthedocs.io/"
"en/latest/#how-it-works for more info.")
user = self.user_callback(user_id)
if user is None:
ctx.user = self.anonymous_user()
else:
ctx.user = user
else:
ctx.user = user
self.user_callback
def user_loader(self, callback):
self.user_callback = callback
return callback