1. 程式人生 > >Flask基礎 Flask基礎

Flask基礎 Flask基礎

Flask基礎

 

Flask初識

Flask是一個基於Python開發並且依賴jinja2模板和Werkzeug WSGI服務的一個微型框架,

對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,然後觸發Flask框架,

開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給使用者,如果要返回給使用者複雜的內容時,

需要藉助jinja2模板來實現對模板的處理,即:將模板和資料進行渲染,將渲染後的字串返回給使用者瀏覽器。

下載:

pip install flask

werkzeug

werkzeug類比django的wsgiref模組,封裝了sorket。

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)
werkzeug

基本的使用

一  FlaskDemo

複製程式碼
from flask import Flask

# 例項化一個Flask物件 給它指定一個名字
app = Flask(__name__)

@app.route("/index")
def index():
    return "hello~~"

if __name__ == '__main__':
    app.run()
複製程式碼
# by gaoxin
from flask import Flask, render_template, request, redirect


app = Flask(__name__)


@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template("login.html")
    user = request.form.get("user")
    pwd = request.form.get("pwd")
    if user == "gaoxin" and pwd == "123":
        return redirect("/index")
    else:
        return render_template("login.html", error="使用者名稱或密碼錯誤")
        # return render_template("login.html", **{"error": "使用者名稱或密碼錯誤"})


@app.route("/index")
def index():
    return render_template("index.html")


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

# 注意tempaltes以及static的配置
登入demo

二  配置檔案

flask中的配置檔案是一個flask.config.Config物件(繼承字典),預設配置為:
    {
        '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,
    }
配置檔案資訊

我們可以在例項化Flask物件之後~列印一下app.config來檢視Flask的配置資訊。

我們也可以通過app.config.xxx = xxx來更改配置資訊,但是通常我們不這樣去做~~

配置檔案的實現方式~~

複製程式碼
app.config.from_object("python類或類的路徑")
 
 app.config.from_object('專案根路徑.settings.TestingConfig')
 
 # settings.py
 
            class Config(object):
                DEBUG = False
                TESTING = False
                DATABASE_URI = 'sqlite://:memory:'
 
            class ProductionConfig(Config):
                DATABASE_URI = 'mysql://[email protected]/foo'
 
            class DevelopmentConfig(Config):
                DEBUG = True
 
            class TestingConfig(Config):
                TESTING = True
複製程式碼

三  路由系統

路由的引數

複製程式碼
@app.route('/user/<username>')
@app.route('/post/<int:post_id>')
@app.route('/post/<float:post_id>')
@app.route('/post/<path:path>')

# 路由預設支援的引數
DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}
複製程式碼

路由的命名

@app.route('/index.html',methods=['GET','POST'],endpoint='index') # 預設是函式名字

命名路由的反向解析

url_for("路由的名字", nid=32425)

四 模板

Flask模板的使用的是JinJa2模板,語法跟django無差別

細節不一樣的地方就是,模板中函式執行要加括號,更貼近python語法。

支援建立一個函式通過render頁面的形式傳遞到模板中~~

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>自定義函式</h1>
    {{ww()|safe}}

</body>
</html>
html
from flask import Flask,render_template
app = Flask(__name__)
 
 
def my_func():
    return '<h1>自定義函式返回內容</h1>'
 
@app.route('/index', methods=['GET', 'POST'])
def index():
    return render_template('index.html', ww=my_func)
app.py

五 請求和響應

複製程式碼
    from flask import Flask
    from flask import request
    from flask import render_template
    from flask import redirect
    from flask import make_response

    app = Flask(__name__)


    @app.route('/login.html', methods=['GET', "POST"])
    def login():

        # 請求相關資訊
        # request.method
        # request.args
        # request.form
        # request.values
        # request.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 "字串"
        # return render_template('html模板路徑',**{})
        # return redirect('/index.html')

        # response = make_response(render_template('index.html'))
        # response是flask.wrappers.Response型別
        # response.delete_cookie('key')
        # response.set_cookie('key', 'value')
        # response.headers['X-Something'] = 'A value'
        # return response


        return "內容"

    if __name__ == '__main__':
        app.run()
複製程式碼

 六  Session

除了請求物件外,還有一個session物件,它允許你在不同請求間儲存特定使用者的資訊。

它是在 Cookies 的基礎上實現的,並且對 Cookies 進行加密,你需要設定一個金鑰。

設定session

session['xxxxxx'] = 'xxx'

刪除session

session.pop('xxxx', None)

基本使用

from flask import Flask, session, redirect, url_for, escape, request
 
app = Flask(__name__)
app.secret_key = 'lajsdgjasdg'
 
@app.route('/')
def index():
    if 'username' in session:
        return "已經登入"
    return "未登入"
 
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return render_template('login.html'i)
 
@app.route('/logout')
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))
 
基本使用

 七  flash

flash是一個基於session實現的用於儲存資料的集合,特點是使用一次就刪除.

# by gaoxin
from flask import Flask, flash, get_flashed_messages

app = Flask(__name__)
app.config["SECRET_KEY"]= "xxxxxxxx"


@app.route("/set")
def index1():
    flash("hi")
    # flash("hello", "msg")
    return "設定flash值"


@app.route("/get")
def index2():
    msg = get_flashed_messages()
    # msg2 = get_flashed_messages(category_filter=["msg"])
    # print(msg2)
    print(msg)
    return "獲取flash內容"


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

八  中介軟體

Flask的中介軟體跟Django的不太一樣~我們需要從原始碼開始理解~開始理解前~我們先來複習點知識點~~

類加括號以及物件加括號分別執行什麼~~

class A():
    def __init__(self):
        print("init")

    def __call__(self, *args, **kwargs):
        print("__call__")

a = A()
a()
__call__

現在我們開始走原始碼~~我們說過werkzeug是我們封裝sorket的地方~原始碼是從run_simple方法開始走的~~

那我們看下app.run()做了什麼~~

呼叫了werkzueg的run_simple方法~  run_simple(host, port, self, **options)

這個方法中是self是我們的app, 我們知道werkzeug會執行app()

這是我們程式的入口~~會執行我們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)
        print("開始之前")
        ret = self.wsgi_app(environ, start_response)
        print("請求之後")
        return ret
修改原始碼 不建議!!
class MiddleWare(object):
    def __init__(self, old):
        self.old = old

    def __call__(self, *args, **kwargs):
        print("start")
        ret = self.old(*args, **kwargs)
        print("end")
        return ret


if __name__ == '__main__':
    app.wsgi_app = MiddleWare(app.wsgi_app)
    app.run()
    app.__call__
    app.wsgi_app
中介軟體類

通常我們不會這樣去實現中介軟體~Flask中有一些特殊的裝飾器能夠幫我們實現中介軟體的功能~~

九  特殊的裝飾器

需求~我們很多訪問需要進行登入認證~~怎麼實現這個登入認證的功能~

先來複習點裝飾器的知識~被裝飾器裝飾的函式名問題~~

def wrapper(func):

    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        return ret

    return inner


@wrapper
def index():
    return "1111"
print(index.__name__)

# 被裝飾器裝飾的函式名字會變成內部的函式名
# 我們需要給inner函式加個裝飾器來修改inner函式的資訊
import functools
@functools.wraps(func)
def inner()......
被裝飾器裝飾的函式名

自定義裝飾器實現認證

# by gaoxin
from flask import Flask, session, redirect, url_for
import functools

app = Flask(__name__)
app.secret_key = "xxxxxxxx"

# 實現登入 我們不可能每個檢視函式都去獲取session然後判斷
# 我們可以利用裝飾器
# 注意裝飾器裝飾完函式名的問題
# 注意裝飾器執行順序問題


def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        if not session["userinfo"]:
            return redirect(url_for("login"))
        ret = func(*args, **kwargs)
        return ret

    return inner


@app.route("/login")
def login():
    session["userinfo"] = "gaoxin"
    return "登入成功"


@app.route("/user")
@auth
def user():
    return "使用者管理頁面"


@app.route("/book")
@auth
def book():
    return "圖書管理頁面"


if __name__ == '__main__':
    app.run()
裝飾器認證

特殊裝飾器before_request

@app.before_request
def auth2():
    if request.path == "/login":
        return None
    if session.get("userinfo"):
        return None
    return redirect("/login")
before_request

其他特殊裝飾器

@app.template_global()
@app.template_filter()
@app.before_request
@app.after_request
@app.before_firse_request
@app.errorhandler(404)
from flask import Flask, Request, render_template

app = Flask(__name__, template_folder='templates')
app.debug = True


@app.before_first_request
def before_first_request1():
    print('before_first_request1')


@app.before_first_request
def before_first_request2():
    print('before_first_request2')


@app.before_request
def before_request1():
    Request.nnn = 123
    print('before_request1')


@app.before_request
def before_request2():
    print('before_request2')


@app.after_request
def after_request1(response):
    print('before_request1', response)
    return response


@app.after_request
def after_request2(response):
    print('before_request2', response)
    return response


@app.errorhandler(404)
def page_not_found(error):
    return 'This page does not exist', 404


@app.template_global()
def sb(a1, a2):
    return a1 + a2


@app.template_filter()
def db(data):
    return data[::2]


@app.route('/')
def hello_world():
    return render_template('hello.html')


if __name__ == '__main__':
    app.run()
# by gaoxin
例子

!!!注意這裡執行書序問題

如果我在before_request返回一直值,會怎麼樣呢~~~還會不會走after_request呢~~

這個大家可以試一下~~

在django的<=1.9版本的跟Flask是一樣的,就算before_request返回值了也會走所有響應~~

當時Django1.0版本以後就直接返回了~~

十  檢視

之前我們的檢視都是FBV,那麼我們的CBV要怎麼實現呢~~

實現CBV之前~我們要看下路由的實現原理~~

我們看下@app.route("/xx")做了什麼~~

首先這是一個帶引數的裝飾器~那麼帶引數的裝飾器執行順序是什麼樣的~~

1,先去掉@ 執行route("/xx")得到返回值

2, 再拿返回值加上@符號去裝飾接下來的函式

我們走進原始碼會發現~呼叫了add_url_rule方法~~

那麼我們在實現路由的時候就可以這麼做~~

複製程式碼
# by gaoxin

from flask import Flask


app = Flask(__name__)


def index():
    return "index"


app.add_url_rule("/", endpoint="index", view_func=index)


if __name__ == '__main__':
    app.run()
複製程式碼

那麼我們的CBV就可以寫了~~

# by gaoxin

from flask import Flask, views, session, redirect, url_for
import functools


app = Flask(__name__)


def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        if not session["userinfo"]:
            return redirect(url_for("login"))
        ret = func(*args, **kwargs)
        return ret

    return inner


class MyView(views.MethodView):
    methods = ["GET", "POST"]
    decorators = [auth, ]

    def get(self):
        return "GET"

    def post(self):
        return "POST"


# name == endpoint
app.add_url_rule("/", view_func=MyView.as_view(name="index"))


if __name__ == '__main__':
    app.run()
CBV程式設計

拓展~~

           from flask import Flask, views, url_for
            from werkzeug.routing import BaseConverter

            app = Flask(import_name=__name__)


            class RegexConverter(BaseConverter):
                """
                自定義URL匹配正則表示式
                """
                def __init__(self, map, regex):
                    super(RegexConverter, self).__init__(map)
                    self.regex = regex

                def to_python(self, value):
                    """
                    路由匹配時,匹配成功後傳遞給檢視函式中引數的值
                    :param value: 
                    :return: 
                    """
                    return int(value)

                def to_url(self, value):
                    """
                    使用url_for反向生成URL時,傳遞的引數經過該方法處理,返回的值用於生成URL中的引數
                    :param value: 
                    :return: 
                    """
                    val = super(RegexConverter, self).to_url(value)
                    return val

            # 新增到flask中
            app.url_map.converters['regex'] = RegexConverter


            @app.route('/index/<regex("\d+"):nid>')
            def index(nid):
                print(url_for('index', nid='888'))
                return 'Index'


            if __name__ == '__main__':
                app.run()
自定義路由的正則匹配

十一  藍圖

為我們提供目錄的劃分,就是解耦用的~~

我沒看下藍圖的目錄結構~~

1,建立一個專案~ 專案下建立一個同名的python的包~~

2,建立一個manage.py  啟動檔案 執行app.run()

3,  在python包下的init檔案下 例項化Flask 得到app

4,下Python包下面建立views資料夾,用藍圖註冊檢視和路由~

5,把檢視中的藍圖物件註冊到APP中~~

from flask imort Blueprint

userBlue = Blueprint("userBlue", __name__)

@userBlue.route("/user")
def user():
    return "USER"
生成藍圖物件
from .view.user import userBlue
from flask import Flask


def create_app():
    app = Flask(__name)

    app.register_blueprint(userBlue)
    return app
app中註冊藍圖

每個藍圖可以指定自己的模板以及靜態檔案~

還可以在app中註冊藍圖的時候指定路由字首~~

還可以給藍圖加before_request~~

Flask初識

Flask是一個基於Python開發並且依賴jinja2模板和Werkzeug WSGI服務的一個微型框架,

對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,然後觸發Flask框架,

開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給使用者,如果要返回給使用者複雜的內容時,

需要藉助jinja2模板來實現對模板的處理,即:將模板和資料進行渲染,將渲染後的字串返回給使用者瀏覽器。

下載:

pip install flask

werkzeug

werkzeug類比django的wsgiref模組,封裝了sorket。

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)
werkzeug

基本的使用

一  FlaskDemo

複製程式碼
from flask import Flask

# 例項化一個Flask物件 給它指定一個名字
app = Flask(__name__)

@app.route("/index")
def index():
    return "hello~~"

if __name__ == '__main__':
    app.run()
複製程式碼
# by gaoxin
from flask import Flask, render_template, request, redirect


app = Flask(__name__)


@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template("login.html")
    user = request.form.get("user")
    pwd = request.form.get("pwd")
    if user == "gaoxin" and pwd == "123":
        return redirect("/index")
    else:
        return render_template("login.html", error="使用者名稱或密碼錯誤")
        # return render_template("login.html", **{"error": "使用者名稱或密碼錯誤"})


@app.route("/index")
def index():
    return render_template("index.html")


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

# 注意tempaltes以及static的配置
登入demo

二  配置檔案

flask中的配置檔案是一個flask.config.Config物件(繼承字典),預設配置為:
    {
        '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,
    }
配置檔案資訊

我們可以在例項化Flask物件之後~列印一下app.config來檢視Flask的配置資訊。

我們也可以通過app.config.xxx = xxx來更改配置資訊,但是通常我們不這樣去做~~

配置檔案的實現方式~~

複製程式碼
app.config.from_object("python類或類的路徑")
 
 app.config.from_object('專案根路徑.settings.TestingConfig')
 
 # settings.py
 
            class Config(object):
                DEBUG = False
                TESTING = False
                DATABASE_URI = 'sqlite://:memory:'
 
            class ProductionConfig(Config):
                DATABASE_URI = 'mysql://[email protected]/foo'
 
            class DevelopmentConfig(Config):
                DEBUG = True
 
            class TestingConfig(Config):
                TESTING = True
複製程式碼

三  路由系統

路由的引數

複製程式碼
@app.route('/user/<username>')
@app.route('/post/<int:post_id>')
@app.route('/post/<float:post_id>')
@app.route('/post/<path:path>')

# 路由預設支援的引數
DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}
複製程式碼

路由的命名

@app.route('/index.html',methods=['GET','POST'],endpoint='index') # 預設是函式名字

命名路由的反向解析

url_for("路由的名字", nid=32425)

四 模板

Flask模板的使用的是JinJa2模板,語法跟django無差別

細節不一樣的地方就是,模板中函式執行要加括號,更貼近python語法。

支援建立一個函式通過render頁面的形式傳遞到模板中~~

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>自定義函式</h1>
    {{ww()|safe}}

</body>
</html>
html
from flask import Flask,render_template
app = Flask(__name__)
 
 
def my_func():
    return '<h1>自定義函式返回內容</h1>'
 
@app.route('/index', methods=['GET', 'POST'])
def index():
    return render_template('index.html', ww=my_func)
app.py

五 請求和響應

複製程式碼
    from flask import Flask
    from flask import request
    from flask import render_template
    from flask import redirect
    from flask import make_response

    app = Flask(__name__)


    @app.route('/login.html', methods=['GET', "POST"])
    def login():

        # 請求相關資訊
        # request.method
        # request.args