Flask使用筆記

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)
  • sqlalchemy中的matesata()中的naming_convention:

https://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.MetaData.params.naming_convention

* %(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 to True 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章