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, include

urlpatterns = [
    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 RedirectView

def index(request):
    return render(request, 'index.html')

class TurnTo(RedirectView):
    # 设置属性
    permanent = False
    url = None
    pattern_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 TemplateView


class Index(TemplateView):
    template_name = 'index.html'
    template_engine = None
    content_type = None
    extra_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 models


class PersonInfo(models.Model):  
    id = models.AutoField(primary_key=True)  # id, 整形, 自增, 主键
    name = models.CharField(max_length=20)  # 名字, 字符串, 宽度为20
    age = 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 PersonInfo


class 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 PersonInfo


class Index(DetailView):
    # 设置模板文件
    template_name = 'index.html'
    # 设置模型外的数据
    extra_context = {'title': '人员信息表'}
    # 设置模型的查询字段
    slug_field = 'age'
    
    # 设置路由的变量名, 与属性slug_field实现模型的查询操作
    slug_url_kwarg = 'age'  # s
    pk_url_kwarg = 'pk'  # 默认就是主键, 写了等于白写

    # 设置查询模型PersonInfo
    model = 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 PersonInfo


class PersonInfoForm(forms.ModelForm):
    class Meta:
        model = PersonInfo
        fields = '__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 HttpResponse


def 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 HttpResponse


def 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 HttpResponse


def 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 HttpResponse


def result(request):
    return HttpResponse('Success')


class Index(DeleteView):
    template_name = 'index.html'
    success_url = '/result'
    model = PersonInfo
    context_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 models


class PersonInfo(models.Model):
    id = models.AutoField(primary_key=True)  # id, 整形, 自增, 主键
    name = models.CharField(max_length=20)  # 名字, 字符串, 宽度为20
    age = 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... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying index.0001_initial... OK
  Applying 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 PersonInfo


class Index(MonthArchiveView):
    allow_empty = True
    allow_future = True
    context_object_name = 'mylist'
    template_name = 'index.html'
    model = PersonInfo
    date_field = 'hireDate'
    queryset = PersonInfo.objects.all()
    year_format = '%Y'
    # month_format默认格式是支持英文日期, 如Oct, Sep
    month_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 PersonInfo


class Index(WeekArchiveView):
    allow_empty = True
    allow_future = True
    context_object_name = 'mylist'
    template_name = 'index.html'
    model = PersonInfo
    date_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.mfbz.cn/a/558318.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第一届AI Agent智能体现场开发大赛报名开启!8月上旬火热开赛~

由联想拯救者、AIGC开放社区、英特尔携手主办的“AI生成未来第二届拯救者杯OPENAIGC开发者大赛”已经正式启动&#xff0c;“2024 AI Agent极限挑战赛”作为特设专项赛道&#xff0c;也将同步于8月上旬开赛&#xff0c;参赛者将在更加紧张刺激的现场比赛中展现其技术与创造力。…

TypeScript 基础:接口、泛型和自定义类型在 Vue 3 中的应用

接口&#xff08;Interfaces&#xff09; 首先&#xff0c;我们定义一个接口 PersonInter 来描述一个人的信息&#xff0c;包括 id、name 和 age。 interface PersonInter {id: string;name: string;age: number; }自定义类型&#xff08;Custom Types&#xff09; 然后&…

如何在Windows 10中启用和使用上帝模式,这里有详细步骤

序言 上帝模式&#xff08;God Mode&#xff09;是一个特殊的文件夹&#xff0c;只在一个窗口中显示所有可用的操作设置。它可以节省搜索命令的时间&#xff0c;而无需知道通过“开始”菜单或“控制面板”查找命令的步骤。上帝模式默认情况下是隐藏的&#xff0c;所以我们需要…

世界500强:破解“智慧核能”数智化成功转型密码

近日&#xff0c;实在智能携手中国核能行业协会信息化专业委员会在中国人工智能小镇成功举办“基于大模型的RPA数字员工在核能行业实战应用案例专项培训”&#xff0c;中国核工业集团、中国广核集团、国家电力投资集团等企事业单位共同参加。中核集团作为我国核科技工业的主体&…

C++修炼之路之继承<二>

目录 一&#xff1a;子类的六大默认成员函数 二&#xff1a;继承与友元 三&#xff1a;继承与静态成员 四&#xff1a;复杂的继承关系菱形继承菱形虚拟继承 1.单继承 2.多继承 3.菱形继承&#xff1b;一种特殊的多继承 4.菱形虚拟继承 5.虚拟继承解决数据冗余和二…

javaagent使用

Java Agent是什么&#xff1f; Java Agent是Java平台提供的一个强大工具&#xff0c;它可以在运行时修改或增强Java应用程序的行为。是在JDK1.5以后引入的&#xff0c;它能够在不影响正常编译的情况下修改字节码&#xff0c;相当于是在main方法执行之前的拦截器&#xff0c;也叫…

启明云端ESP32-S3+车载桥接器案例,能实现对车载产品集控

最近房车旅行很盛行&#xff0c;谁不想五一自驾游开车去外面玩&#xff1f;为了能提升用户体验&#xff0c;车企房车智能化升级越来越普遍&#xff0c;接下来小启给大家讲一个案例&#xff0c;启明云端ESP32-S3车载桥接器&#xff0c;感兴趣的可以看看。 一、ESP32-S3车载桥接器…

实验:使用FTP+yum实现自制yum仓库

实验准备 FTP服务器端&#xff1a;centos-1&#xff08;IP:10.9.25.33&#xff09; 客户端&#xff1a;centos-2 两台机器保证网络畅通&#xff0c;原yum仓库可用&#xff0c;已关闭防火墙和selinux FTP服务器端 ①安装vsftpd并运行&#xff0c;设定开机自启动 安装vsftpd…

免费ssl通配符证书申请教程

在互联网安全日益受到重视的今天&#xff0c;启用HTTPS已经成为网站运营的基本要求。它不仅保障用户数据传输的安全&#xff0c;提升搜索引擎排名&#xff0c;还能增强用户对网站的信任。通配符证书是一种SSL/TLS证书&#xff0c;用于同时保护一个域名及其所有下一级子域名的安…

​面试经典150题——对称二叉树

1. 题目描述 2. 题目分析与解析 2.1 思路一——递归 为了解决问题“检查一个二叉树是否是对称的”&#xff0c;我们需要判断树的左子树和右子树是否是彼此的镜像。这意味着树的左子树的左侧应该与右子树的右侧相同&#xff0c;左子树的右侧应该与右子树的左侧相同。 定义问题…

数字工厂管理系统的应用场景主要有哪些

随着信息技术的飞速发展&#xff0c;数字工厂管理系统逐渐成为了工业制造领域的一大亮点。这一系统集成了物联网、大数据、云计算、人工智能等先进技术&#xff0c;实现了对工厂生产流程的智能化、高效化管理。那么&#xff0c;数字工厂管理系统究竟在哪些应用场景中发挥着重要…

Unity 3D定点数物理引擎实战系列:BEPU物理引擎碰撞计算与碰撞规则的架构与设计

前面我们讲解了如何监听物理引擎的碰撞事件, 在物理引擎内核中如何架构与设计碰撞规则,使得物理Entity与周围的物理环境产生碰撞时&#xff0c;如何灵活的控制物理碰撞&#xff0c;本节給大家详细的讲解BEPUphysicsint 物理引擎内部是如何管理与控制碰撞规则的。本文主要讲解3个…

项目二:学会使用python爬虫请求库(小白入门级)

上一章已经了解python爬虫的基本知识&#xff0c;这一次让我们一起来学会如何使用python请求库爬取目标网站的信息。当然这次爬虫之旅相信我能给你带来不一样的体验。 目录 一、安装requests 库 简介 安装 步骤 1.requests的基本使用3步骤 2.查看所使用编码 3.设置编码…

Qt菜单栏

文章目录 创建菜单栏创建菜单并在菜单栏中添加创建子菜单并添加到菜单创建菜单项并在菜单中添加分割线实现简易的记事本 Qt 窗口是通过 QMainWindow类 来实现的 创建菜单栏 Qt 中的菜单栏是通过 QMenuBar 这个类来实现的&#xff0c;一个窗口最多只有一个菜单栏。 菜单栏包含…

动态库静态库linux

动态库静态库 静态库 静态库必须包含在可执行文件里&#xff0c;整个都要包含 缺点&#xff1a;消耗系统大&#xff0c;每个使用静态库的程序都要复制静态库&#xff08;浪费内存&#xff09; 影响使用场景&#xff1a; 在静态库内存小的时候&#xff0c;可以用来提升速度 制…

学习MQ异步

1.MQ异步调用的优势 事件驱动模式&#xff1a; 优势&#xff1a; 总结&#xff1a; 2.初识MQ 核心概念以及结构&#xff1a; 常见的消息模型&#xff1a; 基本消息队列模型&#xff1a; 生产者代码&#xff1a; Testpublic void testSendMessage() throws IOException, Timeo…

亿级电商实时数据分析平台构建实战

基于FlinkClickHouse构建亿级电商实时数据分析平台&#xff08;PC、移动、小程序&#xff09; 引用网络文章开启本课程的开篇&#xff1a; 在大数据分析领域中&#xff0c;传统的大数据分析需要不同框架和技术组合才能达到最终的效果&#xff0c;在人力成本&#xff0c;技术能…

比较转录组学方法推断基因共表达网络及其在玉米和水稻叶片转录组中的应用 TO-GCN时序分析-文献精读-8

Comparative transcriptomics method to infer gene coexpression networks and its applications to maize and rice leaf transcriptomes 比较转录组学方法推断基因共表达网络及其在玉米和水稻叶片转录组中的应用 TO-GCN时序分析&#xff0c;媲美加权基因共表达网络分析-WG…

2024.4.16

多进程并发服务器 #include<myhead.h> #define SER_IP "192.168.125.54" #define SER_PORT 8888 void handler(int signo) {if(signoSIGCHLD){while(waitpid(-1,NULL,WNOHANG)>0);} } int main(int argc, char *argv[]) {//将SIGCHLD信号与处理函数绑定if(…

数据孤岛是业务效率的无声杀手

数据孤岛是组织的一个常见问题&#xff0c;因为它们可能对数据可访问性、数据完整性和数据管理造成障碍。当组织内的不同部门或团队拥有用于存储数据的数据库或系统&#xff0c;并且没有用于所有数据的中央存储库时&#xff0c;就会出现数据孤岛。这可能会导致难以全面了解数据…
最新文章