提到 Django 和 Flask,Tornado 开发者印象最深的可能就要属这个视图函数了,使用简单但是难以扩展,尤其这个 if request.method == 'GET'
着实不够优雅,引起使用起来非常受限。这样一个明显的缺陷,不可能不被官方注意到,两者也确实提供了更强大的的视图类。
Django 1.8 的文档 中提供了这样的示例:
# some_app/views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
# urls.py
from django.conf.urls import url
from some_app.views import AboutView
urlpatterns = [
url(r'^about/', AboutView.as_view()),
]
与 Tornado 的用法还是有些区别的。Tornado 中直接把 ViewHandler 类作为了视图,Django 中却是类方法的运行结果。我们不妨来看看这个 as_view()
方法是如何实现的,其实现代码在 TemplateView
的父类 View
中:
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
只不过是封装了一个根据 HTTP Method 调用特定方法的函数,将这个函数作为返回值返回并与对应路由绑定。
Tornado 开发者需要注意的是,Django View class 的 HTTP 处理方法需要传入 request
参数,而不是像 Tornado 那样调用自身的 self.request
。区别的原因在于,Django 并非 Tornado 那样以类作为处理请求的基本单位,Django 的视图类本质上还是视图函数的一层封装。
Django 中还有一种 MixIn 的用法,懒得看了 :P
Flask 在 “step by step” 的理念下,尽可能地保持了功能的简单性,也是函数视图大行其道的原因。Flask 在开发中,很大程度上借鉴了 Django 的设计,当然“all in one”思想除外,Flask 同样提供了 flask.view.View
来快速生成视图,原理和 Django 类似,不同的是 Flask 中广泛应用的装饰器需要这样使用:
from flask.views import MethodView
class UserAPI(MethodView):
def get(self):
users = User.query.all()
...
def post(self):
user = User.from_form_data(request.form)
...
def user_required(f):
"""Checks whether user is logged in or raises error 401."""
def decorator(*args, **kwargs):
if not g.user:
abort(401)
return f(*args, **kwargs)
return decorator
view = user_required(UserAPI.as_view('users'))
app.add_url_rule('/users/', view_func=view)
0.8 版本之后提供了装饰器列表简化装饰器的使用:
class UserAPI(MethodView):
decorators = [user_required]
相关文档:Flask 插拨式视图
Django 和 Flask 都并非一开始就支持类视图,因此提供的类视图对重构都非常友好,基本上只需要将函数的代码复制到新的视图类中就可以了。推荐有需要的函数视图尽可能地重构为类视图,以提供更强大的继承和复用功能。