6.3 基于类的通用视图
2025-02-17
6.3.1 用于渲染模板的TemplateView
- 当视图中没有复杂的业务逻辑,如系统的引导页面,欢迎页面等,使用TemplateView是明智的选择
- TemplateView的定义
class TemplateView(TemplateResponseMixin, ContextMixin, View)
- ContextMixin:这个类中定义了一个方法:get_context_data。它返回一个字典对象,用于渲染模板上下文。通常,在使用TemplateView时都会重写这个方法,给模板提供上下文数据
- TemplateResponseMixin:这个类中定义了两个重要的属性:template_name和render_to_response方法。其中:
- template_name用于指定模板路径,它是必须要提供的
- render_to_response方法根据模板路径和上下文数据(context)返回TemplateResponse
Django查找Template的策略
- 项目的settings.py 文件可以找到
TEMPLATES
# settings
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]- APP_DIRS默认为True(搜索应用下的template目录);False:需要设置DIRS指定模板的位置
# settings.py
'DIRS': [os.path.join(BASE_DIR, 'templates')]- 当设置了DIRS和APP_DIRS=True,也会使用DIRS指定路径下模板文件
- 例子:
- “”是Django模板系统中引用变量的形式,在使用当前的模板时,context需要包含hello。另外,当前模板文件的完整路径是my_bbs/post/templates/post/index.html
- 利用TemplateView实例对index.html的渲染
# views.html
from django.views.generic import TemplateView
class IndexView(TemplateView):
template_name = 'post/index.html'
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['hello'] = 'Hello Django BBS post APP'
return context重写了get_context_data方法,并在上下文字典中加入了hello, 所以,index.html模板渲染的结果就是:Hello Django BBS。
最后,还需要给IndexView配置URL模式,在post/urls.py文件中添加路由指向
为什么建议使用TemplateView
为什么Django还要提供TemplateView这个通用视图呢?
类的特点是抽象,将共性抽离出来,再利用继承去实现特定的逻辑,可以在很大程度上实现代码复用。而这一点对于函数来说,是很难做到的。虽然可以使用装饰器给函数添加额外的功能,但是这也增加了代码实现的难度和复杂性。
对于通用视图来说,使用它们的优势是可以更加专注地实现业务逻辑,避免了两类样板式的代码。
第一类,对应HTTP请求类型的同名(小写)请求方法。例如,IndexView中并没有提供get方法,但是可以接受GET请求。
第二类,返回HttpResponse对象。同样,也没有在IndexView中返回任何响应,这也是在TemplateView中完成的。
TemplateView是Django提供的一个最简单的通用视图,用于展示给定的模板,但是,不应该在这里实现创建或更新对象的操作
具体示例代码
# settings.py
import os
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR), 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]# post/urls.py
from django.urls import path
from post import views
urlpatterns = [
path('index/', views.IndexView.as_view())
# View的as_view方法给出视图类的可调用入口
]# post/views.py
# 基于类的视图
from django.views.generic import TemplateView
class IndexView(TemplateView):
# template_name = 'post/index.html' # 除了指定template_name参数,还可以通过函数来获取模板的路径
def get_template_names(self):
return 'post/index.html'
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['hello'] = 'Hello Django BBS templates for view'
return context
# 不使用TemplateView,而使用视图函数的形式,也可实现相同的功能
def index_view(request):
return render(request, 'post/index.html', context={'hello':'Hello Django BBS Template for class'})6.3.2 用于重定向的RedirectView
重定向通用类视图:RedirectView(django/views/generic/base.py)文件中
RedirectView定义了四个类属性和两个方法
permanent:标识是否使用永久重定向,默认为False(302),True(301)
url:重定向的地址
pattern_name:重定向目标URL模式的名称(path中的name参数)
- url和pattern_name至少需要提供一个
query_string:是否将查询字符串拼接到新地址中,默认False(丢弃原地址中的查询字符串)
通常使用RedirectView实现重定向都会重写get_redirect_url方法,在其内部完成视图的业务逻辑,并返回重定向的地址

TemplateView和RedirectView在构建视图中相当有用,能将视图的功能表达的更为简单直接,隐藏了样式的重复代码
6.3.3 用于展示Model列表的ListView
- 展示Model列表的视图在任何一个应用中都会存在,而且会出现多次
- ListView的两个特性
- 可以不提供模板名称,使用默认的规则
- 例如Topic默认的模板名称为:post/topic_list.html
- 提供了template_name,则会使用自定义的模板
- 可以不提供模板名称,使用默认的规则
BaseListView
BaseListView只是为GET请求类型定义了get方法,其中get_queryset、get_allow_empty和get_context_data都来自它的父类MultipleObjectMixin

get_queryset():获取视图展示的Model列表,返回值必须是可迭代的对象,却可以是QuerySet
- 如果提供的queryset是一个Model列表,那么,它并不包含model属性,因此,需要自定义模板名称
get_allow_empty():返回allow_empty属性,它是一个布尔值,默认是True,代表允许展示空的Model列表。如果设置为False,且Model列表为空,则会返回404
get_context_data():返回用于渲染模板的上下文数据

get_context_data - object_list参数即get_queryset方法的返回值,如果它有model属性,那么这个方法会返回model名称与list拼接得到的字符串(在没有设置context_object_name的情况下)。例如,对于Topic来说,这里的返回值就是topic_list
6.3.4 展示Model详情的DetailView
定义了规则生成默认的模板名称:app_label/model_name_detail.html
继承自:BaseDetailView
重新实现Topic详情视图
# view.py
class TopicDetailView(DetailView):
model = Topic
def get_context_data(self, **kwargs):
context = super(TopicDetailView,self).get_context_data(**kwargs)
pk = self.kwargs.get(self.pk_url_kwarg)
context.update({
'comment_list': Comment.objects.filter(topic=pk)
})
return context# urls.py
urlpatterns = [
path('topic_views/<int:pk>/',views.TopicDetailView.as_view()),
]<!-- topic_detail.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% block content %}
<h2>Topic Detail</h2>
<ul>
<li>(id): {{ topic.id }}</li>
<li>(title): {{ topic.title }}</li>
<li>(content): {{ topic.content }}</li>
<li>(user): {{ topic.user.username }}</li>
<li>(created_time): {{ topic.created_time }}</li>
<li>(last_modified): {{ topic.last_modified }}</li>
<li>(评论):
<table border="1">
<tr>
<th>评论id</th>
<th>内容</th>
<th>赞同</th>
<th>返回</th>
</tr>
{% for comment in comment_list %}
<tr>
<td>{{ comment.id }}</td>
<td>{{ comment.content }}</td>
<td>{{ comment.up }}</td>
<td>{{ comment.down }}</td>
</tr>
{% endfor %}
</table>
</li>
</ul>
{% endblock %}
</body>
</html>