• Flask基础


    配置文件

    配置文件导入原理

    根据 "settings.DevelopmentConfig" 这个字符串形式的路径可以导入 "settings" 这个模块,并且可以找到模块中的 "DevelopmentConfig" 这个类,还可以根据 "dir" 找到 "DevelopmentConfig" 类中的大写的静态字段

    import importlib
    
    
    path = "settings.DevelopmentConfig"
    
    p, c = path.rsplit('.', maxsplit=1)
    m = importlib.import_module(p)
    cls = getattr(m, c)
    
    
    # 如何根据cls这个类获取到类中的静态字段
    
    for key in dir(cls):
        if key.isupper():
            print(key, getattr(cls, key))     
    

    settings.py 配置文件

    import datetime
    
    
    class Config(object):
        TESTING = False
        PROPAGATE_EXCEPTIONS = None
        PRESERVE_CONTEXT_ON_EXCEPTION = None
        SECRET_KEY = None
        PERMANENT_SESSION_LIFETIME = datetime.timedelta(days=31)
        USE_X_SENDFILE = False
        SERVER_NAME = None
        APPLICATION_ROOT = '/'
        SESSION_COOKIE_NAME = 'session'
        SESSION_COOKIE_DOMAIN = None
        SESSION_COOKIE_PATH = None
        SESSION_COOKIE_HTTPONLY = True
        SESSION_COOKIE_SECURE = False
        SESSION_COOKIE_SAMESITE = None
        SESSION_REFRESH_EACH_REQUEST = True
        MAX_CONTENT_LENGTH = None
        SEND_FILE_MAX_AGE_DEFAULT = datetime.timedelta(seconds=43200)
        TRAP_BAD_REQUEST_ERRORS = None
        TRAP_HTTP_EXCEPTIONS = False
        EXPLAIN_TEMPLATE_LOADING = False
        PREFERRED_URL_SCHEME = 'http'
        JSON_AS_ASCII = True
        JSON_SORT_KEYS = True
        JSONIFY_PRETTYPRINT_REGULAR = False
        JSONIFY_MIMETYPE = 'application/json'
        TEMPLATES_AUTO_RELOAD = None
        MAX_COOKIE_SIZE = 4093
    
    
    class ProductionConfig(Config):
        DEBUG = False
    
    
    class DevelopmentConfig(Config):
        DEBUG = True
    
    

    app.config.from_object 导入配置信息

    app.py 中可以使用按需修改后settings.py 中的配置信息

    from flask import Flask
    
    app = Flask(__name__)
    
    app.config.from_object("settings.DevelopmentConfig")
    

    路由系统

    flask 中的路由url是依据带参数的装饰器写的

    • endpoint="n1" 相当于 django 路由系统 url 中的 name 起别名进行反向解析
    • 如果不写 endpoint="..." 则默认为:url_for("index") 视图函数名称 index
    • flask 路由反向解析用的是 url_for("n1")
    • 动态路由
      • /<int:nid> 动态接收 id 值(int类型),可以传参
      • /<nid> 未定义类型则默认接收 string 字符串类型
    from flask import Flask
    from flask import url_for
    
    
    @app.route('/index/<int:nid>', methods=["GET", "POST"], endpoint="n1")
    # endpoint="n1" 相当于 django 路由系统 url 中的 name 起别名进行反向解析
    # 如果不写 endpoint="..." 则默认为:url_for("index") 视图函数名称 index
    # flask 路由反向解析用的是 url_for("n1")
    
    # 动态路由
    # '/<int:nid>' 动态接收 id 值(int类型),可以传参
    # '/<nid>' 未定义类型则默认接收 string 字符串类型
    def index(nid):
        print(url_for("n1", nid=1))
        # /index/1
        return "Index"
    

    视图

    FBV 模式

    import functools
    from flask import Flask
    from flask import render_template
    from flask import request
    from flask import redirect
    from flask import session
    from flask import url_for
    from flask import Markup
    from flask import flash
    from flask import get_flashed_messages
    
    app = Flask(__name__)
    app.config.from_object("settings.DevelopmentConfig")
    
    # 模拟数据库中存储的用户
    STUDENT_DICT = {
        1: {'name': '小明', 'age': 18, 'gender': '男'},
        2: {'name': '小李', 'age': 19, 'gender': '男'},
        3: {'name': '小花', 'age': 17, 'gender': '女'},
    }
    
    
    # def auth(func):
    #     # '@functools.wraps(func)' 加上这个装饰器,可以避免被装饰的函数名变为'inner'
    #     @functools.wraps(func)
    #     def inner(*args, **kwargs):
    #         if not session.get('user'):
    #             return redirect(url_for('login'))
    #         ret = func(*args, **kwargs)
    #         return ret
    #     return inner
    
    
    # 被'@app.before_request' 此装饰器装饰的业务逻辑会在所有视图函数执行之前执行
    # 跟 'django' 的 'process_request' 极为相似
    # 当被此装饰器装饰的业务逻辑有返回值时,整个flask框架中的所有视图函数都不会被执行
    # 直接显示 "被此装饰器装饰的业务逻辑的返回值"
    @app.before_request
    def auth():
        if request.path == '/login':
            return None
        if session.get('user'):
            return None
        return redirect(url_for('login'))
    
    
    # 被此全局模板函数装饰器所装饰的业务逻辑,不需要借助任何视图函数传递此方法给模板,
    # 此方法会自动传递到需要应用此方法的模板中!
    # 模板只需要调用即可 {{global_template_func(1, 2)}} 即可在页面相应的位置显示出 3
    @app.template_global()
    def global_template_func(a, b):
        return a + b
    
    
    # {{ 1|filter_template_func(2, 3) }}
    # 页面显示 6
    @app.template_filter()
    def filter_template_func(a, b, c):
        return a + b + c
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            user = request.form.get('user')
            password = request.form.get('password')
            if user == 'aliang' and password == '007':
                session['user'] = user
                return redirect(url_for('index'))
            return render_template('login.html', error='用户名或密码错误!')
        return render_template('login.html')
    
    
    @app.route('/index')
    # @auth
    def index():
        return render_template('index.html', stu_dic=STUDENT_DICT)
    
    
    @app.route('/delete/<int:nid>')
    # @auth
    def delete(nid):
        del STUDENT_DICT[nid]
        return redirect(url_for('index'))
    
    
    @app.route('/detail/<int:nid>')
    # @auth
    def detail(nid):
        info = STUDENT_DICT[nid]
        return render_template('detail.html', info=info)
    
    
    def func(arg):
        return arg + 1
    
    
    @app.route('/tpl')
    def tpl():
        context = {
            'users': ['小明', '小李', '小花'],
            # 'txt': "<input type='text' />",
            'txt': Markup("<input type='text' />"),
            'func': func
        }
        return render_template('tpl.html', **context)
    
    
    @app.route('/page1')
    def page1():
        flash('存储临时数据')
    
        return "Session"
    
    
    @app.route('/page2')
    def page2():
        flash_msg = get_flashed_messages()
    
        return flash_msg
    
    
    class Middleware(object):
        def __init__(self, origin_wsgi_app):
            self.origin_wsgi_app = origin_wsgi_app
    
        def __call__(self, *args, **kwargs):
            # 在请求之前做相应的业务逻辑
    
            ret = self.origin_wsgi_app(*args, **kwargs)
    
            # 在请求之后做相应的业务逻辑
    
            return ret
    
    
    if __name__ == '__main__':
        app.wsgi_app = Middleware(app.wsgi_app)
        app.run()
    

    请求相关

    request

    request.method
    
    requestd.args
    
    request.form
    
    request.values
    
    requesdt.cookies
    
    request.headers
    
    request.path
    
    request.full_path
    
    request.script_root
    
    request.url
    
    request.base_url
    
    request.url_root
    
    request.host_url
    
    request.host
    
    request.files
    
    obj = request.files['the_file_name']
    obj.save('/var/www/uploads/' + secure_filename(f.filename))
    

    响应相关

    return

    from flask import render_template
    from flask import redirect
    from flask import jsonify
    
    
    # 以下4种返回的数据都是请求体,能不能将请求头也返回?可以封装!
    
    dic = {'k1': 'v1'}
    
    # return json.dumps(dic)
    # 相当于"django"中的JsonResponse
    return jsonify(dic)
    
    return "Index"
    
    return render_template("index.html")
    
    return redirect("/index")
    

    封装<响应头>&<响应体>后再返回

    定制返回的响应头

    from flask import make_response
    
    # 将要返回的字符串"Index"封装到"make_response"中
    # 设置了响应体
    obj = make_response("Index")
    
    # 设置了响应头
    obj.headers['xxxx'] = 'oooo'
    
    # 设置cookie信息
    obj.set_cookie('key', 'value')
    
    return obj
    

    模板渲染

    flask 模板渲染语法跟python语法极其相似

    视图传参——>模板渲染

    # app.py 文件中
    from flask import Markup
    
    
    def func(arg):
        return arg + 1
    
    
    @app.route('/tpl')
    def tpl():
        context = {
            'users': ['小明', '小李', '小花'],
            # 'txt': "<input type='text' />",
            'txt': Markup("<input type='text' />"),
            'func': func
        }
        return render_template('tpl.html', **context)
    
    

    tpl.html 模板文件中

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        
    {{users.0}}
    {{users[0]}}
    {{txt}}
    {{txt|safe}}
    {{func(6)}}
        
    </body>
    </html>
    

    @app.template_global() 全局模板函数装饰器之一

    被此全局模板函数装饰器所装饰的业务逻辑,不需要借助任何视图函数传递此方法给模板,
    此方法会自动传递到需要应用此方法的模板中!

    模板只需要调用即可 {{global_template_func(1, 2)}} 即可在页面相应的位置显示出 3

    # app.py 文件中
    
    @app.template_global()
    def global_template_func(a, b):
        return (a + b)
    

    tpl.html 模板文件中

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    {{global_template_func(1, 2)}}
        
    </body>
    </html>
    

    @app.template_filter() 全局模板函数装饰器之二

    在模板中这样调用:{{ 1|filter_template_func(2, 3) }}
    则在页面相应位置显示 6

    # app.py 文件中
    
    @app.template_filter()
    def filter_template_func(a, b, c):
        return a + b + c
    

    tpl.html 模板文件中

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    {{1|filter_template_func(2, 3)}}
     
    {% if 1|filter_template_func(2, 3) %}
        <div>返回值为6</div>
    {% else %}
        <div>没有返回值</div>
    {% endif %}
        
    </body>
    </html>
    

    模板继承——> extends "layout.html"

    layout.html 模板文件中

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        {% block content%}
        
        {% endblock %}
    </body>
    </html>
    

    tpl.html 继承 layout.html 模板文件

    {% extends "layout.html"%}
    
    {% block content%}
        {{users.0}}
    	{{users[0]}}
    	{{txt}}
    	{{txt|safe}}
    	{{func(6)}}
    {% endblock %}
    
    

    模板插件导入——> include "nav.html"

    nav.html 模板插件——>导航栏等插件

    ......
    

    tpl.html 文件中

    {% extends "layout.html"%}
    
    {% block content%}
    	{% include "nav.html" %}
        {{users.0}}
    	{{users[0]}}
    	{{txt}}
    	{{txt|safe}}
    	{{func(6)}}
    {% endblock %}
    

    模板宏定义——>{% macro ...%} ... {% endmacro %}

    适用于某个标签在整个页面上多次使用,可以按照宏定义的方式将此标签定义为一个代码块,在需要使用时直接调用即可!就不需要重写很多遍了!

    模板宏定义的意思是可以在模板中定义一个代码块,代码块中可以按照python函数的定义方式去定义某个需要在特定时刻使用的标签

    在需要显示此标签时,直接调用即可

    如下:

    {% macro tag(name, type='text', value='') %}
    	<h1>宏定义input标签</h1>
    	<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
    	<input type="submit" value="提交">
    {% endmacro %}
    
    {{tag("user")}}
    
    

    Session

    • django 中的session信息放在数据库中

    • flask 中的session信息经过加密之后保存在用户浏览器的cookie中

    利用session保存用户的登录状态

    可以做权限管理

    • 方法一:每个需要登录后才可以访问的页面所对应的视图函数中都查询一次是否有session信息

    • 方法二:利用装饰器,在每个需要登录后才可以访问的页面所对应的视图函数上加装饰器,在走相应的视图函数之前先走装饰器中定义好的判断是否存在session信息的业务逻辑

      • 装饰器(应用于被装饰的视图函数较少时

        import functools
        
        
        def auth(func):
            @functools.wraps(func)  # 加上这个装饰器,可以避免被装饰的函数名变为'inner'
            def inner(*args, **kwargs):
                if not session.get('user'):
                    return redirect(url_for('login'))
                ret = func(*args, **kwargs)
                return ret
            return inner
            
        
    • 方法三:使用 '@app.before_request' 装饰器
      被'@app.before_request' 此装饰器装饰的业务逻辑会在所有视图函数执行之前执行,跟 'django' 的 'process_request' 极为相似

    当被此装饰器装饰的业务逻辑有返回值时,整个flask框架中的所有视图函数都不会被执行,但所有被 '@app.after_request' 装饰器装饰的业务逻辑都会按照定义好的顺序倒序执行直接显示 "被此装饰器装饰的业务逻辑的返回值"

    当被此装饰器装饰的业务逻辑中返回 None 时,其他的视图才能正常执行

    @app.before_request
    def auth():
        if request.path == '/login':
            return None
        if session.get('user'):
            return None
        return redirect(url_for('login'))
    

    闪现——>flash

    基于session信息的设置原理封装好方法:<临时存>&<临时取>

    原理:在session中存储一个数据,读取一次之后,通过pop将数据移除,可以达到存一次取一次即失效的效果!

    • flask中的session 信息是经过加密后保存在cookie中的,可以一直取,只要不过期,不手动删除,则session信息一直存在!

    • 借助 flash 存储某一临时数据 & get_flashed_messages 获取临时数据,取一次即失效

      • 临时存,临时取
      • 取出来的是列表类型的数据,第二次取得就是空列表
    from flask import flash
    from flask import get_flashed_messages
    
    
    @app.route('/page1')
    def page1():
        flash('存储临时数据')
    
        return "Session"
    
    
    @app.route('/page2')
    def page2():
        flash_msg = get_flashed_messages()
    
        return flash_msg
    
    # ['存储临时数据']
    

    分类临时存,按分类临时取

    from flask import flash
    from flask import get_flashed_messages
    
    
    @app.route('/page1')
    def page1():
        flash('存储临时数据1', '类别1')
        flash('存储临时数据2', '类别1')
        flash('存储临时数据3', '类别2')
    
        return "Session"
    
    
    @app.route('/page2')
    def page2():
        flash_msg = get_flashed_messages(category_filter=['类别1'])
    
        return flash_msg
    
    
    # ['存储临时数据1', '存储临时数据2']
    

    中间件

    跟django的中间件的实现有较大区别

    flask 框架开始执行流程

    • flask 项目启动接口

      if __name__ == '__main__':
          app.run()
      
    • 执行 run 方法后使得服务端处于监听状态

    • 当有请求连接时,

      try:
          run_simple(host, port, self, **options)
      

      run_simple(host, port, self, **options) 中的第三个参数 self() 即 flask 的实例化对象 app() 会执行类的 __call__ 方法

      def __call__(self, environ, start_response):
          """The WSGI server calls the Flask application object as the
          WSGI application. This calls :meth:`wsgi_app` which can be
          wrapped to applying middleware."""
          return self.wsgi_app(environ, start_response)
      

    即在客户端请求连接时,基于flask框架的项目服务端会执行 __call__ 方法

    而我们知道中间件的作用就是在请求通过wsgi封装后经过路由匹配执行相应的视图函数再返回相应的模板页面之前做相应的业务逻辑,比如:发送某一请求访问特定的页面需要先登录之后才可以正常访问。

    所以,flask 框架中的中间件即在 __call__ 方法执行之前做相关定义

    定义中间件

    flask 框架中的中间件即在 Flask 类的 __call__ 方法执行之前做相关定义

    class Middleware(object):
        def __init__(self, origin_wsgi_app):
            self.origin_wsgi_app = origin_wsgi_app
    
        def __call__(self, *args, **kwargs):
            # 在请求之前做相应的业务逻辑
    
            ret = self.origin_wsgi_app(*args, **kwargs)
    
            # 在请求之后做相应的业务逻辑
    
            return ret
            
    
    if __name__ == '__main__':
        app.wsgi_app = Middleware(app.wsgi_app)
        app.run()
    
    

    特殊的装饰器

    类比于django的中间件

    @app.before_first_request

    被'@app.before_first_request' 此装饰器装饰的业务逻辑会在 '@app.before_request' 装饰的业务逻辑之前仅执行一次

    @app.before_first_request
    def excute_first():
        print("项目启动之后第一次请求到来时,会在@app.before_request装饰的业务逻辑之前仅仅执行一次")
    

    @app.before_request

    被'@app.before_request' 此装饰器装饰的业务逻辑会在所有视图函数执行之前执行,


    跟 'django' 的 'process_request' 极为相似


    当被此装饰器装饰的业务逻辑有返回值时,整个flask框架中的所有视图函数都不会被执行,但所有被 '@app.after_request' 装饰器装饰的业务逻辑都会按照定义顺序倒序执行后直接显示 "被此装饰器装饰的业务逻辑的返回值"


    当被此装饰器装饰的业务逻辑没有返回值即返回 None 时,请求按流程正常执行!

    @app.before_request
    def auth():
        print('请先登录!')
    

    @app.after_request

    被'@app.after_request' 此装饰器装饰的业务逻辑会在所有视图函数执行之后执行跟 'django' 的 'process_response' 极为相似


    被装饰的业务逻辑必须要有返回值,若没有返回值,程序会报错

    @app.after_request
    def after(response):
        print('请求已经得到响应!')
        return response
    

    @app.template_global() 全局模板函数装饰器之一

    被此全局模板函数装饰器所装饰的业务逻辑,不需要借助任何视图函数传递此方法给模板,此方法会自动传递到需要应用此方法的模板中!


    模板只需要调用即可 {{global_template_func(1, 2)}} 即可在页面相应的位置显示出 3

    # app.py 文件中
    
    @app.template_global()
    def global_template_func(a, b):
        return (a + b)
    

    tpl.html 模板文件中

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    {{global_template_func(1, 2)}}
        
    </body>
    </html>
    

    @app.template_filter() 全局模板函数装饰器之二

    在模板中这样调用:{{ 1|filter_template_func(2, 3) }}
    则在页面相应位置显示 6

    # app.py 文件中
    
    @app.template_filter()
    def filter_template_func(a, b, c):
        return a + b + c
    

    tpl.html 模板文件中

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    {{1|filter_template_func(2, 3)}}
     
    {% if 1|filter_template_func(2, 3) %}
        <div>返回值为6</div>
    {% else %}
        <div>没有返回值</div>
    {% endif %}
        
    </body>
    </html>
    

    @app.errorhandler

    被'@app.errorhandler(404)' 此装饰器装饰的业务逻辑可以自定义访问出错后的错误信息展示

    @app.errorhandler(404)
    def not_found(error_msg):
        print(error_msg)
        return "访问的页面不存在!"
    
  • 相关阅读:
    [译]WCF RIA Services中的集合(2)
    Silverlight中服务通信方式的选择(WCF、Data Service、Ria Service)
    记录来敦煌一周的情况
    Silverlight通过MVVM实现多语言实时切换(含源代码)
    [译]WCF RIA Services中的集合(1)
    Silverlight Client←→Server数据同步备忘代码
    Siverlight5新功能/改进总结
    Expression Blend 5 Preview For Silverlight5 RC已发布
    你应该知道的,那些未在Silverlight5Beta中出现的特性
    .NET数据库编程求索之路1.引子
  • 原文地址:https://www.cnblogs.com/fengting0913/p/13443945.html
Copyright © 2020-2023  润新知