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 所示 .
图 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中编写以下代码 :
from django. contrib import admin
from django. urls import path, includeurlpatterns = [ path( 'admin/' , admin. site. urls) , path( '' , include( ( 'index.urls' , 'index' ) , namespace= 'index' ) ) ,
]
from django. urls import path
from . views import * urlpatterns = [ path( '' , index, name= 'index' ) , path( 'TurnTo' , TurnTo. as_view( ) , name= 'TurnTo' ) ,
]
from django. shortcuts import render
from django. views. generic. base import RedirectViewdef index ( request) : return render( request, 'index.html' ) class TurnTo ( RedirectView) : permanent = False url = None pattern_name = 'index:index' query_string = True def get_redirect_url ( self, * args, ** kwargs) : print ( 'This is get_redirect_url' ) return super ( ) . get_redirect_url( * args, ** kwargs) def get ( self, request, * args, ** kwargs) : print ( request. META. get( 'HTTP_USER_AGENT' ) ) return super ( ) . get( request, * args, ** kwargs)
<! 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>
在index的views . py中定义了视图类TurnTo , 它继承父类RedirectView ,
对父类的属性进行重设并将父类的类方法get_redirect_url ( ) 和get ( ) 进行重写 ,
通过这样的方式可以对视图类RedirectView进行功能扩展 , 从而满足开发需求 . 定义路由的时候 , 若使用视图类TurnTo处理HTTP请求 ,
则需要对视图类TurnTo使用as_view ( ) 方法 , 这是对视图类turnTo进行实例化处理 .
as_view ( ) 方法可在类View里找到具体的定义过程 ( django \ views \ generic \ base . py ) .
运行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 . )
( 客户端向 / TurnTo?k = 1 发起了一个GET请求 , 服务器返回了一个 302 状态码 , 并可能在响应头中包含了Location : / ?k = 1 这样的信息 .
然后 , 客户端遵循这个重定向 , 向新的URL / ?k = 1 发起了一个新的GET请求 , 这次请求返回了 200 状态码 , 表示请求成功 . )
图 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 所示 .
图 5 - 3 视图类TemplateView的源码
从视图类TemplateView的源码看到 , 它只定义了类方法get ( ) ,
该方法分别调用函数方法get_context_data ( ) 和render_to_response ( ) , 从而完成HTTP请求的响应过程 .
类方法get ( ) 所调用的函数方法主要来自父类TemplateResponseMixin和ContextMixin ,
为了准确地描述函数方法的调用过程 , 我们以流程图的形式加以说明 , 如图 5 - 4 所示 .
图 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中编写以下代码 :
from django. urls import path
from . views import * urlpatterns = [ path( '' , Index. as_view( ) , name= 'index' ) ,
]
from django. views. generic. base import TemplateViewclass 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 contextdef post ( self, request, * args, ** kwargs) : self. extra_context = { 'title' : 'This is POST' } context = self. get_context_data( ** kwargs) return self. render_to_response( context)
<! 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 所示 .
图 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 所示 .
图 5 - 6 视图类ListView的源码文件
从视图类ListView的继承方式看到 , 它继承两个不同的父类 , 这些父类也继承其他的视图类 .
为了梳理类与类之间的继承关系 , 我们以流程图的形式表示 , 如图 5 - 7 所示 .
图 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中编写以下代码 :
from django. db import modelsclass PersonInfo ( models. Model) : id = models. AutoField( primary_key= True ) name = models. CharField( max_length= 20 ) age = models. IntegerField( )
上述代码只是搭建PersonInfo类和数据表personinfo的映射关系 , 但在数据库中并没有生成相应的数据表 .
因此 , 下一步通过两者的映射关系在数据库里生成相应的数据表 .
数据库以SQLite3为例 , 在PyCharm的Terminal里依次输入数据迁移指令 .
D: \MyDjango> python manage. py makemigrations
Migrations for 'index' : index\migrations\0001_initial. py- Create model personinfo
D: \MyDjango> python manage. py migrate
当指令执行完成后 , 使用Navicat Premium软件打开MyDjango的db . sqlite3文件 , 在此数据库中可以看到新创建的数据表 , 如图 5 - 8 所示 .
( 1. 点击连接 -- > 2. 选择SQLite -- > 3. 设置连接名称 -- > 4. 选择现有的数据库文件 -- > 5. 在数据库库文件中选择文件 ( 文件在项目下 ) -- > 6. 测试连接 -- > 7. 连接 )
图 5 - 8 数据表信息
从图 5 - 8 中看到 , 当指令执行完成后 , Django会默认创建多个数据表 ,
其中数据表index_personinfo对应index的models . py中所定义的PersonInfo类 ,
其余的数据表都是Django内置的功能所生成的 , 主要用于Admin站点 , 用户认证和Session会话等功能 .
在数据表index_personinfo中添加如图 5 - 9 所示的数据 .
图 5 - 9 添加数据
完成上述操作后 , 下一步在MyDjango里使用视图类ListView ,
在index的views . py里定义视图类index , 并重新编写模板文件index . html的代码 :
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
<! 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>
视图类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 所示 .
图 5 - 10 运行结果
5.1.4 详细视图DetailView
视图类DetailView是将数据库某一条数据详细显示在网页上 , 它与视图类ListView存在明显的差异 .
在PyCharm里打开视图类DetailView的源码文件 , 分析视图类DetailView的定义过程 , 如图 5 - 11 所示 .
图 5 - 11 视图类DetailView的源码文件
从视图类DetailView的继承方式看到 , 它继承两个不同的父类 , 这些父类也继承其他的视图类 .
为了梳理类与类之间的继承关系 , 我们以流程图的形式表示 , 如图 5 - 12 所示 .
图 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中编写以下代码 :
from django. urls import path
from . views import * urlpatterns = [ path( '<pk>/<age>.html' , Index. as_view( ) , name= 'index' )
]
from django. views. generic import DetailView
from . models import PersonInfoclass Index ( DetailView) : template_name = 'index.html' extra_context = { 'title' : '人员信息表' } slug_field = 'age' slug_url_kwarg = 'age' pk_url_kwarg = 'pk' model = PersonInfo
<! 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>
路由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 , 通过主键获取数据 .
5.2 数据操作视图
数据操作视图是对模型进行操作 , 如增 , 删 , 改 , 从而实现Django与数据库的数据交互 .
数据操作视图有 4 个视图类 , 分别是FormView , CreateView , UpdateView和DeleteView , 说明如下 :
● FormView视图类使用内置的表单功能 , 通过表单实现数据验证 , 响应输出等功能 , 用于显示表单数据 .
● CreateView实现模型的数据新增功能 , 通过内置的表单功能实现数据新增 .
● UpdateView实现模型的数据修改功能 , 通过内置的表单功能实现数据修改 .
● DeleteView实现模型的数据删除功能 , 通过内置的表单功能实现数据删除 .
5.2.1 表单视图FormView
视图类FormView是表单在视图里的一种使用方式 , 表单是搜集用户数据信息的各种表单元素的集合 ,
作用是实现网页上的数据交互 , 用户在网站输入数据信息 , 然后提交到网站服务器端进行处理 , 如数据录入和用户登录 , 注册等 .
在PyCharm里打开视图类FormView的源码文件 , 分析视图类FormView的定义过程 , 如图 5 - 13 所示 .
图 5 - 13 视图类FormView的源码文件
从视图类FormView的继承方式可以看到 , 它也是继承两个不同的父类 , 而父类继承其他的视图类 .
为了梳理类与类之间的继承关系 , 我们以流程图的形式表示 , 如图 5 - 14 所示 .
图 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中分别编写以下代码 :
from django import forms
from . models import PersonInfoclass PersonInfoForm ( forms. ModelForm) : class Meta : model = PersonInfofields = '__all__'
from django. urls import path
from . views import * urlpatterns = [ path( '' , Index. as_view( ) , name= 'index' ) , path( 'result' , result, name= 'result' )
]
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 = PersonInfoFormextra_context = { 'title' : '人员信息表' }
<! 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>
上述代码是视图类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 所示 .
图 5 - 15 运行结果
5.2.2 新增视图CreateView
视图类CreateView是对模型新增数据的视图类 , 它是在表单视图类FormView的基础上加以封装的 .
简单来说 , 就是在视图类FormView的基础上加入数据新增的功能 .
视图类CreateView与FormView是在同一个源码文件里定义的 , 在源码文件里分析视图类CreateView的定义过程 ,
以流程图的形式表述类的继承关系 , 如图 5 - 16 所示 .
图 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' model = PersonInfofields = [ 'name' , 'age' ] extra_context = { 'title' : '人员信息表' }
视图类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 所示 .
图 5 - 17 数据表index_personinfo
5.2.3 修改视图UpdateView
视图类UpdateView是在视图类FormView和视图类DetailView的基础上实现的 ,
它首先使用视图类DetailView的功能 ( 功能核心类是SingleObjectMixin ) ,
通过路由变量查询数据表某条数据并显示在网页上 , 然后在视图类FormView的基础上 , 通过表单方式实现数据修改 . 视图类UpdateView与FormView是在同一个源码文件里定义的 ,
在源码文件里分析视图类UpdateView的定义过程 , 以流程图的形式表示类的继承关系 , 如图 5 - 18 所示 .
图 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中分别编写以下代码 :
from django. urls import path
from . views import * urlpatterns = [ path( '<age>.html' , Index. as_view( ) , name= 'index' ) , path( 'result' , result, name= 'result' ) ,
]
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 = PersonInfofields = [ 'name' , 'age' ] slug_url_kwarg = 'age' slug_field = 'age' extra_context = { 'title' : '人员信息表' }
<! 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>
视图类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 ! ! ! )
图 5 - 19 数据表index_personinfo
5.2.4 删除视图DeleteView
视图类DeleteView的使用方法与视图类UpdateView有相似之处 , 但两者的父类继承关系有所差异 .
在源码文件里分析视图类DeleteView的定义过程 , 以流程图的形式表示类的继承关系 , 如图 5 - 20 所示 .
图 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' ) ,
]
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' : '人员信息表' }
<! 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>
路由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 所示 .
图 5 - 21 数据表index_personinfo
5.3 日期筛选视图
日期筛选视图是根据模型里的某个日期字段进行数据筛选的 , 然后将符合结果的数据以一定的形式显示在网页上 .
简单来说 , 在列表视图ListView或详细视图DetailView的基础上增加日期筛选所实现的视图类 .
它一共定义了 7 个日期视图类 , 说明如下 :
● ArchiveIndexView是将数据表所有的数据以某个日期字段的降序方式进行排序显示的 . 该类的继承关系如图 5 - 22 所示 .
图 5 - 22 视图类ArchiveIndexView的继承关系
● YearArchiveView是在数据表筛选某个日期字段某年的所有的数据 , 默认以升序的方式排序显示 , 年份的筛选范围由路由变量提供 . 该类的继承关系如图 5 - 23 所示 .
图 5 - 23 视图类YearArchiveView的继承关系
● MonthArchiveView是在数据表筛选某个日期字段某年某月的所有的数据 , 默认以升序的方式排序显示 , 年份和月份的筛选范围都由路由变量提供 . 该类的继承关系如图 5 - 24 所示 .
图 5 - 24 视图类MonthArchiveView的继承过程
● WeekArchiveView是在数据表筛选某个日期字段某年某周的所有的数据 , 总周数是将一年的总天数除以 7 所得的 , 数据默认以升序的方式排序显示 , 年份和周数的筛选范围都是由路由变量提供的 . 该类的继承关系如图 5 - 25 所示 .
图 5 - 25 视图类WeekArchiveView的继承关系
● DayArchiveView是对数据表的某个日期字段精准筛选到某年某月某天 , 将符合条件的数据以升序的方式排序显示 , 年份 , 月份和天数都是由路由变量提供的 . 该类的继承关系如图 5 - 26 所示 .
图 5 - 26 视图类DayArchiveView的继承关系
● TodayArchiveView是在视图类DayArchiveView的基础上进行封装处理的 , 它将数据表某个日期字段的筛选条件设为当天时间 , 符合条件的数据以升序的方式排序显示 . 该类的继承关系如图 5 - 27 所示 .
图 5 - 27 视图类TodayArchiveView的继承关系
● DateDetailView是查询某年某月某日某条数据的详细信息 , 它在视图类DetailView的基础上增加了日期筛选功能 , 筛选条件主要有年份 , 月份 , 天数和某个模型字段 , 其中某个模型字段必须具有唯一性 , 才能确保查询的数据具有唯一性 . 该类的继承关系如图 5 - 28 所示 .
从日期筛选视图类的继承关系得知 , 它们的继承关系都有一定的相似之处 , 说明它们的属性和方法在使用上不会存在太大的差异 .
因此 , 我们选择最有代表性的视图类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 , 代码如下 :
from django. db import modelsclass PersonInfo ( models. Model) : id = models. AutoField( primary_key= True ) name = models. CharField( max_length= 20 ) age = models. IntegerField( ) hireDate = models. DateField( )
模型PersonInfo定义完成后 , 将index的migrations文件夹的 0001 _initial . py删除 ,
同时使用Navicat Premium打开数据库文件db . sqlite3 , 将数据库所有的数据表删除 ,
接着在PyCharm的Terminal选项卡里依次输入数据迁移指令 .
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 所示 .
图 5 - 29 数据表index_personinfo的数据信息
完成项目环境搭建后 , 接下来使用视图类MonthArchiveView实现数据筛选功能 .
在index的urls . py , views . py和模板文件index . html中分别编写以下代码 :
from django. urls import path
from . views import * urlpatterns = [ path( '<int:year>/<int:month>.html' , Index. as_view( ) , name= 'index' ) ,
]
from django. views. generic. dates import MonthArchiveView
from . models import PersonInfoclass Index ( MonthArchiveView) : allow_empty = True allow_future = True context_object_name = 'mylist' template_name = 'index.html' model = PersonInfodate_field = 'hireDate' queryset = PersonInfo. objects. all ( ) year_format = '%Y' month_format = '%m' paginate_by = 50
<! 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>
路由index在路由地址里设置变量year和month , 而且变量的数据类型都是整型 ,
其中路由变量month可以设为字符型 , 不同的数据类型会影响视图类MonthArchiveView的属性month_format的值 .
视图类index继承父类MonthArchiveView , 它共设置了 10 个属性 , 每个属性已经详细讲述过了 , 在此不再重复说明 .
视图类index是对模型PersonInfo进行数据查找 , 查找方式是以字段hireDate的日期内容进行数据筛选 ,
筛选条件来自路由变量year和month , 由于变量month的数据类型是整型 , 因此将属性month_format的默认值 % b改为 % m ,
否则Django会提示 404 异常 , 如图 5 - 30 所示 .
图 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进行筛选 , 筛选条件为 2018 年 09 月份 , 符合条件的所有数据显示在网页上 , 如图 5 - 31 所示 .
图 5 - 31 运行结果
从图 5 - 31 看到 , 网页上显示的日期是以月 , 日 , 年的格式显示的 , 并且月份是以英文表示的 .
如果想让日期格式与数据库的日期格式相同 , 那么可以使用模板语法的过滤器date来转换日期格式 .
从路由index的变量month得知 , 该变量的数据类型可设为字符型 , 如果该变量改为字符型 , 那么视图类index无须设置属性month_format .
假设将路由变量month改为字符型并注释视图类index的属性month_format ,
重启运行MyDjango项目 , 在浏览器上访问 : 127.0 .0 .1 : 8000 / 2018 /sep.html , 网页显示的内容如下 .
若想验证属性allow_empty和allow_future的作用 , 则可单独设置allow_empty的值 , 第一次设为True , 第二次设为False ,
并且每次都访问 : 127.0 .0 .1 : 8000 / 2017 / 10. html , 然后对比两次访问结果的差异即可 .
( allow_empty : 在模型中查询数据不存在的情况下是否显示页面 , 若为False并且数据不存在 , 则引发 404 异常 , 默认值为False . )
( 模板文件中 { % if previous_month % } . ) 和 { % if next_month % } 控制上下月份的渲染 . 数据库中最早的时间是 2018 / 7 月 , 访问这个地址的时候 , 不会显示上个月 ( 没有数据不允许访问 ) , 数据库中最晚的时间是 2019 / 10 月 , 访问这个地址的时候 , 不会显示下个月 ( 没有数据不允许访问 ) . )
同理 , 属性allow_future的验证方式相同 , 但其访问的路由地址改为 : 127.0 .0 .1 : 8000 / 2019 / 10. html ( 使用当前时间 + 一个月测试 ) .
( allow_future : 这个属性决定用户是否可以通过 URL 访问未来的日期 , 默认为False , 则用户不能访问未来的日期 . )
综上所述 , 视图类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中分别编写以下代码 :
from django. urls import path
from . views import * urlpatterns = [ path( '<int:year>/<int:week>.html' , Index. as_view( ) , name= 'index' ) ,
]
from django. views. generic. dates import WeekArchiveView
from . models import PersonInfoclass Index ( WeekArchiveView) : allow_empty = True allow_future = True context_object_name = 'mylist' template_name = 'index.html' model = PersonInfodate_field = 'hireDate' queryset = PersonInfo. objects. all ( ) year_format = '%Y' week_format = '%W' paginate_by = 50
<! 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>
路由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的大部分数据集中在 2018 年 9 月 , 这个日期的周数为 38.
运行MyDjango项目 , 在浏览器上访问 : 127.0 .0 .1 : 8000 / 2018 / 38. html ,
Django将日期从 2018 - 09 - 18 至 2018 - 09 - 22 的数据显示在网页上 , 如图 5 - 32 所示 .
图 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的基础上增加日期筛选功能 , 筛选条件主要有年份 , 月份 , 天数和某个模型字段 , 其中某个模型字段必须具有唯一性 , 才能确保查询的数据具有唯一性 .