Django 基础:类视图与中间件
类视图
引入
以函数的方式定义的视图称为函数视图,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同的 HTTP 请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。
def register(request):
""" 处理注册 """
# 获取请求方法,判断是GET/ POST 请求
if request.method == "GET":
# 处理GET请求,返回注册页面
return render(request, "register.html")
else:
# 处理 POST 请求,实现注册逻辑
return HttpResponse("这里实现注册逻辑")
简介
在 Django 中也可以使用类来定义一个视图,称为类视图
实例
使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义。如下所示
from django.views.generic import View
class RegisterView(View):
""" 类视图:处理注册 """
def get(self, request):
""" 处理 GET 请求,返回注册页面 """
return render(request, "register.html")
def post(self, request):
""" 处理 POST 请求,实现注册逻辑 """
return HttpResponse("这里实现注册逻辑")
好处
代码可读性好,类视图相对于函数视图有更高的复用性,如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可
使用
定义类视图需要继承自 Django 提供的父类 View,可使用 from django.views.generic import view 或者 from django.views.generic.base import View 导入,定义方式如上所示:
配置路由时,使用类视图的 as_view() 方法来添加
urlpatterns = [
# 视图函数:注册
# url(r‘^register/$’, views.register, name='register')
# 类视图:注册
url(r'^register/$', views.RegisterView.as_view(), name='register')
]
原理
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
""
... 省略代码 ...
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
# 调用 dispatch 方法,按照不同请求方式调用不同请求方法
return self.dispathc(request, *args, **kwargs)
... 省略代码 ...
# 返回真正的函数视图
return view
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
使用装饰器
为类视图添加装饰器,可以使用两种方法
为了理解方便,我们先来定义一个为函数视图准备的装饰器(在设计装饰器时基本都以函数视图作为考虑的被封装对象),及一个要被装饰的类视图。
def my_decorator(func):
def wrapper(request, *args, **kwargs):
print("自定义装饰器被调用了")
print("请求路径 %s" % request.path)
return func(request, *args, **kwargs)
return wrapper
class DemoView(View):
def get(self, request):
print("get 方法")
return HttpResponse("OK")
def post(self, request):
print("POST 方法")
return HttpResponse("OK")
在 URL 配置中装饰
urlpatterns = [
url(r'^demo/$', my_decorate(DemoView.as_view()))
]
此种方法最简单,但因装饰行为被放置到了 url 配置中,单看视图的时候无法知道此视图还被添加了装饰器,不利于代码的完整性,不建议使用。
此种方法会为类视图中的所有请求方法都加上装饰器行为(因为是在视图入口处,分发请求方式前)
在类视图中装饰
在类视图中使用为函数视图准备的装饰器时,不能直接添加装饰器,需要使用 method_decorator 将其转换为适用于类视图方法的装饰器。method_decorator 装饰器使用 name 参数指明被装饰的方法。
# 为全部请求方法添加装饰器
@method_decorator(my_decorator, name="dispatch")
class DemoView(View):
def get(self, request):
print("get 方法")
return HttpResponse("OK")
# 为特定请求方法添加装饰器
@method_decorator(my_decorator, name="get")
class DemoView(View):
def get(self, request):
print("GET方法")
return HttpResponse("OK")
def post(self, request):
print("post 方法")
return HttpResponse("OK")
如果需要为类视图的多个方法添加装饰器,但又不是所有方法(为所有方法添加装饰器参考上面例子),可以直接在需要添加装饰器的方法上使用 method_decorator ,如下所示:
from django.utils.decorators import method_decorator
# 为特定请求方法添加装饰器
class DemoView(View):
@method_decorator(my_decorator)
def get(self, request):
print("get 方法")
return HttpResponse("OK")
@method_decorator(my_decorator)
def post(self, request):
print("post 方法")
return HttpResponse("OK")
def put(self, request):
print("put 方法")
return HttpResponse("OK")
Mixin 扩展类
使用面向对象多继承的特性,可以通过定义父类(作为扩展类),在父类中定义想要向类视图补充的方法,类视图继承这些扩展父类,便可以实现代码复用。
定义的扩展父类名称通常以 Mixin 结尾。
举例如下:
class ListModelMixin(object):
""" list 扩展类 """
def list(self, request, *args, **kwargs):
...
class CreateModelMixin(object):
""" create 扩展类 """
def create(self, request, *args, **kwargs):
...
class BooksView(CreateModelMixin, ListModelMixin, View):
""" 同时继承两个扩展类,复用 list 和 create 方法 """
def get(self, request):
self.list(request)
...
def post(self, request):
self.create(request)
...
class SaveOrderView(CreateModelMixin, View):
""" 继承 CreateModeMixin 扩展类,复用 create 方法 """
def post(self, request):
self.create(request):
...
中间件
简介
Django 中的中间件是一个轻量级,底层的插件系统,可以介入 Django 的请求和响应处理过程,修改 Django 的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了 Django 框架的健壮性。
我们可以使用中间件,在Django 处理视图的不同阶段对输入或输出进行干预。
定义方法
定义一个中间件工厂函数,然后返回一个可以调用的中间件。中间件工厂函数需要接收一个可以调用的 get_response 对象。返回的中间件也是一个被调用的对象,并且像视图一样需要接收一个 request 对象参数,返回一个response 对象。
def simple_middleware(get_response):
# 此处编写的代码仅在 Django 第一次配置和初始化的时候执行一次
def middleware(request):
# 此处编写的代码会在每个请求处理视图前被调用。
response = get_response(request)
# 此处编写的代码会在每个请求处理视图之后被调用
return response
return middleware
例如:在 users 应用中新建一个 middleware.py 文件
def my_middleware(get_response):
print("init 被调用")
def middleware(request):
print("before request 被调用")
response = get_response(request)
print("after response 被调用")
return response
return middleware
定义好中间件后,需要在 settings.py 文件中添加注册的中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'users.middleware.my_middleware', # 添加中间件
]
定义一个视图进行测试
def demo_view(request):
print("view 视图被调用")
return HttpResponse("OK")
执行结果
Django 运行在调试模式下,中间 init 部分有可能被调用两次
多个中间件的执行顺序
- 在请求视图被处理前,中间件由上至下依次执行
- 在请求视图被处理后,中间件由下至上依次执行
定义两个中间件,实例:
def my_middleware(get_response):
print("init 调用")
def middleware(request):
print("before request 被调用")
response = get_response(request)
print("after response 被调用")
return response
return middleware
def my_middleware2(get_response):
print("init2 被调用")
def middleware(request):
print("before request 2 被调用")
response = get_response(request)
print("after response 2 被调用")
return response
return middleware
注册添加两个中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'users.middleware.my_middleware', # 添加
'users.middleware.my_middleware2', # 添加
]
执行结果
init2 被调用
init 被调用
before request 被调用
before request 2 被调用
view 视图被调用
after response 2 被调用
after response 被调用