Flask 視圖


寫個驗證用戶登錄的裝飾器:在調用函數前,先檢查session里有沒有用戶

from functools import wraps
from flask import session, abort
 
def login_required(func):
    @wraps(func)
    def decorated_function(*args, **kwargs):
        if not 'user' in session:
            abort(401)
        return func(*args, **kwargs)
    return decorated_function
 
app.secret_key = '12345678'

需將此裝飾器加在每個需要驗證登錄的請求方法上即可

@app.route('/admin')
@login_required
def admin():
    return '<h1>Admin Dashboard</h1>'

URL集中映射

Flask也支持像Django一樣,把URL路由規則統一管理,而不是寫在視圖函數上

我們先來寫個視圖函數,將它放在一個”views.py”文件中:

def foo():
    return '<h1>Hello Foo!</h1>'

然后在Flask主程序上調用”app.add_url_rule”方法:

    
app.add_url_rule('/foo', view_func=views.foo)

這樣,路由”/foo”就綁定在”views.foo()”函數上了,效果等同於在”views.foo()”函數上加上”@app.route(‘/foo’)”裝飾器。通過”app.add_url_rule”方法,我們就可以將路由同視圖分開,將路由統一管理,實現完全的MVC。

裝飾器本質上是一個閉包函數,所以我們當然可以把它當函數使用:

app.add_url_rule('/foo', view_func=login_required(views.foo))

可插拔視圖Pluggable View

from flask.views import View
 
class HelloView(View):
    def dispatch_request(self, name=None):
        return render_template('hello-view.html', name=name)
 
view = HelloView.as_view('helloview')
app.add_url_rule('/helloview', view_func=view)
app.add_url_rule('/helloview/<name>', view_func=view)

我們創建了一個”flask.views.View”的子類,並覆蓋了其”dispatch_request()”函數,渲染視圖的主要代碼必須寫在這個函數里。然后我們通過”as_view()”方法把類轉換為實際的視圖函數,”as_view()”必須傳入一個唯一的視圖名。此后,這個視圖就可以由”app.add_url_rule”方法綁定到路由上了。上例的效果,同本篇第一節中”/hello”路徑的效果,完全一樣。

這個例子比較簡單,只是為了介紹怎么用視圖類,體現不出它的靈活性,我們再看個例子:

class RenderTemplateView(View):
    def __init__(self, template):
        self.template = template
 
    def dispatch_request(self):
        return render_template(self.template)
 
app.add_url_rule('/hello', view_func=RenderTemplateView.as_view('hello', template='hello-view.html'))
app.add_url_rule('/login', view_func=RenderTemplateView.as_view('login', template='login-view.html'))

視圖裝飾器支持

class HelloView(View):
    decorators = [login_required]
 
    def dispatch_request(self, name=None):
        return render_template('hello-view.html', name=name)

我們只需將裝飾器函數加入到視圖類變量”decorators”中即可。它是一個列表,所以能夠支持多個裝飾器,並按列表中的順序執行。

請求方法的支持

當我們的視圖要同時支持GET和POST請求時,視圖類可以這么定義:

class MyMethodView(View):
    methods = ['GET', 'POST']
 
    def dispatch_request(self):
        if request.method == 'GET':
            return '<h1>Hello World!</h1>This is GET method.'
        elif request.method == 'POST':
            return '<h1>Hello World!</h1>This is POST method.'
 
app.add_url_rule('/mmview', view_func=MyMethodView.as_view('mmview'))

我們只需將需要支持的HTTP請求方法加入到視圖類變量”methods”中即可。沒加的話,默認只支持GET請求。

基於方法的視圖

上節介紹的HTTP請求方法的支持,的確比較方便,但是對於RESTFul類型的應用來說,有沒有更簡單的方法,比如省去那些if, else判斷語句呢?Flask中的”flask.views.MethodView”就可以做到這點,它是”flask.views.View”的子類。我們寫個user API的視圖吧:

from flask.views import MethodView
 
class UserAPI(MethodView):
    def get(self, user_id):
        if user_id is None:
            return 'Get User called, return all users'
        else:
            return 'Get User called with id %s' % user_id
 
    def post(self):
        return 'Post User called'
 
    def put(self, user_id):
        return 'Put User called with id %s' % user_id
 
    def delete(self, user_id):
        return 'Delete User called with id %s' % user_id

現在我們分別定義了get, post, put, delete方法來對應四種類型的HTTP請求,注意函數名必須這么寫。怎么將它綁定到路由上呢?

user_view = UserAPI.as_view('users')
# 將GET /users/請求綁定到UserAPI.get()方法上,並將get()方法參數user_id默認為None
app.add_url_rule('/users/', view_func=user_view, 
                            defaults={'user_id': None}, 
                            methods=['GET',])
# 將POST /users/請求綁定到UserAPI.post()方法上
app.add_url_rule('/users/', view_func=user_view, 
                            methods=['POST',])
# 將/users/<user_id>URL路徑的GET,PUT,DELETE請求,
# 綁定到UserAPI的get(), put(), delete()方法上,並將參數user_id傳入。
app.add_url_rule('/users/<user_id>', view_func=user_view, 
                                     methods=['GET', 'PUT', 'DELETE'])

上例中”app.add_url_rule()”可以傳入參數default,來設置默認值;參數methods,來指定支持的請求方法。

如果API多,有人覺得每次都要加這么三個路由規則太麻煩,可以將其封裝個函數:

def register_api(view, endpoint, url, primary_id='id', id_type='int'):
    view_func = view.as_view(endpoint)
    app.add_url_rule(url, view_func=view_func,
                          defaults={primary_id: None},
                          methods=['GET',])
    app.add_url_rule(url, view_func=view_func,
                          methods=['POST',])
    app.add_url_rule('%s<%s:%s>' % (url, id_type, primary_id),
                          view_func=view_func,
                          methods=['GET', 'PUT', 'DELETE'])
 
register_api(UserAPI, 'users', '/users/', primary_id='user_id')

現在,一個”register_api()”就可以綁定一個API了

延遲加載視圖

當某一視圖很占內存,而且很少會被使用,我們會希望它在應用啟動時不要被加載,只有當它被使用時才會被加載。也就是接下來要介紹的延遲加載。Flask原生並不支持視圖延遲加載功能,但我們可以通過代碼實現。這里,我引用了官方文檔上的一個實現。

from werkzeug import import_string, cached_property
 
class LazyView(object):
    def __init__(self, import_name):
        self.__module__, self.__name__ = import_name.rsplit('.', 1)
        self.import_name = import_name
 
    @cached_property
    def view(self):
        return import_string(self.import_name)
 
    def __call__(self, *args, **kwargs):
        return self.view(*args, **kwargs)
def bar():
    return '<h1>Hello Bar!</h1>'

我們先寫了一個LazyView,然后在views.py中定義一個名為bar的視圖函數:

現在讓我們來綁定路由:

app.add_url_rule('/lazy/bar', view_func=LazyView('views.bar'))

路由綁定在LazyView的對象上,因為實現了__call__方法,所以這個對象可被調用,不過只有當’/lazy/bar’地址被請求時才會被調用。此時”werkzeug.import_string”方法會被調用,看了下Werkzeug的源碼,它的本質就是調用”__import__”來動態地導入Python的模塊和函數。所以,這個”view.bar”函數只會在’/lazy/bar’請求發生時才被導入到主程序中。不過要是每次請求發生都被導入一次的話,開銷也很大,所以,代碼使用了”werkzeug.cached_property”裝飾器把導入后的函數緩存起來

同上一節的”register_api()”函數一樣,你也可以把綁定延遲加載視圖的代碼封裝在一個函數里。

def add_url_for_lazy(url_rule, import_name, **options):
    view = LazyView(import_name)
    app.add_url_rule(url_rule, view_func=view, **options)
 
add_url_for_lazy('/lazy/bar', 'views.bar')

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM