Django类视图(CBV)
FBV(function base views) 基于函数的视图,就是在视图里使用函数处理请求。
CBV(class base views) 基于类的视图,就是在视图里使用类处理请求。
在前面,已经使用了FBV视图,在定义函数阶段,只要定义参数request,并且绑定响应的路由,就可以做到视图与路由的绑定
但是函数视图对于复杂项目,用起来更加繁琐,无法实现更加方便的面向对象,并且可以配合Django实现Mixin类型。
而CBV视图,在大规模项目中,用起来更加方便简单。
1. 类视图的基本用法
类视图与函数视图不一样,我们需要实现以下几种定义
- 函数中为不同的HTTP请求方法单独定义函数,例如接受POST请求,就需要类中定义post函数,以此类推
- 类视图必须继承自 django.views.View 父类
- 类视图的请求入口,通过类中的as_view()静态方法实现->View 父类 已经定义
这里简单实现一个登录接口的类视图
注意事项!!as_view必须带括号!!!
urls.py
from django.urls import path
import user.views
urlpatterns = [
path("admin/", admin.site.urls), # 基于FBV视图的实现,不需要带括号
path("login/", user.views.UserLogin.as_view(), name='login'), # as_view要带括号
]
views.py
from django.views import View
from django.http import HttpResponse
from . import *
class UserLogin(View):
"""
必须继承View父类
"""
def get(self, request):
"""
定义get请求时返回一个login.html
"""
return render(request, 'login.html')
def post(self, request):
"""
定义post请求时返回登录结果,并添加上session
"""
username = request.POST.get('username').lower()
password = request.POST.get('password')
user_obj = User.objects.filter(username=username, password=md5_enc(password))
if user_obj.exists():
user_obj = user_obj[0]
user_id = user_obj.id
rep = HttpResponse(f'登录成功,用户ID:{user_id}!!')
request.session['userid'] = user_obj.id
else:
rep = HttpResponse('用户名或密码错误!')
return rep
通过CBV视图,我们可以更加清晰的分离每个接口之间不同请求的业务逻辑,对于GET请求是查询信息,POST更注重验证/更改信息
2. 为什么要as_view(),怎么实现的?为什么CBV不用带括号
看了1中对于路由的绑定,应该对于这个问题非常好奇,那么我们看看CBV父类View中as_view()的源码
django.views.View.as_view() 源码部分
@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
""" 翻译: 请求~响应过程的主入口点 """
......
......
# 上面这一部分抛开不看,就是遍历initkwargs,看看传的参数会不会有问题
def view(request, *args, **kwargs): # 这里是重点,因为最后函数返回的就是view
self = cls(**initkwargs) # 获取实例化类对象
self.setup(request, *args, **kwargs)
if not hasattr(self, "request"): # 这里是如果没有request这个属性,肯定就报错
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs) # 这里是重点,调用了dispath函数
# 所以我们着重去看一下dispatch函数实现了什么
# 下面有一些省略,是关于文档的
......
......
return view # 最后返回的是内部的闭包函数view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
"""
解析: 尝试通过请求对象,分配对应的请求方法,如果方法不在http_method_names列表就报错
"""
if request.method.lower() in self.http_method_names: # 这里是判断请求对象的方法的小写,在不在http_method_names列表
handler = getattr(
self, request.method.lower(), self.http_method_not_allowed
)
# 如果在的话,就通过getattr返回我们定义的请求类的对应方法,例如post就返回post方法
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # 最后返回一个self.method方法
####
# 通过上面的源码解析,我们可以看出实际调用顺序:
as_view() -> dispatch() -> 对应的请求方法(不带括号)
所以其实就等于我们FBV中直接指定 XXX请求一样
# 所以as_view()需要带括号,因为他是一个函数,返回一个方法