flask路由系統、偏函式、CBV、模板、請求響應、session、請求擴充套件

认真的六六發表於2024-06-14

路由系統

 1 程式碼演示
 2 
 3 from flask import Flask
 4 
 5 app = Flask(__name__)
 6 
 7 app.debug = True
 8 # 路由基本使用
 9 # @app.route('/', methods=['GET'])
10 # @app.get()
11 # @app.post()
12 def index(name):
13     print(name)
14     return 'hello world'
15 
16 
17 # 自己註冊路由,看route原始碼,等同於django的path
18 app.add_url_rule('/index', endpoint=None, view_func=index, methods=['GET'],defaults={'name': 'zhangsan'})
19 
20 if __name__ == '__main__':
21     app.run()
22 
23 '''
24 route()原始碼解析,本質
25 使用的時候@app.route('/', methods=['GET'])---decorator---index=decorator(index)
26     def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
27         def decorator(f: T_route) -> T_route:
28         # 1.如果沒有傳endpoint,那麼就是None
29             endpoint = options.pop("endpoint", None)
30             # 2.self app物件,是Flask類的例項---flask類中的add_url_rule方法方法是路由的本質
31             self.add_url_rule(rule, endpoint, f, **options)
32             return f
33         return decorator
34 於是註冊路由,我們可以不用裝飾器,可以自己寫
35 '''
36 
37 
38 ------------------------------------------------------------------------------------
39 
40 # 2 route的引數,其實就是add_url_rule的引數
41     # rule, URL規則,路徑
42     # view_func, 檢視函式名稱
43     # defaults = None, 預設值, 當URL中無引數,函式需要引數時,使用defaults = {'k': 'v'}-為函式提供引數 ---》django中也有,叫kwargs
44     # endpoint = None, 名稱[別名],用於反向生成URL,即: url_for('名稱')
45     # methods = None, 允許的請求方式,如:["GET", "POST"]
46     # strict_slashes = None 對URL最後的 / 符號是否嚴格要求
47         '''
48             @app.route('/index', strict_slashes=False)
49             #訪問http://www.xx.com/index/ 或http://www.xx.com/index均可
50             @app.route('/index', strict_slashes=True)
51             #僅訪問http://www.xx.com/index
52         '''
53     # redirect_to = None重定向到指定地址
54         '''
55             @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
56         '''
57 
58     # subdomain = None子域名訪問
59     
60     
61     
62     
63 # 3 轉換器 app.add_url_rule('/index/<int:pk>')
64 DEFAULT_CONVERTERS = {
65     'default':          UnicodeConverter,
66     'string':           UnicodeConverter,
67     'any':              AnyConverter,
68     'path':             PathConverter,
69     'int':              IntegerConverter,
70     'float':            FloatConverter,
71     'uuid':             UUIDConverter,
72 }

偏函式

 1 from functools import partial  # 內建的
 2 
 3 
 4 def add(x, y, z):
 5     return x + y + z
 6 
 7 
 8 res = add(1, 2, 3)
 9 print(res)
10 
11 # 偏函式
12 add_1 = partial(add, 1)  # 提前傳值
13 print(add_1(2, 3))
14 
15 
16 
17 後期去找資料文件再補充

CBV 基於類的檢視

基本使用

 1 from flask import Flask, url_for
 2 from flask.views import MethodView
 3 
 4 app = Flask(__name__)
 5 app.debug = True
 6 
 7 
 8 class IndexView(MethodView):
 9     def get(self):
10         return 'get請求'
11 
12     def post(self):
13         return 'post請求'
14 
15 
16 # name其實就是endpoint的別名,且必須傳,如果都傳,以endpoint為準
17 # app.add_url_rule('/index', endpoint='index', view_func=IndexView.as_view(name='jh'))
18 
19 app.add_url_rule('/index', view_func=IndexView.as_view(name='jj'))
20 if __name__ == '__main__':
21     app.run()

原始碼分析

 1 # 0 app.add_url_rule('/index', view_func=IndexView.as_view(name='jh')) 
 2 
 3 # 1 as_view 原始碼
 4 @classmethod
 5 def as_view(cls, name, *class_args) :
 6     if cls.init_every_request:
 7         def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
 8             self = view.view_class(  # type: ignore[attr-defined]
 9                 *class_args, **class_kwargs
10             )
11             # current_app.ensure_sync 不用看
12             # 當成 return  self.dispatch_request(**kwargs)
13             return current_app.ensure_sync(self.dispatch_request)(**kwargs)  
14         else:
15             self = cls(*class_args, **class_kwargs)
16             def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
17                 return current_app.ensure_sync(self.dispatch_request)(**kwargs)  
18             if cls.decorators:
19                 # 把view的名字改為 傳入的name,否則它一直叫view--》endpoint沒傳,就報錯
20                 # view.__name__ = name
21                 for decorator in cls.decorators:
22                     view = decorator(view)
23                     view.__name__ = name
24                     return view
25                 
26 # 2 變成了 app.add_url_rule('/index', view_func=view) ,view的名字是傳入的name
27 
28 # 3 請求來了--》執行view()--->有沒有引數取決於誰?有沒有轉換器,有沒有defaults
29 
30 # 4 執行view本質在執行 self.dispatch_request()--> self是 檢視類的物件
31 
32 # 5 MethodView 的dispatch_request
33 def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue:
34     # 如果是get請求,meth 就是 get方法
35     meth = getattr(self, request.method.lower(), None)
36     # 執行get加括號---》跟django沒有區別
37     return meth(**kwargs)

模板

 1 程式碼示例:
 2 
 3 from flask import Flask, url_for,request
 4 from flask.views import MethodView, View
 5 
 6 app = Flask(__name__)
 7 app.debug = True
 8 
 9 
10 class IndexView(MethodView):
11     def get(self):
12         return 'get請求'
13 
14     def post(self):
15         return 'post請求'
16 
17 
18 class GoodView(View):
19     def dispatch_request(self):
20         if request.method == 'GET':
21             return self.login()
22 
23     def login(self):
24         return 'login'
25 
26 
27 # name其實就是endpoint的別名,且必須傳,如果都傳,以endpoint為準
28 # app.add_url_rule('/index', endpoint='index', view_func=IndexView.as_view(name='jh'))
29 
30 app.add_url_rule('/index', view_func=IndexView.as_view(name='jj'))
31 app.add_url_rule('/login', view_func=GoodView.as_view(name='login'))
32 if __name__ == '__main__':
33     app.run()
34 
35 ------------------------------------------------------------------------------
36 # 1 前後端混合才用
37 
38 # 2 django的dtl,拿到這直接可以用
39     -區別在有的函式不一樣:過濾器,標籤   |safe
40     -if  for  
41     -取字典,取列表 都完全一樣
42     -include
43     -extends
44     
45 # 3 比dtl多的
46     - 函式可以加括號---》就能傳引數

請求響應

 1 from flask import Flask, url_for, request
 2 from flask.views import MethodView, View
 3 
 4 app = Flask(__name__)
 5 app.debug = True
 6 
 7 
 8 class IndexView(MethodView):
 9     def get(self):
10         # 請求物件
11         # request.method  提交的方法
12         print(request.args)  # get請求提及的資料  http://127.0.0.1:5000/index?name=66
13         # request.form   post請求提交的資料
14         # request.values  post和get提交的資料總和,,在postman中測,資料放在body內
15         # request.cookies  客戶端所帶的cookie
16         # request.headers  請求頭
17         # request.path     不帶域名,請求路徑
18         # request.full_path  不帶域名,帶引數的請求路徑
19         # request.script_root
20         # request.url           帶域名帶引數的請求路徑
21         # request.base_url        帶域名請求路徑
22         # request.url_root      域名
23         # request.host_url        域名
24         # request.host            127.0.0.1:500
25         # request.files
26         # obj = request.files['the_file_name']
27         # obj.save('/var/www/uploads/' + secure_filename(f.filename))
28         return 'get請求'
29 
30     def post(self):
31         return 'post請求'
32 
33 
34 app.add_url_rule('/index', view_func=IndexView.as_view(name='jj'))
35 
36 if __name__ == '__main__':
37     app.run()
38 
39 
40 -------------------------------------------------------------------------
41 響應
42 
43 from flask import Flask, url_for, request,make_response,jsonify
44 from flask.views import MethodView, View
45 
46 app = Flask(__name__)
47 app.debug = True
48 
49 
50 class IndexView(MethodView):
51     def get(self):
52         # 1 新手四件套:字串,模板,重定向,json
53         # 2 向cookie中寫入資料
54         res=make_response('get請求') # make_response 放四件套之一都可
55         res.set_cookie('key','lqz',httponly=True,path='/')
56         # 3 向響應頭中寫入資料
57         res.headers['xxxx']='sss'
58         return res
59 
60     def post(self):
61         return 'post請求'
62 
63 
64 
65 app.add_url_rule('/index', view_func=IndexView.as_view(name='lqz'))  #
66 if __name__ == '__main__':
67     app.run()

session

 1 基本使用
 2 
 3 from flask import Flask, url_for, request, session
 4 from flask.views import MethodView, View
 5 
 6 app = Flask(__name__)
 7 app.debug = True
 8 app.secret_key = 'abscdeklx'
 9 
10 
11 @app.get('/')
12 def index():
13     # 寫入session
14     name = request.args.get('name')
15     session['name'] = name
16     return '寫入session成功'
17 
18 
19 @app.get('/home')
20 def home():
21     # 讀取session
22     name = session.get('name')  # 取值
23     # session.pop('name')  刪除
24     # session.clear() 清空
25     return 'session中的name是:%s' % name
26 
27 
28 if __name__ == '__main__':
29     app.run()

不同瀏覽器再開啟設定值取值,都不一樣,不互相影響

原理及原始碼

  # open_session
    def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None:
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        val = request.cookies.get(self.get_cookie_name(app))
        if not val:
            return self.session_class()
        max_age = int(app.permanent_session_lifetime.total_seconds())
        try:
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

    # save_session
    def save_session(
        self, app: Flask, session: SessionMixin, response: Response
    ) -> None:
        name = self.get_cookie_name(app)
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)
        secure = self.get_cookie_secure(app)
        samesite = self.get_cookie_samesite(app)
        httponly = self.get_cookie_httponly(app)

        # Add a "Vary: Cookie" header if the session was accessed at all.
        if session.accessed:
            response.vary.add("Cookie")

        # If the session is modified to be empty, remove the cookie.
        # If the session is empty, return without setting the cookie.
        if not session:
            if session.modified:
                response.delete_cookie(
                    name,
                    domain=domain,
                    path=path,
                    secure=secure,
                    samesite=samesite,
                    httponly=httponly,
                )
                response.vary.add("Cookie")

            return

        if not self.should_set_cookie(app, session):
            return

        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))  # type: ignore
        response.set_cookie(
            name,
            val,  # type: ignore
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite,
        )
        response.vary.add("Cookie")

其他引數

# 1 雖然操作session,本質還是以cookie形式儲存到瀏覽器中了
    -過期時間,httponly
    -配置檔案配置

請求擴充套件

 1 # 1 flask中叫請求擴充套件---》本質作用實現像django中介軟體的作用一樣
 2 
 3 # 2 flask也有中介軟體,但是一般不用,用請求擴充套件即可
 4 
 5 # 3 常用的
 6 1 before_request
 7 2 after_request
 8 3 teardown_request
 9 4 errorhandler
10 
11 
12 # 4 案例
13 '''
14 # 1 before_request :
15     1 請求來進檢視函式之前執行
16     2 多個會從上往下依次執行
17     3 如果返回None,表示繼續下一個
18     4 如果返回了四件套:表示結束,不繼續往後走
19 '''
20 
21 @app.before_request
22 def before01():
23     print('來了老弟1')
24     # 向請求物件中,放值
25     request.name='lqz'
26 
27 @app.before_request
28 def before02():
29     print('來了老弟2')
30 
31 
32 '''
33 # 1 after_request :
34     1 檢視函式執行完,走
35     2 多個會從下往上依次執行
36     3 必須有返回值,是響應物件
37     4 處理跨域,再響應頭中加--》就用它
38 '''
39 @app.after_request
40 def after01(response):
41     print('走了老弟1')
42     return response
43 @app.after_request
44 def after01(response):
45     print('走了老弟2')
46     response.headers['ssss']='sss'
47     return response
48 
49 
50 '''
51 teardown_request
52     -1 無論檢視函式執行成功或失敗,都會走它
53     -2 即便檢視函式執行出異常,也會走
54     -3 一般用來記錄日誌
55 '''
56 @app.teardown_request
57 def teardown(exc):
58     # exc是檢視函式錯誤物件--》記錄錯誤日誌
59     print(exc)
60 
61 
62 '''
63 errorhandler
64     -1 監聽http響應狀態碼
65     -2 全域性異常處理
66 
67 '''
68 @app.errorhandler(404)
69 def error_404(arg):
70     return jsonify({'code':'xxx'})
71 @app.errorhandler(500)
72 def error_500(arg):
73     return jsonify({'code':'500錯誤了'})

 1 案例
 2 
 3 from urllib import request
 4 
 5 from flask import Flask
 6 
 7 app = Flask(__name__)
 8 app.debug = True
 9 
10 
11 # before_request:請求來進檢視函式之前執行
12 @app.before_request
13 def before01():
14     print('來啦')
15     request.name = '張三'
16 
17 
18 @app.before_request
19 def before02():
20     print('來啦2')
21 
22 
23 @app.get('/')
24 def index():
25     print(request.name)
26     return '成功'
27 
28 
29 if __name__ == '__main__':
30     app.run()

相關文章