5. Django 探究CBV视图

image-20240415001327934

5. 探究CBV视图

Web开发是一项无聊而且单调的工作, 特别是在视图功能编写方面更为显著.
为了减少这种痛苦, Django植入了视图类这一功能, 该功能封装了视图开发常用的代码, 
无须编写大量代码即可快速完成数据视图的开发, 这种以类的形式实现响应与请求处理称为CBV(Class Base Views).
视图类是通过定义和声明类的形式实现的, 根据用途划分3部分: 数据显示视图, 数据操作视图和日期筛选视图.

5.1 数据显示视图

数据显示视图是将后台的数据展示在网页上, 数据主要来自模型, 一共定义了4个视图类,
分别是RedirectView, TemplateView, ListView和DetailView, 说明如下:
 RedirectView用于实现HTTP重定向, 默认情况下只定义GET请求的处理方法.
 TemplateView是视图类的基础视图, 可将数据传递给HTML模板, 默认情况下只定义GET请求的处理方法.
 ListView是在TemplateView的基础上将数据以列表显示, 通常将某个数据表的数据以列表表示.
 DetailView是在TemplateView的基础上将数据详细显示, 通常获取数据表的单条数据.

5.1.1 重定向视图RedirectView

3.3.3小节已简单演示了视图类RedirectView的使用方法, 本小节将深入了解视图类RedirectView.
视图类RedirectView用于实现HTTP重定向功能, 即网页跳转功能.
在Django的源码里可以看到视图类RedirectView的定义过程, 如图5-1所示.

image-20240413001910953

5-1 视图类RedirectView
从图5-1得知, 视图类RedirectView继承父类View, 类View是所有视图类的底层功能类.
视图类RedirectView定义了4个属性和8个类方法, 分别说明如下:
 permanent: 根据属性值的真假来选择重定向方式, 若为True, 则HTTP状态码为301, 否则HTTP状态码为302(临时重定向).
 url: 代表重定向的路由地址.
 pattern_name: 代表重定向的路由命名. 如果已设置参数url, 则无须设置该参数, 否则提示异常信息.
 query_string: 是否将当前路由地址的请求参数传递到重定向的路由地址.
 get_redirect_url(): 根据属性pattern_name所指向的路由命名来生成相应的路由地址.
 get(): 触发HTTP的GET请求所执行的响应处理.
 剩余的类方法head(), post(), options(), delete(), put()和patch()是HTTP的不同请求方式, 它们都由get()方法完成响应处理.3.3.3小节, 视图类RedirectView是在urls.py文件里直接使用的, 由于类具有继承的特性,
因此还可以对视图类RedirectView进行功能扩展, 这样能满足复杂的开发需求.
以MyDjango为例, 在index的urls.py, views.py和模板文件index.html中编写以下代码:
# 新建项目, MyDjango 的 urls.py
from django.contrib import admin
from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),path('', include(('index.urls', 'index'), namespace='index')),
]

image-20240413005858442

# index 的 urls.pu
from django.urls import path
from .views import *urlpatterns = [path('', index, name='index'),path('TurnTo', TurnTo.as_view(), name='TurnTo'),
]

image-20240413005926415

# index 的 views.py
from django.shortcuts import render
from django.views.generic.base import RedirectViewdef index(request):return render(request, 'index.html')class TurnTo(RedirectView):# 设置属性permanent = Falseurl = Nonepattern_name = 'index:index'query_string = True# 重新写get_redirect_url方法def get_redirect_url(self, *args, **kwargs):print('This is get_redirect_url')return super().get_redirect_url(*args, **kwargs)# 重写get方法def get(self, request, *args, **kwargs):print(request.META.get('HTTP_USER_AGENT'))return super().get( request, *args, **kwargs)

image-20240413010049190

<!-- templates 的 index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>首页</title>
</head>
<body>
<h3>Hello RedirectView</h3>
<a href="{% url 'index:TurnTo' %}?k=1">ToTurn</a>
</body>
</html>

image-20240413010214488

在index的views.py中定义了视图类TurnTo, 它继承父类RedirectView, 
对父类的属性进行重设并将父类的类方法get_redirect_url()和get()进行重写,
通过这样的方式可以对视图类RedirectView进行功能扩展, 从而满足开发需求.定义路由的时候, 若使用视图类TurnTo处理HTTP请求, 
则需要对视图类TurnTo使用as_view()方法, 这是对视图类turnTo进行实例化处理.
as_view()方法可在类View里找到具体的定义过程(django\views\generic\base.py).

image-20240413011637269

运行MyDjango项目, 在浏览器上访问127.0.0.1:8000, 当单击'ToTurn'链接后,
浏览器的地址栏将会变为: 127.0.0.1:8000/?k=1, 在PyCharm里可以看到视图类TurnTo的输出内容, 如图5-2所示.(当你访问与TurnTo视图关联的URL时, get方法首先执行, 然后在get方法内部会调用get_redirect_url方法来获取重定向的URL.)

2024-04-13_011137

(客户端向/TurnTo?k=1发起了一个GET请求, 服务器返回了一个302状态码, 并可能在响应头中包含了Location: /?k=1这样的信息.
然后, 客户端遵循这个重定向, 向新的URL/?k=1发起了一个新的GET请求, 这次请求返回了200状态码, 表示请求成功.)

image-20240413005821551

5-2 视图类TurnTo的输出内容
上述例子只是重写了GET请求的响应处理, 如果在开发过程中需要对其他的HTTP请求进行处理, 
那么只要重新定义相应的类方法即可, 比如在视图类TurnTo里定义类方法post(), 该方法是定义POST请求的响应过程.
客户端向/TurnTo?k=1发起了一个GET请求, 服务器返回了一个302状态码, 并可能在响应头中包含了Location: /?k=1这样的信息.
然后, 客户端遵循这个重定向, 向新的URL/?k=1发起了一个新的GET请求, 这次请求返回了200状态码, 表示请求成功.

5.1.2 基础视图TemplateView

视图类TemplateView是所有视图类里最基础的应用视图类, 
开发者可以直接调用应用视图类, 它继承多个父类: TemplateResponseMixin, ContextMixin和View. 
在PyCharm里查看视图类TemplateView的源码, 如图5-3所示.

image-20240413012855963

5-3 视图类TemplateView的源码
从视图类TemplateView的源码看到, 它只定义了类方法get(), 
该方法分别调用函数方法get_context_data()和render_to_response(), 从而完成HTTP请求的响应过程.
类方法get()所调用的函数方法主要来自父类TemplateResponseMixin和ContextMixin,
为了准确地描述函数方法的调用过程, 我们以流程图的形式加以说明, 如图5-4所示.

image-20240413181336009

5-4 视图类TemplateView的定义过程
视图类TemplateView的get()所调用的函数说明如下:
 视图类ContextMixin的get_context_data()方法用于获取模板上下文内容,模板上下文是将视图里的数据传递到模板文件, 再由模板引擎将数据转换成HTML网页数据.
 视图类TemplateResponseMixin的render_to_response()用于实现响应处理, 由响应类TemplateResponse完成.我们可以在视图类TemplateView的源码文件里找到视图类TemplateResponseMixin的定义过程,
该类设置了4个属性和两个类方法, 这些属性和类方法说明如下:
 template_name: 设置模板文件的文件名.
 template_engine: 设置解析模板文件的模板引擎.
 response_class: 设置HTTP请求的响应类, 默认值为响应类TemplateResponse.
 content_type: 设置响应内容的数据格式, 一般情况下使用默认值即可.
 render_to_response(): 实现响应处理, 由响应类TemplateResponse完成.
 get_template_names(): 获取属性template_name的值.
经上分析, 我们已对视图类TemplateView有了一定的了解, 
现在通过简单的例子讲述如何使用视图类TemplateView实现视图功能, 完成HTTP的请求与响应处理.
以MyDjango为例, 在index的urls.py, views.py和模板文件index.html中编写以下代码:
# index 的 urls.py
from django.urls import path
from .views import *urlpatterns = [path('', Index.as_view(), name='index'),
]

image-20240413185626128

# index 的 views,py
from django.views.generic.base import TemplateViewclass Index(TemplateView):template_name = 'index.html'template_engine = Nonecontent_type = Noneextra_context = {'title': 'This is GET'}# 重新定义模板文件上下文的获取方式def get_context_data(self, **kwargs):context = super().get_context_data(**kwargs)context['value'] = 'I am MyDjango'return context# 定义HTTP的POST请求处理方式# 参数request代表HTTP请求信息# 若路由设有变量, 则可从参数kwargs里获取def post(self, request, *args, **kwargs):self.extra_context = {'title': 'This is POST'}context = self.get_context_data(**kwargs)return self.render_to_response(context)

image-20240413185715152

<!-- template 的 index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>首页</title>
</head>
<body>
<h3>Hello {{ title }}</h3>
<div>{{ value }}</div>
<br>
<form action="" method="post">{% csrf_token %}<input type="submit" value="submit"></form>
</body>
</html>
上述代码是将网站首页的视图函数index改为视图类index,
自定义视图类index继承视图类TemplateView, 并重设了4个属性, 重写了两个类方法, 具体说明如下:
 template_name: 将模板文件index.html作为网页文件.
 template_engine: 设置解析模板文件的模板引擎, 默认值为None,即默认使用配置文件settings.py的TEMPLATES所设置的模板引擎BACKEND.
 content_type: 设置响应内容的数据格式, 默认值为None, 即代表数据格式为text/html.
 extra_context: 为模板文件的上下文(模板变量)设置变量值, 可将数据转换成网页数据展示在浏览器上.
 get_context_data(): 继承并重写视图类TemplateView的类方法, 在变量context里新增数据value.
 post(): 自定义POST请求的处理方法, 当触发POST请求时, 将会重设属性extra_context的值, 并调用get_context_data()将属性extra_context重新写入, 从而实现动态改变模板上下文的数据内容.在模板文件index.html里看到模板上下文(模板变量) {{ title }}{{ value }}, 它们的数据来源于get_context_data()的返回值.
当访问: 127.0.0.1:8000 的时候, 上下文title为'This is GET';
当单击'submit'按钮的时候, 上下文title的值改为'This is POST', 如图5-5所示.

2024-04-13_185308

5-5 运行结果(context和extra_context是两个与模板渲染时使用的上下文(context)相关的概念.
context是一个字典, 它包含了传递给模板的所有变量. 在TemplateView或其子类中, 
通常会重写get_context_data方法来定制传递给模板的上下文.
这个方法的默认实现是从视图中的其他属性和传入的 **kwargs 中获取数据, 然后返回一个字典.extra_context是TemplateView类的一个属性, 它是一个字典, 用于存储要添加到模板上下文中的额外变量.
这些变量会在get_context_data方法中被合并到最终的上下文字典中.extra_context用于定义视图类级别的默认上下文变量, 而context则用于在每个请求处理过程中动态构建和定制上下文.)

5.1.3 列表视图ListView

我们知道视图是连接路由和模板的中心枢纽, 除此之外, 视图还可以连接模型.
简单来说, 模型是指Django通过一定的规则来映射数据库, 
从而方便Django与数据库之间实现数据交互, 这个交互过程是在视图里实现的.
由于视图可以与数据库实现数据交互, 因此Django定义了视图类ListView, 
该视图类是将数据表的数据以列表的形式显示, 常用于数据的查询和展示.
在PyCharm里打开视图类ListView的源码文件, 分析视图类ListView的定义过程, 如图5-6所示.

image-20240413191509320

5-6 视图类ListView的源码文件
从视图类ListView的继承方式看到, 它继承两个不同的父类, 这些父类也继承其他的视图类.
为了梳理类与类之间的继承关系, 我们以流程图的形式表示, 如图5-7所示.

image-20240413192202454

5-7 视图类ListView的继承过程
根据上述的继承关系可知, 视图类ListView的底层类是由TemplateResponseMixin, ContextMixin和View组成的,
在这些底层类的基础上加入了模型的操作方法, 从而得出视图类ListView.
分析视图类ListView的定义过程得知, 它具有视图类TemplateView的所有属性和方法, 因为两者的底层类是相同的.
此外, 视图类ListView新增了以下属性和方法.
 allow_empty: 由MultipleObjectMixin定义, 在模型查询数据不存在的情况下是否显示页面, 若为False并且数据不存在, 则引发404异常, 默认值为True.
 queryset: 由MultipleObjectMixin定义, 代表模型的查询对象, 这是对模型对象进行查询操作所生成的查询对象.
 model: 由MultipleObjectMixin定义, 代表模型, 模型以类表示, 一个模型代表一张数据表.
 paginate_by: 由MultipleObjectMixin定义, 属性值为整数, 代表每一页所显示的数据量.
 paginate_orphans: 由MultipleObjectMixin定义, 属性值为整数, 默认值为0,代表最后一页可以包含的'溢出'的数据量, 防止最后一页的数据量过少.
 context_object_name: 由MultipleObjectMixin定义, 设置模板上下文, 即为模板变量进行命名.
 paginator_class: 由MultipleObjectMixin定义, 设置分页的功能类, 默认情况下使用内置分页功能django.core.paginator.Paginator.
 page_kwarg: 由MultipleObjectMixin定义, 属性值为字符串, 默认值为page, 设置分页参数的名称.
 ordering: 由MultipleObjectMixin定义, 属性值为字符串或字符串列表, 主要对属性queryset的查询结果进行排序.
 get_queryset(): 由MultipleObjectMixin定义, 获取属性queryset的值.
 get_ordering(): 由MultipleObjectMixin定义, 获取属性ordering的值.
 paginate_queryset(): 由MultipleObjectMixin定义, 根据属性queryset的数据来进行分页处理.
 get_paginate_by(): 由MultipleObjectMixin定义, 获取每一页所显示的数据量.
 get_paginator(): 由MultipleObjectMixin定义, 返回当前页数所对应的数据信息.
 get_paginate_orphans(): 由MultipleObjectMixin定义, 获取最后一页可以包含的'溢出'的数据量.
 get_allow_empty(): 由MultipleObjectMixin定义, 获取属性allow_empty的属性值.
 get_context_object_name(): 由MultipleObjectMixin定义, 设置模板上下文(模板变量)的名称,若context_object_name未设置, 则上下文名称将由模型名称的小写+'_list'表示, 比如模型PersonInfo, 其模板上下文的名称为personinfo_list.
 get_context_data(): 由MultipleObjectMixin定义, 获取模板上下文(模板变量)的数据内容.
 template_name_suffix: 由MultipleObjectTemplateResponseMixin定义, 设置模板后缀名, 用于设置默认的模板文件.
虽然视图类ListView定义了多个属性和方法, 但实际开发中经常使用的属性和方法并不多.
由于视图类ListView需要使用模型对象, 因此在MyDjango项目里定义PersonInfo模型, 在index的models.py中编写以下代码:
# index的models.py
from django.db import modelsclass PersonInfo(models.Model):  id = models.AutoField(primary_key=True)  # id, 整形, 自增, 主键name = models.CharField(max_length=20)  # 名字, 字符串, 宽度为20age = models.IntegerField()  # 姓名, 整形

image-20240413195837293

上述代码只是搭建PersonInfo类和数据表personinfo的映射关系, 但在数据库中并没有生成相应的数据表.
因此, 下一步通过两者的映射关系在数据库里生成相应的数据表.
数据库以SQLite3为例, 在PyCharm的Terminal里依次输入数据迁移指令.
# 根据models.py生成相关的.py文件,该文件用于创建数据表
D:\MyDjango>python manage.py makemigrations
Migrations for 'index':index\migrations\0001_initial.py- Create model personinfo# 创建数据表
D:\MyDjango>python manage.py migrate

image-20240413200029361

当指令执行完成后, 使用Navicat Premium软件打开MyDjango的db.sqlite3文件, 在此数据库中可以看到新创建的数据表, 如图5-8所示.
(1. 点击连接 --> 2. 选择SQLite --> 3. 设置连接名称 --> 4. 选择现有的数据库文件--> 5. 在数据库库文件中选择文件(文件在项目下) --> 6. 测试连接 --> 7. 连接 )

image-20240413200229190

image-20240413200526861

5-8 数据表信息
从图5-8中看到, 当指令执行完成后, Django会默认创建多个数据表, 
其中数据表index_personinfo对应index的models.py中所定义的PersonInfo类, 
其余的数据表都是Django内置的功能所生成的, 主要用于Admin站点, 用户认证和Session会话等功能.
在数据表index_personinfo中添加如图5-9所示的数据.

image-20240413200752294

5-9 添加数据
完成上述操作后, 下一步在MyDjango里使用视图类ListView, 
在index的views.py里定义视图类index, 并重新编写模板文件index.html的代码:
# index 的 views.py
from django.views.generic import ListView
from .models import PersonInfoclass Index(ListView):# 设置模板文件template_name = 'index.html'# 设置模型外的数据extra_context = {'title': '人员信息表'}# 查询模型queryset = PersonInfo.objects.all()# 每页展示一条数据paginate_by = 1# 若不设置, 则模板上下文默认为personinfo_list# context_object_name = 'personinfo'

image-20240413210242469

<!-- templates 的 index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{{ title }}</title>
<body>
<h3>{{ title }}</h3>
<table border="1">{% for i in personinfo_list %}<tr><th>{{ i.name }}</th><th>{{ i.age }}</th></tr>{% endfor %}
</table>
<br>
{% if is_paginated %}<div class="pagination"><span class="page-links">{% if page_obj.has_previous %}<a href="/?page={{ page_obj.previous_page_number }}">上一页</a>{% endif %}{% if page_obj.has_next %}<a href="/?page={{ page_obj.next_page_number }}">下一页</a>{% endif %}<br><br><span class="page-current">第{{ page_obj.number }}页, 共{{ page_obj.paginator.num_pages }}页.</span></span></div>
{% endif %}
</body>
</html>

image-20240413210814319

视图类index继承父类ListView, 并且仅设置4个属性就能完成模型数据的展示.
视图类ListView虽然定义了多个属性和方法, 但是大部分的属性和方法已有默认值和处理过程, 这些就能满足日常开发需求.
上述的视图类index仅支持HTTP的GET请求处理, 因为父类ListView只定义了get()方法, 
如果想让视图类index也能够处理POST请求, 那么只需在该类下自定义post()方法即可.模板文件index.html按照功能可分为两部分: 数据展示和分页功能.
数据展示编写在HTML的table标签和title标签, 模板上下文title({{ title }})是由视图类index属性extra_context传递的,
模板上下文personinfo_list来自视图类index的属性queryset所查询的数据.
分页功能编写在div标签中, 分页功能相关内容将会在后续章节详细讲述.运行MyDjango项目, 在浏览器上可以看到网页出现翻页功能, 通过单击翻页链接可以查看数据表personinfo的数据信息, 如图5-10所示.

image-20240413210708264

5-10 运行结果

5.1.4 详细视图DetailView

视图类DetailView是将数据库某一条数据详细显示在网页上, 它与视图类ListView存在明显的差异.
在PyCharm里打开视图类DetailView的源码文件, 分析视图类DetailView的定义过程, 如图5-11所示.

image-20240413211331715

5-11 视图类DetailView的源码文件
从视图类DetailView的继承方式看到, 它继承两个不同的父类, 这些父类也继承其他的视图类.
为了梳理类与类之间的继承关系, 我们以流程图的形式表示, 如图5-12所示.

image-20240413212730901

5-12 视图类DetailView的继承过程
根据上述的继承关系可知, 视图类DetailView的底层类由TemplateResponseMixin, ContextMixin和View组成, 
它的设计模式与视图类ListView有一定的相似之处.分析视图类DetailView的定义过程得知, 它不仅具有视图类TemplateView的所有属性和方法, 还新增了以下属性和方法.
 template_name_field: 由SingleObjectTemplateResponseMixin定义, 用于确定模板的名称.
 template_name_suffix: 由SingleObjectTemplateResponseMixin定义,设置模板后缀名, 默认后缀是_detail, 用于设置默认模板文件.
 get(): 由BaseDetailView定义, 定义HTTP的GET请求的处理方法.
 model: 由SingleObjectMixin定义, 代表模型, 模型以类表示, 一个模型代表一张数据表.
 queryset: 由SingleObjectMixin定义, 这是对模型对象进行查询操作所生成的查询对象.
 ontext_object_name: 由SingleObjectMixin定义, 设置模板上下文, 即为模板变量进行命名.
 slug_field: 由SingleObjectMixin定义, 设置模型的某个字段作为查询对象, 默认值为slug.
 slug_url_kwarg: 由SingleObjectMixin定义, 代表路由地址的某个变量, 作为某个模型字段的查询范围, 默认值为slug.
 pk_url_kwarg: 由SingleObjectMixin定义, 代表路由地址的某个变量, 作为模型主键的查询范围, 默认值为pk.
 query_pk_and_slug: 由SingleObjectMixin定义, 若为True, 则使用属性pk_url_kwarg和slug_url_kwarg同时对模型进行查询, 默认值为False.
 get_object(): 由SingleObjectMixin定义, 对模型进行单条数据查询操作.
 get_queryset(): 由SingleObjectMixin定义, 获取属性queryset的值.
 get_slug_field(): 由SingleObjectMixin定义, 根据属性slug_field查找与之对应的数据表字段.
 get_context_object_name(): 由SingleObjectMixin定义, 设置模板上下文(模板变量)的名称,若context_object_name未设置, 则上下文名称将由模型名称的小写表示, 比如模型PersonInfo, 其模板上下文的名称为personinfo.
 get_context_data(): 由SingleObjectMixin定义, 获取模板上下文(模板变量)的数据内容.
从字面上理解新增的属性和方法有一定的难度, 我们不妨以项目实例的形式来加以说明.
以MyDjango为例, 沿用5.1.3小节的模型PersonInfo(index的models.py),
然后在index的urls.py, views.py和模板文件index.html中编写以下代码:
# index 的 views.py
from django.urls import path
from .views import *urlpatterns = [# 定义路由path('<pk>/<age>.html', Index.as_view(), name='index')
]

image-20240413230616588

# index 的 views.py
from django.views.generic import DetailView
from .models import PersonInfoclass Index(DetailView):# 设置模板文件template_name = 'index.html'# 设置模型外的数据extra_context = {'title': '人员信息表'}# 设置模型的查询字段slug_field = 'age'# 设置路由的变量名, 与属性slug_field实现模型的查询操作slug_url_kwarg = 'age'  # spk_url_kwarg = 'pk'  # 默认就是主键, 写了等于白写# 设置查询模型PersonInfomodel = PersonInfo# 属性queryset可以做简单的查询操作# queryset = PersonInfo.objects.all()# 若不设置, 则模板上下文默认为personinfo# context_object_name = 'personinfo'# 是否将pk和slug作为查询条件, 不设置为True, lug_field和slug_url_kwarg无效# query_pk_and_slug = False

image-20240413231245943

<!-- templates 的 index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{{ title }}</title>
<body>
<h3>{{ title }}</h3>
<table border="1"><tr><th>{{ personinfo.name }}</th><th>{{ personinfo.age }}</th></tr>
</table>
</body>
</html>

image-20240413230728969

路由index设有两个路由变量pk和age, 这两个变量为模型PersonInfo的字段id和age提供查询范围.视图类index的属性model以模型PersonInfo作为查询对象;
属性pk_url_kwarg和slug_url_kwarg用于获取指定的路由变量,比如属性pk_url_kwarg, 其值为pk, 等同于路由变量pk的变量名, 
视图类index会根据该值来获取路由变量pk的值, 而且该属性以模型的主键作为查询条件, 假如路由变量pk的值为10, 
视图类index先从属性pk_url_kwarg获取路由变量pk的值, 再从模型查询主键id等于10的数据.再举例说明, 属性slug_url_kwarg的值为age, 它指向路由变量age, 
视图类index会根据属性slug_url_kwarg的值来获取路由变量age的值,
再从属性slug_field来确定查询的字段, 假设属性slug_field的值为age, 路由变量age的值为15, 
视图类index将模型字段age等于15的数据查询出来.假设属性slug_field的值改为name, 属性slug_url_kwarg的值改为name, 路由变量name的值为Tom,
那么视图类index将模型字段name等于Tom的数据查询出来.如果没有设置属性context_object_name, 查询出来的结果就以模型名称的小写表示, 主要用于模板上下文.
比如模型PersonInfo, 查询出来的结果将命名为personinfo, 并且传到模板文件index.html作为模板上下文.属性query_pk_and_slug用于设置查询字段的优先级, 属性pk_url_kwarg用于查询模型的主键, 属性slug_url_kwarg用于查询模型其他字段.
如果query_pk_and_slug为False, 那么优先查询模型的主键, 若为True, 则将主键和slug_field所设置的字段一并查询.综上所述, 视图类DetailView的属性pk_url_kwarg和slug_url_kwarg用于确定模型的查询条件;
属性slug_field用于确定模型的查询字段; 属性query_pk_and_slug用于确定模型主键和其他字段的组合查询.
启动项目, 输入地址: http://127.0.0.1:8000/1/12.html , 通过主键获取数据.

2024-04-13_231312

5.2 数据操作视图

数据操作视图是对模型进行操作, 如增, , , 从而实现Django与数据库的数据交互.
数据操作视图有4个视图类, 分别是FormView, CreateView, UpdateView和DeleteView, 说明如下:
 FormView视图类使用内置的表单功能, 通过表单实现数据验证, 响应输出等功能, 用于显示表单数据.
 CreateView实现模型的数据新增功能, 通过内置的表单功能实现数据新增.
 UpdateView实现模型的数据修改功能, 通过内置的表单功能实现数据修改.
 DeleteView实现模型的数据删除功能, 通过内置的表单功能实现数据删除.

5.2.1 表单视图FormView

视图类FormView是表单在视图里的一种使用方式, 表单是搜集用户数据信息的各种表单元素的集合, 
作用是实现网页上的数据交互, 用户在网站输入数据信息, 然后提交到网站服务器端进行处理, 如数据录入和用户登录, 注册等.
在PyCharm里打开视图类FormView的源码文件, 分析视图类FormView的定义过程, 如图5-13所示.

image-20240413232020179

5-13 视图类FormView的源码文件
从视图类FormView的继承方式可以看到, 它也是继承两个不同的父类, 而父类继承其他的视图类.
为了梳理类与类之间的继承关系, 我们以流程图的形式表示, 如图5-14所示.

image-20240413232500184

5-14 视图类FormView的继承过程
根据上述的继承关系可知, 视图类FormView的底层类是由TemplateResponseMixin, ContextMixin和View组成的,
设计模式与其他视图类十分相似.分析视图类FormView的定义过程得知, 它不仅具有视图类TemplateView的所有属性和方法, 还新增了以下属性和方法.
 initial: 由FormMixin定义, 设置表单初始化的数据.
 form_class: 由FormMixin定义, 设置表单类.
 success_url: 由FormMixin定义, 设置重定向的路由地址.
 prefix: 由FormMixin定义, 设置表单前缀(即表单在模板的上下文), 可在模板里生成表格数据.
 get_initial(): 由FormMixin定义, 获取表单初始化的数据.
 get_prefix(): 由FormMixin定义, 获取表单的前缀.
 get_form_class(): 由FormMixin定义, 获取表单类.
 get_form(): 由FormMixin定义, 调用get_form_kwargs()完成表单类的实例化.
 get_form_kwargs(): 由FormMixin定义, 执行表单类实例化的过程.
 get_success_url(): 由FormMixin定义, 获取重定向的路由地址.
 form_valid(): 由FormMixin定义, 表单有效将会重定向到指定的路由地址.
 form_invalid(): 由FormMixin定义, 表单无效将会返回空白表单.
 get_context_data(): 由FormMixin定义, 获取模板上下文(模板变量)的数据内容.
 get(): 由ProcessFormView定义, 定义HTTP的GET请求的处理方法.
 post(): 由ProcessFormView定义, 定义HTTP的POST请求的处理方法.
以项目实例的形式来加以说明新增的属性和方法.
在MyDjango项目里, 沿用5.1.3小节所定义的模型PersonInfo(index的models.py),
并在index文件夹里创建form.py文件, 最后在index的form.py, urls.py, views.py和模板文件index.html中分别编写以下代码:
# index 的 form.py
from django import forms
from .models import PersonInfoclass PersonInfoForm(forms.ModelForm):class Meta:model = PersonInfofields = '__all__'

image-20240413234937230

# index 的 urls.py
from django.urls import path
from .views import *urlpatterns = [# 定义路由path('', Index.as_view(), name='index'),path('result', result, name='result')
]

image-20240413235000705

# index 的 views.py
from django.views.generic.edit import FormView
from .form import PersonInfoForm
from django.http import HttpResponsedef result(request):return HttpResponse('Success')class Index(FormView):# 设置表单初始化的数据initial = {'name': 'Betty', 'age': 20}# 使用的模板文件template_name = 'index.html'# 重定向地址, 提交表单后跳转的地址success_url = '/result'# 设置表单类form_class = PersonInfoForm# 设置模型外的数据extra_context = {'title': '人员信息表'}

image-20240413235049130

<!-- templates 的 index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{{ title }}</title>
<body>
<h3>{{ title }}</h3>
<form method="post">{% csrf_token %}{{ form.as_p }}<input type="submit" value="确定">
</form>
</body>
</html>

image-20240413235121539

上述代码是视图类FormView的简单应用, 它涉及模型和表单的使用, 说明如下:
 index的form.py里定义了表单类PersonInfoForm, 该表单是根据模型PersonInfo定义的模型表单, 表单的字段来自模型的字段.有关表单的知识点将会在第8章详细讲述.
 路由index的请求处理由视图类FormView完成, 而路由result为视图类index的属性success_url提供路由地址.
 视图类index仅设置了5个属性, 属性extra_context的值对应模板上下文title;属性form_class所设置的表单在实例化之后可在模板里使用上下文form.as_p生成表格, 模板上下文form的命名是固定的, 它来自类FormMixin的get_context_data().
 在网页上单击'确定'按钮, 视图类index就会触发父类FormView所定义的post()方法,然后调用表单内置的is_valid()方法对表单数据进行验证.如果验证有效, 就调用form_valid()执行重定向处理, 重定向的路由地址由属性success_url提供;如果验证无效, 就调用form_invalid(), 在当前页面返回空白的表单.
运行MyDjango项目, 在浏览器上访问127.0.0.1:8000, 发现表单上设有数据, 这是由视图类index的属性initial设置的.
单击'确定'按钮, 浏览器就会自动跳转到路由result, 说明表单验证成功, 如图5-15所示.

2024-04-13_235844

5-15 运行结果

5.2.2 新增视图CreateView

视图类CreateView是对模型新增数据的视图类, 它是在表单视图类FormView的基础上加以封装的.
简单来说, 就是在视图类FormView的基础上加入数据新增的功能.
视图类CreateView与FormView是在同一个源码文件里定义的, 在源码文件里分析视图类CreateView的定义过程,
以流程图的形式表述类的继承关系, 如图5-16所示.

image-20240414000954714

5-16 视图类CreateView的继承过程
从上述的继承关系可知, 视图类CreateView的底层类是由TemplateResponseMixin, ContextMixin和View组成的, 整个设计共继承8个类.
分析视图类CreateView的定义过程得知, 它不仅具有视图类TemplateView, SingleObjectMixin和FormView的所有属性和方法,
还新增或重写了以下属性和方法:
 fields: 由ModelFormMixin定义, 设置模型字段, 以列表表示, 每个字段代表一个列表元素, 可生成表单的数据列表, 为用户提供数据输入.
 get_form_class(): 由ModelFormMixin定义, 重写FormMixin的方法, 根据属性fields和form_class的组合情况进行判断, 从而选择表单的生成方式.
 get_form_kwargs(): 由ModelFormMixin定义, 重写FormMixin的方法.
 get_success_url(): 由ModelFormMixin定义, 重写FormMixin的方法,判断属性success_url是否为空, 若为空, 则从模型的内置方法get_absolute_url()获取重定向的路由地址.
 form_valid(): 由ModelFormMixin定义, 重写FormMixin的表单验证方法, 新增表单数据保存到数据库的功能.
 template_name_suffix: 由CreateView定义, 设置模板的后缀名, 用于设置默认的模板文件.视图类CreateView虽然具备TemplateView, SingleObjectMixin和FormView的所有属性和方法,
但经过重写某些方法后, 导致其运行过程已发生变化, 其中最大的特点在于get_form_class()方法, 
它是通过判断属性form_class(来自FormMixin类), 
fields(来自ModelFormMixin类)和model(来自SingleObjectMixin类), 从而实现数据新增操作.
其判断方法如下:
 若fields和form_class都不等于None, 则抛出异常, 提示不允许同时设置fields和form_class.
 若form_class不等于None且fields等于None, 则返回form_class.
 若form_class等于None, 则从属性model, object和get_queryset()获取模型对象.同时判断属性fields是否为None, 若为None, 则抛出异常;若不为None, 则根据属性fields生成表单对象, 由表单内置的modelform_factory()方法实现.
综上所述, 视图类CreateView有两种表单生成方式.
第一种是设置属性form_class, 通过属性form_class指定表单对象, 
这种方式需要开发者自定义表单对象, 假如自定义的表单和模型的字段不相符, 在运行过程中很容易出现异常情况.
第二种是设置属性model和fields, 由模型对象和模型字段来生成相应的表单对象, 
生成的表单字段与模型的字段要求相符, 可以减少异常情况, 并且无须开发者自定义表单对象.沿用5.2.1小节的MyDjango项目, index的form.py, urls.py和模板文件index.html中的代码无须修改,
只需修改index的views.py, 代码如下:
from django.views.generic.edit import CreateView
from .form import PersonInfoForm
from .models import PersonInfo
from django.http import HttpResponsedef result(request):return HttpResponse('Success')class Index(CreateView):# 设置表单初始化的数据initial = {'name': 'Betty', 'age': 20}# 使用的模板文件template_name = 'index.html'# 重定向地址, 提交表单后跳转的地址success_url = '/result'# 表单生成方式一# form_class = PersonInfoForm# 表单生成方式二model = PersonInfo# fields设置模型字段, 从而生成表单字段fields = ['name', 'age']extra_context = {'title': '人员信息表'}

image-20240414003655371

视图类index只需设置某些类属性即可完成模型数据的新增功能, 整个数据新增过程都由视图类CreateView完成.
视图类index还列举了两种表单生成方式, 默认使用属性model和fields生成表单, 
读者可将属性form_class的注释清除, 并对属性model和fields进行注释处理, 即可使用第一种表单生成方式.
运行MyDjango项目, 在浏览器上访问: 127.0.0.1:8000, 发现表单上设有数据, 这是由视图类index的属性initial设置的.
单击'确定'按钮, 浏览器就会自动跳转到路由result, 说明表单验证成功.
在Navicat Premium里打开MyDjango的db.sqlite3数据库文件, 查看数据表index_personinfo的数据新增情况, 如图5-17所示.

image-20240414003824358

5-17 数据表index_personinfo

5.2.3 修改视图UpdateView

视图类UpdateView是在视图类FormView和视图类DetailView的基础上实现的, 
它首先使用视图类DetailView的功能(功能核心类是SingleObjectMixin), 
通过路由变量查询数据表某条数据并显示在网页上, 然后在视图类FormView的基础上, 通过表单方式实现数据修改.视图类UpdateView与FormView是在同一个源码文件里定义的, 
在源码文件里分析视图类UpdateView的定义过程, 以流程图的形式表示类的继承关系, 如图5-18所示.

image-20240414153016156

5-18 视图类UpdateView的继承关系
从上述的继承关系发现, 视图类UpdateView与CreateView的继承关系十分相似, 
只不过两者的运行过程有所不同, 从而导致功能上有所差异.
视图类UpdateView的属性和方法主要来自视图类DetailView, ModelFormMixin和FormMixin, 
这些属性和方法分别在5.1.4小节, 5.2.1小节和5.2.2小节介绍过了.
以MyDjango为例, 数据库文件db.sqlite3沿用5.2.2小节的数据, 
在index的urls.py, views.py和模板文件index.html中分别编写以下代码:
# index 的 urls.py
from django.urls import path
from .views import *urlpatterns = [# 定义路由path('<age>.html', Index.as_view(), name='index'),path('result', result, name='result'),
]

image-20240414040209978

# index 的 views.py
from django.views.generic.edit import UpdateView
from .models import PersonInfo
from django.http import HttpResponsedef result(request):return HttpResponse('Success')class Index(UpdateView):template_name = 'index.html'success_url = '/result'model = PersonInfo# fields设置模型字段, 从而生成表单字段fields = ['name', 'age']slug_url_kwarg = 'age' slug_field = 'age'extra_context = {'title': '人员信息表'}

image-20240414040314321

<!-- templates 的 index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{{ title }}</title>
<body>
<h3>{{ title }}-{{ personinfo.name }}</h3>
<form method="post">{% csrf_token %}{{ form.as_p }}<input type="submit" value="确定">
</form>
</body>
</html>

image-20240414040415947

视图类index一共设置了8个属性, 这些属性主要来自类TemplateResponseMixin, 
SingleObjectMixin, FormMixin和ModelFormMixin, 这是实现视图类UpdateView的核心功能类.
路由index的变量age对应视图类index的属性slug_url_kwarg, 用于对模型字段age进行数据筛选.
筛选结果将会生成表单form和personinfo对象, 表单form是由类FormMixin的get_context_data()生成的,
personinfo对象则由类SingleObjectMixin的get_context_data()生成, 两者的数据都是来自模型PersonInfo.
运行MyDjango项目, 在浏览器上访问: 127.0.0.1:8000/13.html, 
其中路由地址的13代表数据表index_personinfo的字段age等于13的数据, 如图5-19所示.
在网页上将表单name改为Tim并单击'确定'按钮, 在数据表index_personinfo中查看字段age等于13的数据变化情况, 如图5-19所示.
( slug_url_kwarg设置检索的字段值必须是唯一, 否则会报错, 不要修改age!!! )

image-20240414041009188

5-19 数据表index_personinfo 

5.2.4 删除视图DeleteView

视图类DeleteView的使用方法与视图类UpdateView有相似之处, 但两者的父类继承关系有所差异.
在源码文件里分析视图类DeleteView的定义过程, 以流程图的形式表示类的继承关系, 如图5-20所示.

image-20240414154035268

5-20 视图类DeleteView的继承关系
视图类DeleteView只能删除单条数据, 路由变量为模型主键提供查询范围, 
因为模型主键具有唯一性, 所以通过主键查询能精准到某条数据.
查询出来的数据通过POST请求实现数据删除, 删除过程由类DeletionMixin的delete()方法完成.
视图类DeleteView的属性与方法主要来自类SingleObjectMixin, 
DeletionMixin和TemplateResponseMixin, 每个属性与方法的作用不再重复讲述.
以MyDjango为例, 数据库文件db.sqlite3沿用5.2.3小节的数据, 
在index的urls.py, views.py和模板文件index.html中分别编写以下代码:
from django.urls import path
from .views import *urlpatterns = [# 定义路由path('<pk>.html', Index.as_view(), name='index'),path('result', result, name='result'),
]

image-20240414160117645

from django.views.generic.edit import DeleteView
from .models import PersonInfo
from django.http import HttpResponsedef result(request):return HttpResponse('Success')class Index(DeleteView):template_name = 'index.html'success_url = '/result'model = PersonInfocontext_object_name = 'personinfo'  # 默认就是小写的默认名称, 可以省略extra_context = {'title': '人员信息表'}

image-20240414160318753

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{{ title }}</title>
<body>
<h3>{{ title }}-{{ personinfo.name }}</h3>
<form method="post">{% csrf_token %}{{ form.as_p }}   {# 此次视图不起作用可以省略 #}<div>删除{{ personinfo.name }}?</div><input type="submit" value="确定">
</form>
</body>
</html>

image-20240414160507678

路由index设置变量pk, 对应视图类index的属性pk_url_kwarg, 该属性的默认值为pk, 默认值的设定可以在类SingleObjectMixin中找到.
视图类index会根据路由变量pk的值在数据表index_personinfo里找到相应的数据信息, 查找过程由类BaseDetailView完成.
模板上下文不再生成表单对象form, 只有personinfo对象, 该对象由类SingleObjectMixin的get_context_data()生成.
运行MyDjango项目, 在浏览器上访问: 127.0.0.1:8000/1.html, 
路由地址的1代表数据表index_personinfo的主键id等于1的数据, 如图5-17所示.
在网页上单击'确定'按钮即可删除该数据, 在数据表index_personinfo中查看主键id等于1的数据是否存在, 如图5-21所示.

image-20240414155749989

5-21 数据表index_personinfo

5.3 日期筛选视图

日期筛选视图是根据模型里的某个日期字段进行数据筛选的, 然后将符合结果的数据以一定的形式显示在网页上.
简单来说, 在列表视图ListView或详细视图DetailView的基础上增加日期筛选所实现的视图类.
它一共定义了7个日期视图类, 说明如下:
 ArchiveIndexView是将数据表所有的数据以某个日期字段的降序方式进行排序显示的.该类的继承关系如图5-22所示.

image-20240414173452571

5-22 视图类ArchiveIndexView的继承关系
 YearArchiveView是在数据表筛选某个日期字段某年的所有的数据, 默认以升序的方式排序显示, 年份的筛选范围由路由变量提供.该类的继承关系如图5-23所示.

image-20240414173421738

5-23 视图类YearArchiveView的继承关系	
 MonthArchiveView是在数据表筛选某个日期字段某年某月的所有的数据, 默认以升序的方式排序显示,年份和月份的筛选范围都由路由变量提供. 该类的继承关系如图5-24所示.

image-20240414173805963

5-24 视图类MonthArchiveView的继承过程
 WeekArchiveView是在数据表筛选某个日期字段某年某周的所有的数据,总周数是将一年的总天数除以7所得的, 数据默认以升序的方式排序显示, 年份和周数的筛选范围都是由路由变量提供的.该类的继承关系如图5-25所示.

image-20240414173940139

5-25 视图类WeekArchiveView的继承关系
 DayArchiveView是对数据表的某个日期字段精准筛选到某年某月某天,将符合条件的数据以升序的方式排序显示, 年份, 月份和天数都是由路由变量提供的.该类的继承关系如图5-26所示.

image-20240414174052877

5-26 视图类DayArchiveView的继承关系
 TodayArchiveView是在视图类DayArchiveView的基础上进行封装处理的,它将数据表某个日期字段的筛选条件设为当天时间, 符合条件的数据以升序的方式排序显示.该类的继承关系如图5-27所示.

image-20240414175334179

5-27 视图类TodayArchiveView的继承关系
 DateDetailView是查询某年某月某日某条数据的详细信息,它在视图类DetailView的基础上增加了日期筛选功能, 筛选条件主要有年份, 月份, 天数和某个模型字段,其中某个模型字段必须具有唯一性, 才能确保查询的数据具有唯一性.该类的继承关系如图5-28所示.

image-20240414181203376

从日期筛选视图类的继承关系得知, 它们的继承关系都有一定的相似之处, 说明它们的属性和方法在使用上不会存在太大的差异.
因此, 我们选择最有代表性的视图类MonthArchiveView和WeekArchiveView进行讲述,
在日常开发中, 这两个日期视图类通常用于开发报表功能(月报表和周报表).

5.3.1 月份视图MonthArchiveView

本节来了解视图类MonthArchiveView的继承过程,
类MultipleObjectMixin的属性和方法在5.1.3小节已经详细讲述过了, 本小节不再重复列举.
此外, 视图类MonthArchiveView新增的属性和方法说明如下:
 template_name_suffix: 由MonthArchiveView定义, 设置模板后缀名, 用于设置默认模板文件.
 date_list_period: 由BaseDateListView定义, 经BaseMonthArchiveView重写, 设置日期列表的最小单位, 默认值为day.
 get_dated_items(): 由BaseDateListView定义, 经BaseMonthArchiveView重写, 根据年份和月份在数据表查询符合条件的数据.
 year_format: 由YearMixin定义, 设置年份的数据格式, 即路由变量的数据格式, 默认值为%Y, 代表数字年份, 2019.
 year: 由YearMixin定义, 设置默认查询的年份, 如果没有设置属性值, 就从路由变量year里获取, 默认值为None.
 get_year_format(): 由YearMixin定义, 获取属性year_format的属性值.
 get_year(): 由YearMixin定义, 获取属性year的属性值.
 get_next_year(): 由YearMixin定义, 获取下一年的年份.
 get_previous_year(): 由YearMixin定义, 获取上一年的年份.
 _get_next_year(): 由YearMixin定义的受保护方法, 获取下一年的年份.
 _get_current_year: 由YearMixin定义的受保护方法, 获取当前的年份.
 month_format: 由MonthMixin定义, 设置月份的数据格式, 即路由变量的数据格式, 默认值为%b, 代表月份英文前3个字母, 如Sep.
 month: 由MonthMixin定义, 设置查询的月份, 默认值为None.
 get_month_format(): 由MonthMixin定义, 获取属性month_format的属性值.
 get_month(): 由MonthMixin定义, 获取属性month的属性值.
 get_next_month(): 由MonthMixin定义, 获取下个月的月份.
 get_previous_month(): 由MonthMixin定义, 获取上个月的月份.
 _get_next_month(): 由MonthMixin定义的受保护方法, 获取下个月的月份.
 _get_current_month(): 由MonthMixin定义的受保护方法, 获取当前的月份.
 allow_empty: 由BaseDateListView定义, 数据类型为布尔型, 在模型中查询数据不存在的情况下是否显示页面, 若为False并且数据不存在, 则引发404异常, 默认值为False.
 get(): 由BaseDateListView定义, 定义HTTP请求的GET请求处理.
 get_ordering(): 由BaseDateListView定义, 确定排序方式, 默认值是以日期字段排序, 若设置类MultipleObjectMixin的属性ordering, 则以属性ordering进行排序.
 get_dated_queryset(): 由BaseDateListView定义, 根据属性allow_future和allow_empty设置日期条件.
 get_date_list_period(): 由BaseDateListView定义, 获取date_list_period的属性值.
 get_date_list(): 由BaseDateListView定义, 根据日期条件在数据表里查找相符的数据列表.
 date_field: 由DateMixin定义, 默认值为None, 设置模型的日期字段, 通过该字段对数据表进行查询筛选.
 allow_future: 由DateMixin定义, 默认值为None, 设置是否显示未来日期的数据, 如产品有效期.
 get_date_field(): 由DateMixin定义, 获取属性date_field的属性值.
 get_allow_future(): 由DateMixin定义, 获取属性allow_future的属性值.
 uses_datetime_field(): 由DateMixin定义, 判断字段是否为DateTimeField格式, 并根据判断结果返回True或False.
 _make_date_lookup_arg(): 由DateMixin定义的受保护方法, 根据uses_datetime_field()判断结果是否执行日期格式转换.
 _make_single_date_lookup(): 由DateMixin定义的受保护方法, 根据uses_datetime_field()判断结果设置日期的查询条件.
以上从源码角度分析了视图类MonthArchiveView的属性和方法, 
下一步从项目开发的角度来讲述如何使用视图类MonthArchiveView实现数据筛选功能.
以MyDjango为例, 首先将index的models.py重新定义, 在模型PersonInfo里增设日期字段hireDate, 代码如下:
# index 的 models.py
from django.db import modelsclass PersonInfo(models.Model):id = models.AutoField(primary_key=True)  # id, 整形, 自增, 主键name = models.CharField(max_length=20)  # 名字, 字符串, 宽度为20age = models.IntegerField()  # 姓名, 整形hireDate = models.DateField()  # 雇佣日期, 日期类型

image-20240414190306618

模型PersonInfo定义完成后, 将index的migrations文件夹的0001_initial.py删除, 
同时使用Navicat Premium打开数据库文件db.sqlite3, 将数据库所有的数据表删除,
接着在PyCharm的Terminal选项卡里依次输入数据迁移指令.

image-20240414190344680

image-20240414190456307

# 根据models.py生成相关的.py文件, 该文件用于创建数据表
D:\MyDjango>python manage.py makemigrations
Migrations for 'index':index\migrations\0001_initial.py- Create model personinfo
# 创建数据表
D:\MyDjango>python manage.py migrate
Operations to perform:Apply all migrations: admin, auth, contenttypes, index, sessions
Running migrations:Applying contenttypes.0001_initial... OKApplying auth.0001_initial... OKApplying admin.0001_initial... OKApplying admin.0002_logentry_remove_auto_add... OKApplying admin.0003_logentry_add_action_flag_choices... OKApplying contenttypes.0002_remove_content_type_name... OKApplying auth.0002_alter_permission_name_max_length... OKApplying auth.0003_alter_user_email_max_length... OKApplying auth.0004_alter_user_username_opts... OKApplying auth.0005_alter_user_last_login_null... OKApplying auth.0006_require_contenttypes_0002... OKApplying auth.0007_alter_validators_add_error_messages... OKApplying auth.0008_alter_user_username_max_length... OKApplying auth.0009_alter_user_last_name_max_length... OKApplying auth.0010_alter_group_name_max_length... OKApplying auth.0011_update_proxy_permissions... OKApplying auth.0012_alter_user_first_name_max_length... OKApplying index.0001_initial... OKApplying sessions.0001_initial... OK
当指令执行完成后, 再次使用Navicat Premium软件打开db.sqlite3文件, 
在数据库中可以看到新创建的数据表, 在数据表index_personinfo中添加数据内容, 如图5-29所示.

image-20240414191203873

5-29 数据表index_personinfo的数据信息
完成项目环境搭建后, 接下来使用视图类MonthArchiveView实现数据筛选功能.
在index的urls.py, views.py和模板文件index.html中分别编写以下代码:
# index 的 urls.py
from django.urls import path
from .views import *urlpatterns = [# 定义路由path('<int:year>/<int:month>.html', Index.as_view(), name='index'),# path('<int:year>/<str:month>.html', Index.as_view(), name='index'),
]

image-20240414205429235

# index 的 urls.py
from django.views.generic.dates import MonthArchiveView
from .models import PersonInfoclass Index(MonthArchiveView):allow_empty = Trueallow_future = Truecontext_object_name = 'mylist'template_name = 'index.html'model = PersonInfodate_field = 'hireDate'queryset = PersonInfo.objects.all()year_format = '%Y'# month_format默认格式是支持英文日期, 如Oct, Sepmonth_format = '%m'paginate_by = 50

image-20240414205457630

<!-- templates 的 index.html -->
<!DOCTYPE html>
<html>
<head><title>{{ title }}</title>
<body>
<ul>{% for v in mylist %}<li>{{ v.hireDate }}: {{ v.name }}</li>{% endfor %}
</ul>
<p>{% if previous_month %}Previous Month: {{ previous_month }}{% endif %}<br>{% if next_month %}Next Month: {{ next_month }}{% endif %}
</p>
</body>
</html>	

image-20240414205546699

路由index在路由地址里设置变量year和month, 而且变量的数据类型都是整型,
其中路由变量month可以设为字符型, 不同的数据类型会影响视图类MonthArchiveView的属性month_format的值.
视图类index继承父类MonthArchiveView, 它共设置了10个属性, 每个属性已经详细讲述过了, 在此不再重复说明.
视图类index是对模型PersonInfo进行数据查找, 查找方式是以字段hireDate的日期内容进行数据筛选,
筛选条件来自路由变量year和month, 由于变量month的数据类型是整型, 因此将属性month_format的默认值%b改为%m,
否则Django会提示404异常, 如图5-30所示.

image-20240414210738000

5-30 异常信息
模板文件index.html使用了模板上下文mylist, previous_month和next_month,
此外, 视图类MonthArchiveView还设有其他模板上下文, 具体说明如下:
 mylist: 这是由模型PersonInfo查询所得的数据对象, 它的命名是由视图类index的属性context_object_name设置的, 如果没设置该属性, 模板上下文就默认为object_list或者page_obj.
 previous_month: 根据路由变量year和month的日期计算出上一个月的日期.
 next_month: 根据路由变量year和month的日期计算出下一个月的日期.
 date_list: 从查询所得的数据里获取日期字段的日期内容.
 paginator: 由类MultipleObjectMixin生成, 这是Django内置的分页功能对象.
我们运行MyDjango项目, 在浏览器上访问: 127.0.0.1:8000/2018/9.html, 
Django对数据表index_personinfo的字段hireDate进行筛选, 筛选条件为201809月份, 符合条件的所有数据显示在网页上, 如图5-31所示.

image-20240414205521968

5-31 运行结果
从图5-31看到, 网页上显示的日期是以月, , 年的格式显示的, 并且月份是以英文表示的.
如果想让日期格式与数据库的日期格式相同, 那么可以使用模板语法的过滤器date来转换日期格式.
从路由index的变量month得知, 该变量的数据类型可设为字符型, 如果该变量改为字符型, 那么视图类index无须设置属性month_format.
假设将路由变量month改为字符型并注释视图类index的属性month_format, 
重启运行MyDjango项目, 在浏览器上访问: 127.0.0.1:8000/2018/sep.html , 网页显示的内容如下.

image-20240414212004160

若想验证属性allow_empty和allow_future的作用, 则可单独设置allow_empty的值, 第一次设为True, 第二次设为False,
并且每次都访问: 127.0.0.1:8000/2017/10.html , 然后对比两次访问结果的差异即可.
( allow_empty: 在模型中查询数据不存在的情况下是否显示页面, 若为False并且数据不存在, 则引发404异常, 默认值为False.)

2024-04-14_213548

( 模板文件中 {% if previous_month %}.)  {% if next_month %} 控制上下月份的渲染. 数据库中最早的时间是2018/7, 访问这个地址的时候, 不会显示上个月(没有数据不允许访问), 数据库中最晚的时间是2019/10, 访问这个地址的时候, 不会显示下个月(没有数据不允许访问).)

2024-04-14_230716

同理, 属性allow_future的验证方式相同, 但其访问的路由地址改为: 127.0.0.1:8000/2019/10.html (使用当前时间+一个月测试).
( allow_future: 这个属性决定用户是否可以通过 URL 访问未来的日期, 默认为False, 则用户不能访问未来的日期.)

2024-04-14_232755

综上所述, 视图类MonthArchiveView是在列表视图ListView的基础上设置日期筛选功能的视图类,
日期筛选对象来自模型里的某个日期字段, 筛选条件是由路由变量year和month提供的,
其中路由变量month的数据类型可选择为整型或字符型, 不同的数据类型需要为month_format设置相应的属性值.

5.3.2 周期视图WeekArchiveView

在一年中, 无论是平年还是闰年, 一共有52, 而且每年同一个周数的日期是各不相同的.
如果要对数据表的数据生成周报表, 就需要根据当前年份的周数来计算相应的日期范围, 这样可以大大降低开发效率.
为此, Django提供了视图类WeekArchiveView, 只需提供年份和周数即可在数据表里筛选相应的数据信息.视图类WeekArchiveView的继承过程在图5-25中已描述过了, 整个设计共继承10个类.
除了类WeekMixin之外, 其他类的属性和方法已详细介绍过了, 本小节不再重复讲述, 我们只列举类WeekMixin定义的属性和方法, 说明如下:
 week_format: 由WeekMixin定义, 默认值为%U, 这是设置周数的计算方式,可选值为%W或%U, 如果值为%W, 周数就从星期一开始计算, 如果值为%U, 周数就从星期天开始计算.
 week: 由WeekMixin定义, 设置默认周数, 如果没有设置属性值, 就从路由变量week里获取.
 get_week_format(): 由WeekMixin定义, 获取属性week_format的值.
 get_week(): 由WeekMixin定义, 获取属性week的值.
 get_next_week(): 由WeekMixin定义, 调用_get_next_week()来获取下一周的开始日期.
 get_previous_week(): 由WeekMixin定义, 获取上一周的开始日期.
 _get_next_week(): 由WeekMixin定义的受保护方法, 返回下一周的开始日期.
 _get_current_week(): 由WeekMixin定义的受保护方法, 返回属性week所设周数的开始日期.
 _get_weekday(): 由WeekMixin定义的受保护方法, 获取属性week所设周数的工作日.
通过分析视图类WeekArchiveView的源码, 我们对视图类WeekArchiveView有了大致的了解.
下一步通过实例来讲述如何使用视图类WeekArchiveView, 它的使用方式与视图类MonthArchiveView非常相似.
沿用5.3.1小节的MyDjango项目, 在index的urls.py, views.py和模板文件index.html中分别编写以下代码:
# index 的 urls.py
from django.urls import path
from .views import *urlpatterns = [# 定义路由path('<int:year>/<int:week>.html', Index.as_view(), name='index'),
]

image-20240414235119493

# index 的 views.py
from django.views.generic.dates import WeekArchiveView
from .models import PersonInfoclass Index(WeekArchiveView):allow_empty = Trueallow_future = Truecontext_object_name = 'mylist'template_name = 'index.html'model = PersonInfodate_field = 'hireDate'queryset = PersonInfo.objects.all()year_format = '%Y'week_format = '%W'paginate_by = 50

image-20240414235145503

<!-- templates 的 index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{{ title }}</title>
<body>
<ul>{#  设置了context_object_name还可以使用默认名称, 那么context_object_name就相当于别名  #}{% for v in object_list %}<li>{{ v.hireDate }}: {{ v.name }}</li>{% endfor %}
</ul>
<p>{% if previous_week %}Previous Week: {{ previous_week }}{% endif %}<br>{% if next_week %}Next Week: {{ next_week }}{% endif %}
</p>
</body>
</html>

image-20240414235503288

路由index定义路由变量year和week, 它们只能支持整型的数据格式, 并且变量名是固定的,
否则视图类WeekArchiveView无法从路由变量里获取年份和周数.视图类index继承父类WeekArchiveView, 它共设置了10个属性,
每个属性的设置与5.3.1小节的视图类index大致相同, 唯独将属性month_format改为week_format.模板文件index.html的模板上下文也与视图类MonthArchiveView提供的模板上下文相似,
只不过上一周和下一周的上下文改为previous_week和next_week.综上所述, 视图类WeekArchiveView和MonthArchiveView在使用上存在相似之处,
也就是说, 只要熟练掌握某个日期视图类的使用方法, 其他日期视图类的使用也能轻易地掌握.
数据表index_personinfo的大部分数据集中在20189, 这个日期的周数为38.
运行MyDjango项目, 在浏览器上访问: 127.0.0.1:8000/2018/38.html ,
Django将日期从2018-09-182018-09-22的数据显示在网页上, 如图5-32所示.

image-20240414235634364

5-32 运行结果

5.4 本章小结

Web开发是一项无聊而且单调的工作, 特别是在视图编写功能方面更为显著.
为了减少这类痛苦, Django植入了视图类这一功能, 该功能封装了视图开发常用的代码和模式, 
可以在无须编写大量代码的情况下, 快速完成数据视图的开发, 这种以类的形式实现响应与请求处理称为CBV.视图类是通过定义和声明类的形式实现的, 根据用途划分3部分: 数据显示视图, 数据操作视图和日期筛选视图.
数据显示视图是将后台的数据展示网页上, 数据主要来自模型, 一共定义了4个视图类,
分别是RedirectView, TemplateView, ListView和DetailView, 说明如下:
 RedirectView用于实现HTTP重定向, 默认情况下只定义GET请求的处理方法.
 TemplateView是视图类的基础视图, 可将数据传递给HTML模板, 默认情况下只定义GET请求的处理方法.
 ListView是在TemplateView的基础上将数据以列表显示, 通常将某个数据表的数据以列表表示.
 DetailView是在TemplateView的基础上将数据详细显示, 通常获取数据表的单条数据.数据操作视图是对模型进行操作, 如增, , , 从而实现Django与数据库的数据交互.
数据操作视图有4个视图类, 分别是FormView, CreateView, UpdateView和DeleteView, 说明如下:
 FormView视图类使用内置的表单功能, 通过表单实现数据验证, 响应输出等功能, 用于显示表单数据.
 CreateView实现模型的数据新增功能, 通过内置的表单功能实现数据新增.
 UpdateView实现模型的数据修改功能, 通过内置的表单功能实现数据修改.
 DeleteView实现模型的数据删除功能, 通过内置的表单功能实现数据删除.日期筛选视图是根据模型里的某个日期字段进行数据筛选, 然后将符合结果的数据以一定的形式显示在网页上.
简单来说, 就是在列表视图ListView或详细视图DetailView的基础上增加日期筛选所实现的视图类.
它一共定义了7个日期视图类, 其说明如下:
 ArchiveIndexView是将数据表所有的数据以某个日期字段的降序方式进行排序显示.
 YearArchiveView是在数据表筛选某个日期字段某年的所有数据, 并默认以升序的方式排序显示, 年份的筛选范围由路由变量提供.
 MonthArchiveView是在数据表筛选某个日期字段某年某月的所有数据, 并默认以升序的方式排序显示,年份和月份的筛选范围都是由路由变量提供的.
 WeekArchiveView是在数据表筛选某个日期字段某年某周的所有数据, 总周数是将一年的总天数除以7所得的, 数据默认以升序的方式排序显示, 年份和周数的筛选范围都是由路由变量提供的.
 DayArchiveView是对数据表的某个日期字段精准筛选到某年某月某天,将符合条件的数据以升序的方式排序显示, 年份, 月份和天数都是由路由变量提供的.
 TodayArchiveView是在视图类DayArchiveView的基础上进行封装处理,它将数据表某个日期字段的筛选条件设为当天时间, 符合条件的数据以升序的方式排序显示.
 DateDetailView用于查询某年某月某日某条数据的详细信息, 它在视图类DetailView的基础上增加日期筛选功能, 筛选条件主要有年份, 月份, 天数和某个模型字段,其中某个模型字段必须具有唯一性, 才能确保查询的数据具有唯一性.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/769.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用阿里云试用Elasticsearch学习:sentence-transformers 包使用

环境&#xff1a;centos8&#xff0c;windows坑太多。 一、检查linux环境openssl哪个版本&#xff08;如果是OpenSSL 1.1.1k 直接跳过&#xff09; [roothecs-334217 python39]# openssl version OpenSSL 1.0.2k-fips 26 Jan 2017原因后续会出麻烦&#xff0c;遇到这种情况最…

16.C++常用的算法_算数生成算法

文章目录 遍历算法1. accumulate()代码工程运行结果 2. fill()代码工程运行结果 遍历算法 1. accumulate() 代码工程 第三个参数为累加的起始值&#xff0c;如果没有特殊需求写0即可; 需要注意包含头文件#include<numeric>#define _CRT_SECURE_NO_WARNINGS #include&l…

【C语言】Dijkstra算法详解

一、引言二、Dijkstra算法原理三、Dijkstra算法的C语言实现四、Dijkstra算法的应用场景五、总结 一、引言 Dijkstra算法是一种著名的图论算法&#xff0c;用于解决单源最短路径问题。它是由荷兰计算机科学家Edsger W. Dijkstra在1956年提出的。本文将详细介绍Dijkstra算法的原理…

代码随想录:二叉树11-12

目录 222.完全二叉树的节点个数 题目 代码&#xff08;层序迭代&#xff09; 代码&#xff08;后序递归&#xff09; 代码&#xff08;满二次树递归&#xff09; 总结 110.平衡二叉树 题目 代码&#xff08;后序递归&#xff09; 代码&#xff08;层序迭代&#xff0…

短视频批量采集提取软件|视频关键词下载工具

短视频批量采集软件&#xff1a;快速抓取、高效下载 一、开发背景 随着短视频平台的兴起&#xff0c;获取并分析相关视频内容已成为许多业务的必要步骤。然而&#xff0c;传统的手动方式无法满足快速、批量获取的需求&#xff0c;因此我们开发了一款专业的短视频批量采集软件。…

Linux内核之读、写信号量:up_read、up_write用法实例(五十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

ASP.NET基于WEB的工作计划流程管理系统的设计与实现

摘 要 信息技术的飞速发展&#xff0c;尤其是网络通讯技术、数据库技术及自动化技术的日新月异&#xff0c;为单位、企业的办公带来了极大的便利。但是由于单位、企业的工作性质众多&#xff0c;工作流程各有差异&#xff0c;企业、单位、部门之间的管理机制各不相同&#xf…

用队列实现栈(力扣第225题)

#include "stdio.h" #include "stdbool.h" #include "string.h" #include "stdlib.h" #include "assert.h"//初始化队列 typedef int QueueDataType;typedef struct queue {QueueDataType val;struct queue* next; }Qnode;t…

微信小程序地图polyline坐标太多异常显示BUG

描述 微信小程序map地图上显示polyline线&#xff0c;点位超过1250个出现bug&#xff0c;&#xff08;仅真机上出现&#xff0c;模拟器上正常&#xff09; 这里以加载四川省边界为例, 以下是示例代码 // 读取geojson数据 uni.request({url: https://geo.datav.aliyun.com/a…

openplc Linux 使用modbus RTU 从机通讯

1.Linux 环境下&#xff0c;openplc 默认使用的是modbus tcp协议通信。 想要使用串口 modbus rtu 通讯可以通过在runtime中添加SlaveDevices从机设备 2.添加设备&#xff0c;分配地址。 左边添加串口配置&#xff0c;右边是需要通讯的地址&#xff0c;从机地址都是从100开始&am…

yolov8 区域计数

yolov8 区域计数 1. 基础2. 计数功能2.1 计数模块2.2 判断模块 3. 主代码4. 实验结果5. 源码 1. 基础 本项目是在 WindowsYOLOV8环境配置 的基础上实现的&#xff0c;测距原理可见上边文章 2. 计数功能 2.1 计数模块 在指定区域内计数模块 def count_objects_in_region(bo…

STM32 堆栈内存以及变量存储分布

STM32的程序存储器、数据存储器、寄存器和输入输出端口被组织在同一个4GB的线性地址空间内, 地址范围为0x0000 0000至0xFFFF FFFF。其中FLASH为ROM类型&#xff0c;储存的数据掉电不易失&#xff1b;RAM中存储的数据掉电易失。以STM32F103系列为例&#xff0c;最多有512KB的FLA…

Discuz! X3.4 升级至 Discuz! X3.5 详细教程

第一步&#xff1a;从其他以前的 Discuz! X 版本升级Discuz! X3.4 请先升级到Discuz! X3.4&#xff0c;升级教程网上比较普遍&#xff0c;在此不再论述。 第二步&#xff1a;Discuz! X3.4 升级至 Discuz! X3.5 &#xff08;Discuz 从 X3.5 以后&#xff0c;不在发布GBK版本&…

【软考】UML中的图之类图

目录 1. 说明2. 图示3. 类图使用方式3.1 对系统的词汇建模3.2 对简单的协作建模3.3 对逻辑数据库模式建模 1. 说明 1.类图&#xff08;Class Diagram&#xff09;展现了一组对象、接口、协作和它们之间的关系。2.在面向对象系统的建模中所建立的最常见的图是类图。3.类图给出系…

离线数仓数据导出-hive数据同步到mysql

离线数仓数据导出-hive数据同步到mysql MySQL建库建表数据导出 为方便报表应用使用数据&#xff0c;需将ads各指标的统计结果导出到MySQL数据库中。 datax支持hive同步MySQL&#xff1a;仅仅支持hive存储的hdfs文件导出。所以reader选hdfs-reader&#xff0c;writer选mysql-wri…

怎样在外网登录访问CRM管理系统?

一、什么是CRM管理系统&#xff1f; Customer Relationship Management&#xff0c;简称CRM&#xff0c;指客户关系管理&#xff0c;是企业利用信息互联网技术&#xff0c;协调企业、顾客和服务上的交互&#xff0c;提升管理服务。为了企业信息安全以及使用方便&#xff0c;企业…

SSM小程序作品集展示微信小程序

采用技术 SSM小程序作品集展示微信小程序的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringMVCMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 用户功能 用户注册 用户首页 作品集 优秀作者 我的分享 管…

powershell@命令行提示符样式配置自定义@pwsh重写prompt显示电量内存时间等信息

文章目录 abstract流行的powershell prompt模块示例 powershell原生修改Prompt函数配置文档Prompt命令来自哪里 简单修改带上电量和时间的Prompt 复杂修改预览FAQ:没有必要修改相关仓库地址样式选择平衡样式花哨样式响应性能 小结 abstract 在 PowerShell 中&#xff0c;可以通…

CSDN积分和等级和 能创建专栏数量的关系。还差1000多分!

积分查询&#xff1a;CSDN 博客积分规则 博客积分是CSDN对用户努力的认可和奖励&#xff0c;也是衡量博客水平的重要标准。博客等级也将由博客积分唯一决定。积分规则具体如下&#xff1a; 1、每发布一篇原创或者翻译文章&#xff1a;可获得10分&#xff1b; 2、每发布一篇转载…

做一个答题pk小程序多少钱

在探讨“做一个答题pk小程序多少钱”这一问题时&#xff0c;我们首先需要明确的是&#xff0c;小程序的价格并非固定不变&#xff0c;而是受到多种因素的影响。这些因素包括但不限于小程序的复杂度、功能需求、开发周期、技术难度以及开发团队的规模和经验等。因此&#xff0c;…