对于视图层,除了可以用函数作为视图(FBV
),还可以用类作为视图(CBV
),即在类中定义方法对应http的各种请求方法。使用CBV可以使用类具有的一些特性,如继承、多态等。
- 所有类视图在映射到url时,都需调用视图的
as_view()
类方法。
path("/index",views.MyView.as_view(),name="MyView")
文章目录
一、CBV的原理
在进行url映射到视图的时候,调用CBV
的类方法as_view()
,该方法返回一个视图函数view
。而该视图函数调用另一个dispatch()
方法,该方法通过getattr()
方法进行反射,以返回类中定义的不同方法。
流程:
class CBV_demo(View):
def dispatch(self,request,*args,**kwargs):
print('before')
ret = super(View,self).dispatch(request,*args,*kwargs)
print('after')
return ret
def get(self,request,*args,*kwargs):
return HttpResponse('method_GET')
...
故可以通过重写
dispatch()
方法定义多个共同的行为,然后基于继承进行拓展,以避免实现重复的功能。
二、View
django.views.generic.base.View
是主要的类视图,是所有类视图的基类,同时该类也可通过django.views.View
导入。在实现自己的类视图时,需要继承自该视图,然后根据请求的method
,来实现不同的方法,如对于视图要实现GET
方式的请求,则可在类中定义get(self,request,*args,*kwargs)
方法,默认支持的响应方法有:
['get','post','put','patch','delete','head','options','trace']
视图执行的流程:
setup()
:
该方法进行属性的初始化。dispatch()
:
基于反射机制将不同的请求映射到不同的方法。http_method_not_allowed()
:
若出现了未在类中实现的对应请求方法时,该做出的处理。如仅在类中实现了GET
请求,而为实现POST
请求,则当以POST
请求该url时,会执行该方法。options()
三、TemplateView
django.views.generic.base.TemplateView
,该类视图是专门用来返回模板的,且对于不同的响应执行的是相同的操作。
重要属性:
template_name
属性:模板存储的路径。
方法执行流程:
-
setup()
-
dispatch()
-
http_method_not_allowed()
-
get_context_data()
:用于返回上下文数据,即传递给模板的参数。
实例代码:
from django.views.generic.base import TemplateView
class MyTemplateView(TemplateView):
template_name = "index.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = User.objects.filter(username="wjiaman")
context['user'] = user
return context
而若在模板中不需要传递任何参数,则可以在urls.py
中直接使用TemplateView
来渲染模板,示例代码:
path('about/',TemplateView.as_view(template_name="about.html"))
四、ListView
用以处理列表数据的陈列。我们只需在一个ListView
做好相关配置,Django 会自动帮我们实现分页算法。
class ArticleListView(ListView):
model = Article
template_name = 'article_list.html'
paginate_by = 10
context_object_name = 'articles' # 传递给前端模板的上下文名称
ordering = 'create_time'
page_kwarg = 'page' # 选择页面的参数
def get_context_data(self,**kwargs): # 若要附加额外数据,需要重写该方法
context = super(ArticleListView,self).get_context_data(**kwargs)
print(context)
return context
def get_queryset(self): # 从数据库中获取表单数据
return Article.objects.filter(id__lte=89)
- 若除了表单数据,还想给传递给模板的上下文数据附加其他数据,则需要重写
get_context_data()
方法,且记得在方法中调用父类方法,以获得原始上下文。- 可以重写
get_queryset()
方法,选择从数据库中获取的数据。
Paginator与Page类
Paginator
与Page
类在使用ListView
的时候实现分页功能,它们的位置为:django.core.paginator.Paginator
与django.core.paginator.Page
。
1.Paginator常用属性与方法
count
:总共有多少条数据。num_pages
:总共有多少页。page_range
:页面的区间范围,若有n页,则其为range(1,n+1)
。
2.Page常用属性与方法
has_next
:是否还有下一页;has_previous
:是否有上一页;next_page_number
:下一页页码;previous_page_number
:上一页的页码;number
:当前页;start_index
:当前页第一条数据的索引;end_index
:当前页最后一条数据的索引。
五、给视图添加装饰器
如果使用的为FBV
则只需要在视图函数上写上装饰器即可;若使用的为CBV
,则可以通过以下两种方式实现:
1.装饰dispatch
from django.util.decorators import method_decorator
def login_required(func):
def wrapper(request,*agrs,**kwargs):
if request.GET.get("username"):
return func(request,*args,**kwargs)
else:
return redirect(reverse('login'))
return wrapper
class IndexView(View):
@method_decorator(login_required)
def dispatch(self,request,*args,**kwargs):
super(IndexView,self).dispatch(request,*args,**kwargs)
def get(self,request,*args,**kwargs):
return render(request,"index.html")
2.直接装饰在类上
from django.utils.decorators import method_decorator
def login_required(func):
def wrapper(request,*agrs,**kwargs):
if request.GET.get("username"):
return func(request,*args,**kwargs)
else:
return redirect(reverse('login'))
return wrapper
@method_decorator(login_required,name='dispatch')
class IndexView(View):
def get(self,request,*args,**kwargs):
return render(request,"index.html")
def dispatch(self,) #此时仍需重写dispatch方法,该dispatch方法仅是简单地调用父类的dispatch
super(IndexView,self).dispatch(request,*args,**kwargs)
六、类视图传参问题
在类视图的方法中,通过**kwargs
获取具名参数。如:
# urls.py
path("blog/<str:book_name>", views.AddBook.as_view())
# views.py
class AddBook(View):
def get(self, request, *arg, **kwargs):
book = kwargs.get("book_name")
...