学习笔记
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