python框架之Flask(1)-Flask初使用

Flask是一個基於 Python 開發而且依賴 jinja2 模板和 Werkzeug WSGI 服務的一個微型框架,對於 Werkzeug 本質是 Socket 服務端,其用於接收 http 請求並對請求進行預處理,而後觸發 Flask 框架,開發人員基於 Flask 框架提供的功能對請求進行相應的處理,並返回給用戶,若是要返回給用戶複雜的內容時,須要藉助 jinja2 模板來實現對模板的處理,即:將模板和數據進行渲染,將渲染後的字符串返回給用戶瀏覽器。html

準備

安裝

pip3 install flask

werkzeug的簡單使用

from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple


@Request.application
def hello(request):
    return Response('Hello World!')


if __name__ == '__main__':
    run_simple('localhost', 4000, hello)

使用

hello flask

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

登陸示例

from flask import Flask, request, render_template, redirect, session

app = Flask(__name__,
            template_folder='templates',  # 默認模板文件夾
            static_folder='static',  # 默認靜態文件文件夾
            static_url_path='/static'  # 默認靜態文件訪問路徑
            )
app.config['DEBUG'] = True
# 使用 session 必須定義 不然報錯
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'


# 默認只支持 GET 請求
@app.route('/login', methods=['GET', "POST"])
def login():
    if request.method == 'GET':
        # 渲染模板 默認對應templates 文件夾
        return render_template('login.html')
    user = request.form.get('user')
    pwd = request.form.get('pwd')

    if user == 'zze' and pwd == '666':
        session['user'] = user
        # 重定向
        return redirect('/index')
    return render_template('login.html', msg='用戶名或密碼錯誤!')


@app.route('/index')
def index():
    user = session['user']
    if not user:
        return redirect('/login')
    return render_template('index.html')


if __name__ == '__main__':
    app.run()

配置文件

  • 方式一:app.config

    app.config 本質上實際上是一個字典,因此能夠經過以下方式進行配置:python

    from flask import Flask
    
    app = Flask(__name__)
    app.config['DEBUG'] = True
  • 方式二:對象配置

    固然,Flask 也提供了單文件配置的方式,以下:django

    class Dev:
        DEBUG = True
    settings.py
    from flask import Flask
    
    app = Flask(__name__)
    app.config.from_object('settings.Dev')
  • 方式三:文件配置

    app.config.from_pyfile("settings.py")
    # 如:
    # settings.py
    #    DEBUG = True
  • 方式四:環境變量

    app.config.from_envvar("環境變量名稱")
    # 環境變量的值爲python文件名稱名稱,內部調用from_pyfile方法
  • 方式五:json方式

    app.config.from_json("json文件名稱")
    # json文件名稱,必須是json格式,由於內部會執行json.loads
  • 方式六:字典方式

    app.config.from_mapping({'DEBUG':True})
    # 字典格式
  • 默認配置參數

    {
            'DEBUG':                                get_debug_flag(default=False),  是否開啓Debug模式
            'TESTING':                              False,                          是否開啓測試模式
            'PROPAGATE_EXCEPTIONS':                 None,                         
            'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
            'SECRET_KEY':                           None,
            'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
            'USE_X_SENDFILE':                       False,
            'LOGGER_NAME':                          None,
            'LOGGER_HANDLER_POLICY':               'always',
            'SERVER_NAME':                          None,
            'APPLICATION_ROOT':                     None,
            'SESSION_COOKIE_NAME':                  'session',
            'SESSION_COOKIE_DOMAIN':                None,
            'SESSION_COOKIE_PATH':                  None,
            'SESSION_COOKIE_HTTPONLY':              True,
            'SESSION_COOKIE_SECURE':                False,
            'SESSION_REFRESH_EACH_REQUEST':         True,
            'MAX_CONTENT_LENGTH':                   None,
            'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
            'TRAP_BAD_REQUEST_ERRORS':              False,
            'TRAP_HTTP_EXCEPTIONS':                 False,
            'EXPLAIN_TEMPLATE_LOADING':             False,
            'PREFERRED_URL_SCHEME':                 'http',
            'JSON_AS_ASCII':                        True,
            'JSON_SORT_KEYS':                       True,
            'JSONIFY_PRETTYPRINT_REGULAR':          True,
            'JSONIFY_MIMETYPE':                     'application/json',
            'TEMPLATES_AUTO_RELOAD':                None,
    }

路由系統

@app.route('/user/<username>')
@app.route('/post/<int:post_id>')
@app.route('/post/<float:post_id>')
@app.route('/post/<path:path>')
@app.route('/login', methods=['GET', 'POST'])

經常使用路由系統有以上五種,全部的路由系統都是基於一下對應關係來處理:json

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

能夠經過 endpoint 和 url_for 反向生成:flask

from flask import Flask, url_for

app = Flask(__name__)


@app.route('/index', endpoint='i')
def index():
    return url_for('i')  # endpoint 值未指定時,默認爲方法名


if __name__ == '__main__':
    app.run()

模板

  • 語法

    Flask 使用的是 Jinjia2 模板,因此其語法和在 Django 中使用時無差異。瀏覽器

    Markup 等價 django 的 mark_safe。cookie

  • 模板方法

    {% macro input(name, type='text', value='') %}
        <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
    {% endmacro %}
    
    {{ input('n1') }}

請求和響應

  • 請求相關

    request 內置屬性:
        request.method # 方法
        request.args # GET 請求時的參數
        request.form # POST 請求時的參數
        request.values # 全部參數
        request.cookies # cookie 內容
        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 "字符串"
    # 返回渲染模板
    return render_template('html模板路徑',**{})
    # 重定向
    return redirect('/index.html')

Session

當請求剛到來時,flask 讀取 cookie 中 session 對應的值,並將該值解密並反序列化成字典,放入內存以便視圖函數使用。session

當請求結束時,flask 會讀取內存中字典的值,進行序列化和加密,再寫入到 cookie 中。app

中間件

從 flask 的入口 app.run 方法看起,框架

 1 def run(self, host=None, port=None, debug=None,
 2         load_dotenv=True, **options):
 3     if os.environ.get('FLASK_RUN_FROM_CLI') == 'true':
 4         from .debughelpers import explain_ignored_app_run
 5         explain_ignored_app_run()
 6         return
 7 
 8     if get_load_dotenv(load_dotenv):
 9         cli.load_dotenv()
10 
11         # if set, let env vars override previous values
12         if 'FLASK_ENV' in os.environ:
13             self.env = get_env()
14             self.debug = get_debug_flag()
15         elif 'FLASK_DEBUG' in os.environ:
16             self.debug = get_debug_flag()
17 
18     if debug is not None:
19         self.debug = bool(debug)
20 
21     _host = '127.0.0.1'
22     _port = 5000
23     server_name = self.config.get('SERVER_NAME')
24     sn_host, sn_port = None, None
25 
26     if server_name:
27         sn_host, _, sn_port = server_name.partition(':')
28 
29     host = host or sn_host or _host
30     port = int(port or sn_port or _port)
31 
32     options.setdefault('use_reloader', self.debug)
33     options.setdefault('use_debugger', self.debug)
34     options.setdefault('threaded', True)
35 
36     cli.show_server_banner(self.env, self.debug, self.name, False)
37 
38     from werkzeug.serving import run_simple
39 
40     try:
41         run_simple(host, port, self, **options)
42     finally:
43         self._got_first_request = False
flask.app.Flask.run

直接看到 41 行,這裏的 run_simple 方法實際上就是 werkzeug.serving.run_simple ,而傳入的第三個參數 self 實際上就是指的 app 自己。咱們先要了解的是,在上面的werkzeug的簡單使用中,傳入的是一個方法,而且這個方法會在請求到來時被執行。而在這裏傳入一個對象,加 () 執行一個對象時其實是執行這個對象的 __call__ 方法,查看 app.__call__ 方法:

1 def __call__(self, environ, start_response):
2     return self.wsgi_app(environ, start_response)
flask.app.Flask.__call__

看到這裏咱們能夠肯定,後續整個程序的執行就是依賴這第 2 行的觸發,而咱們只要在第二行執行以前定義的處理就至關於中間件的前置處理,在它以後的處理就至關於後置處理了。因此能夠經過以下方法實現中間件的功能:

from flask import Flask
app
= Flask(__name__) class MiddleWare: def __init__(self, wsgi_app): self.wsgi_app = wsgi_app def __call__(self, *args, **kwargs): print('前置處理') obj = self.wsgi_app(*args, **kwargs) print('後置處理') if __name__ == "__main__": app.wsgi_app = MiddleWare(app.wsgi_app) app.run(port=9999)

flash

flash 就是用來存儲只使用一次的數據,相似於將數據放入列表,而後經過 pop 方法取出。

flash(message, category='message')
get_flashed_messages(with_categories=False, category_filter=[])
  • 使用

    from flask import Flask, flash, get_flashed_messages
    
    app = Flask(__name__)
    
    
    @app.route('/page1')
    def page1():
        flash('臨時數據')
        return 'page1'
    
    
    @app.route('/page2')
    def page2():
        get_flashed_messages()
        return 'page2'
    
    
    if __name__ == '__main__':
        app.run()
  • 源碼分析

    查看 flash 方法:
    1 def flash(message, category='message'):
    2     flashes = session.get('_flashes', [])
    3     flashes.append((category, message))
    4     session['_flashes'] = flashes
    5     message_flashed.send(current_app._get_current_object(),
    6                          message=message, category=category)
    flask.helpers.flash

    能夠看到它其實就是把 message 和 category 以元組的形式存儲在 session 中鍵爲 _flashes 的值中。

    再看 get_flashed_messages 方法:
     1 def get_flashed_messages(with_categories=False, category_filter=[]):
     2     flashes = _request_ctx_stack.top.flashes
     3     if flashes is None:
     4         _request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \
     5             if '_flashes' in session else []
     6     if category_filter:
     7         flashes = list(filter(lambda f: f[0] in category_filter, flashes))
     8     if not with_categories:
     9         return [x[1] for x in flashes]
    10     return flashes
    flask.helpers.get_flashed_messages

    從第 4 行能夠看到每次獲取 flash 數據時,就是從 session 中將以前存入的鍵爲 _flashes 的元素 pop 出來,而後進行過濾返回對應 category 內容。

特殊的裝飾器

  • before_request&after_request

    from flask import Flask, flash, get_flashed_messages
    
    app = Flask(__name__)
    app.secret_key = '123123'
    
    
    @app.before_request
    def before_request1():
        print('before_request1')
    
    
    @app.before_request
    def before_request2():
        print('before_request2')
    
    
    @app.after_request
    def after_request1(response):
        print('after_request1')
        return response
    
    
    @app.after_request
    def after_request2(response):
        print('after_request2')
        return response
    
    
    @app.route('/index')
    def index():
        print('index')
        return 'index'
    
    
    if __name__ == '__main__':
        app.run()
        
    # 執行順序以下:
    #     before_request1
    #     before_request2
    #     index
    #     after_request2
    #     after_request1
  • template_filter

    from flask import Flask, flash, get_flashed_messages, render_template
    
    app = Flask(__name__)
    app.secret_key = '123123'
    
    
    @app.template_filter()
    def add_filter(a, b):
        return a + b
    
    
    @app.route('/index')
    def index():
        print('index')
        return render_template('index.html')
    
    
    if __name__ == '__main__':
        app.run()
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {{1|add_filter(2)}}
    </body>
    </html>
  • template_global

    from flask import Flask, flash, get_flashed_messages, render_template
    
    app = Flask(__name__)
    app.secret_key = '123123'
    
    
    @app.template_global()
    def add_template_func(a, b):
        return a + b
    
    
    @app.route('/index')
    def index():
        print('index')
        return render_template('index.html')
    
    
    if __name__ == '__main__':
        app.run()
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {{add_template_func(1,2)}}
    </body>
    </html>
  • errorhandler

    @app.errorhandler(404)
    def page_not_found(error):
        return render_template('404.html'),404
  • before_first_request

    @app.before_first_request
    def application_start():
        # 同 before_request,但僅在第一次請求時執行一次。
        pass
相關文章
相關標籤/搜索