wjx-2005-07-01  

第五章 探究 CBV 视图

5.1 数据显示视图

​ 数据显示视图是将后台的数据展示在网页上,数据主要来自模型,一共定义了4个视图类,分别说明如下:

  • RedirectView:用于实现 HTTP 重定向,默认情况下只定义 GET 请求的处理方法。
  • TemplateView:视图类的基础视图,可将数据传递给 HTML 模版,默认情况下只定义 GET 请求的处理方法。
  • ListView:在 TemplateView 的基础上将数据以列表显示,通常将某个数据表的数据以列表表示。
  • DetailView:在 TemplateView 的基础上将数据详细显示,通常获取数据表的单条数据。

5.1.1 重定向视图 RedirectView

​ 视图类 RedirectView 用于实现 HTTP 重定向功能,即网页跳转功能。

视图类 RedirectView 定义了4个属性和8个类方法,分别说明如下:

  • permanent:根据属性值的真假来选择重定向方式,若为 True ,则 HTTP 状态码为 301(永久重定向),否则为 302.
  • url:代表重定向的路由地址。
  • pattern_name:代表重定向的路由命名。如果设置参数 url,则无需设置该参数。
  • query_string:是否将当前路由地址的请求参数传递到重定向的路由地址。
  • get_redirect_url():根据属性 pattern_name 所指向的路由命名来生成相应的路由地址。
  • get():触发 HTTP 的 GET 请求所执行的响应处理。
  • 剩余的类方法都是 HTTP 的不同请求方式,它们都由 get() 方法完成响应处理。

​ 由于类具有继承的特性,因此可以对视图类 RedirectView 进行功能扩展。

# App1/views.py 文件
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')
        # super 是一个内置函数,它返回一个代理对象,用于调用父类的方法
        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)
# App1/urls.py
path('turnTo',views.turnTo.as_view(),name='turnTo'),
	<h3>Hello World</h3>
    <a href="{% url 'index:turnTo' %}?k=1">ToTurn</a>

定义路由时,若使用视图类 turnTo 处理 HTTP 请求,则需要对视图类 turnTo 使用 as_view()方法,这是对视图类进行实例化处理。as_view()方法可在类 View 里找到具体的定义过程。

5.1.2 基础视图 TemplateView

​ 视图类 TemplateView 是所有视图类里最基础的应用视图类,从视图类 TemplateView 的源码来看,它只定义了类方法 get(),该方法分别调用函数方法 get_context_data()和render_to_response(),从而完成 HTTP 请求的响应过程。类方法 get() 所调用的函数方法主要来自父类 TemplateResponseMixin 和 ContextMixin

class TemplateView(TemplateResponseMixin, ContextMixin, View):
    """
    Render a template. Pass keyword arguments from the URLconf to the context.
    """
    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

视图类 TemplateView 的 get() 所调用的函数说明如下:

  • 视图类 ContextMixin 的 get_count_data() 方法用于获取模版上下文内容,模版上下文是将图里的数据传递到模版文件,在由模版引擎将数据转换成 HTML 网页数据。
  • 视图类 TemplateResponseMixin 的 render_to_response() 用于实现响应处理,由响应类TemplateResponse 完成。

在视图类 TemplateResponseMixin 的源码文件里找到视图类 TemplateResponseMixin 的定义过程,该类设置了 4 个属性和两个类方法,具体说明如下:

  • template_name:设置模版文件的文件名。
  • template_engine:设置解析模版文件的模版引擎。
  • response_class:设置 HTTP 请求的响应类,默认值为响应类 TemplateResponse。
  • content_type:设置响应内容的数据格式,一般情况下使用默认值即可。
  • render_to_response():实现响应处理,由响应类 TemplateResponse 完成。
  • get_template_names():获取属性 template_name的值。
# App1/views.py
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 的请求信息
    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)
# App1/urls.py
path('',views.index.as_view(),name='index'),
	<h3>Hello,{{ title }}</h3>
    <div>{{ value }}</div>
    <br>
    <form action="" method="post">
        {% csrf_token %}
        <input type="submit" value="Submit">
    </form>

上述代码是将网站首页的视图函数 index 改为视图类 index,自定义视图类 index 继承视图类 TemplateView ,并重设了 4 个属性,重写类两个类方法,具体说明如下:

  • template_name:将模版文件'index.html'作为网页文件。
  • template_engine:设置解析模版文件的模版引擎,默认值为 None,即默认使用配置文件的 TEMPLATES 所设置的模版引擎 BASKEND。
  • content_type:设置响应内容的数据格式,默认值为 None,即代表数据格式为 text/html。
  • get_context_data():继承并重写视图类 TemplateView的类方法,在变量 context 里新增数据 value。
  • extra_context:为模版文件的上下文设置变量值,可将数据转换为网页数据展示在浏览器上。
  • post():自定义 POST 请求的处理方法,当触发 POST 请求时,将会重设置属性 extra_context 的值,并调用 get_context_data() 将属性 重新写入,从而实现动态改变模版上下文的数据内容。

5.1.3 列表视图 ListView

​ 已知视图是连接路由和模版的中心枢纽,除此之外,视图还可以连接模型。简单来说,模型是指 Django 通过一定的规则来映射数据库,从而方便 Django 与数据库之间实现数据交互,这个交互过程是在视图里实现的。

​ 由于视图可以与数据库实现数据交互,因此 Django定义了视图类 ListView,该视图类是将表的数据以列表的形式显示,常用于数据的查询和展示。

class ListView(MultipleObjectTemplateResponseMixin, BaseListView):
    """
    Render some list of objects, set by `self.model` or `self.queryset`.
    `self.queryset` can actually be any iterable of items, not just a queryset.
    """

从视图继承的方式来看,它继承两个不同的父类。视图类 ListView 的底层类是由 TemplateResponseMixin 和 ContextMixin 和 View 组成的,它具有视图类 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 定义,设置分页的功能类,默认情况下使用内置分页功能。
  • ......
# App1/models.py 
from django.db import models

# Create your models here.
class PersonInfo(models.Model):
    id = models.AutoField(primary_key = True)
    name = models.CharField(max_length=20)
    age = models.IntegerField()

以上代码是创建一个模型对象,上述代码只是搭建了 PersonInfo 类和数据表 personinfo的映射关系,但在数据库中并没有生成相应的数据表,需要进一步操作在数据库中生成相应的数据表。

打开 PyCharm 的 Terminal 里依次输入数据迁移指令。

1、python manage.py makemigrations
2、python manage.py migrate

当指令执行完成后,数据库中就可以看到新创建的数据表。

	<h3>{{ title }}</h3>
    <table border="1">
        {% for i in personinfo %}
            <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 %}
class index(ListView):
    # 设置模版文件
    template_name = 'index.html'
    # 设置模型外的数据
    extra_context = {'title':'人员信息表'}
    # 查询模型 PersonInfo
    queryset = PersonInfo.objects.all()
    # 每页展示一条数据
    paginate_by = 1
    # 若不设置,则模版上下文默认为 personinfo_list
    context_object_name = 'personinfo'

5.1.4 详细视图

​ 视图类 DetailView 是将数据库某一条数据详细显示在网页上,它与视图类 ListView 存在明显的差异。

class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
    """
    Render a "detail" view of an object.

    By default this is a model instance looked up from `self.queryset`, but the
    view will support display of *any* object by overriding `self.get_object()`.
    """
# App1/views.py
class index(DetailView):
    # 设置模版文件
    template_name = 'index.html'
    # 设置模型外的数据
    extra_context = {'title':'人员信息表'}
    # 设置模型的查询字段
    slug_field = 'age'
    # 设置路由的变量名
    slug_url_kwarg = 'age'
    pk_url_kwarg = 'pk'
    # 设置查询模型 PersonInfo
    model = PersonInfo
    # 属性 queryset 可以做简单的查询操作
    queryset = PersonInfo.objects.all()
    # 模版上下文的命名
    context_object_name = 'personinfo'
    # 是否将 pk 和 slug 作为查询条件
    # query_pk_and_slug = False

5.2 数据操作视图

​ 数据操作视图是对模型进操作,如增、删、改,从而实现 Django 与数据库的数据交互。数据操作视图有4个视图类,分别是 FormView、CreateView、UpdateView和DeleteView,说明如下:

  • FormView:该视图类使用内置的表单功能,通过内置的表单功能实现数据新增。
  • CreateView:实现模型的数据新增功能,通过内置的表单功能实现数据新增。
  • UpdateView:实现模型的数据修改功能,通过内置的表单功能实现数据修改。
  • DeleteView:实现模型的数据删除功能,通过内置的表单功能实现数据删除。

5.2.1 表单视图 FormView

​ 视图类 FormView 是表单在视图里的一种使用方式,表单是搜集用户数据信息的各种表单元素的集合,作用是实现网页上的数据交互,用户在网站输入数据信息,然后提交到网站服务器端进行处理。

​ 视图类 FormView 不仅具有视图类 TemplateView 的所有属性和方法,还新增了以下属性和方法:

  • initial:由 FormMixin 定义,设置表单初始化的数据。
  • form_class:由 FormMixin 定义,设置表单类。
  • success_url:由 FormMixin 定义,设置重定向的路由地址。
  • prefix:由 FormMixin 定义,设置表单前缀(即表单在模版的上下文),可在模版里生成表格数据。
  • get_initial():由 FormMixin 定义,获取表单初始化的数据。
  • ......

新建 form.py 文件

# form.py
from django import forms
from .models import PersonInfo

class PersonInfoForm(forms.ModelForm):
    """
    Meta 是一个嵌套在 PersonInfoForm 类中的类,它是 Django 模型表单中的一个特殊类,用于定义表单如何与PersonInfo 模型关联
    Meta 类中可以包含多个属性,其中最重要的是 model 和 fields
    """
    class Meta:
        model = PersonInfo # 指定了与之关联的模型
        fields = '__all__' # 定义表单将包含哪些字段
# views.py
class index(FormView):
    initial = {'name':'Betty','age':20} # 表单初始化数据
    template_name = 'index.html' # 关联模版
    success_url = '/result'
    form_class = PersonInfoForm
    extra_context = {'title':'人员信息表'}
def result(request):
    return HttpResponse('Success')
# urls.py
	path('',views.index.as_view(),name='index'),
    path('result',views.result,name='result'),
	<h3>{{ title }}</h3>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="确定">
    </form>

对上述代码做进一步的讲解:

  • App1 的 form.py 文件里定义了表单类 PersonInfoForm,该表单是根据模型 PersonInfo 定义的模型表单,表单的字段来自模型的字段。
  • 路由 index 的请求处理由视图类 FormView 完成,而路由 result 为视图类 index 的属性 success_url 提供路由地址。
  • 视图类 index 仅设置了5个属性,属性 extra_context 的值对应模版上下文的 title,属性 form_class 所设置的表单在实例化之后可在模版里使用上下文 form.as_p 生成表格。

5.2.2 新增视图 CreateView

​ 视图类 CreateView 是对模型新增数据的视图类,它是在表单视图类 FormView 的基础上加以封装的。它具有视图类 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 最大的特点在于 get_form_class() 方法,它是通过判断属性 form_class、fields 和 model,从而实现数据新增操作。其判断方法如下:

    • 若fields 和 form_class 都不等于 None ,则抛出异常,提示不允许同时设置 fields 和 form_class 。
    • 若 form_class 不等于 None,则返回 form_class。
    • 若 form_class 等于 None,则从属性 model、object 和 get_queryset() 获取模型对象。同时,fields不能为 None,根据属性 fields 生成表单对象,由表单内置的 modelform_factory() 方法实现。

综上所述,视图类 CreateView 有两种表单生成方式。第一种是设置属性 form_class,这种方式需要开发者自定义表单对象;第二种是设置属性 model 和 fields,由模型对象和模型字段来生成相应的表单对象,生成的表单字段与模型的字段要求相符,可以减少异常情况发生,无需开发者自定义表单对象。

class index(CreateView):
    initial = {'name':'Betty','age':13}
    template_name = 'index.html'
    success_url = '/result'
    # 表单生成方式1
    # form_class = PersonInfoForm
    # 表单生成方式2
    model = PersonInfo
    fields = {'name','age'}
    extra_context = {'title':'人员信息表'}

​ 视图类 index 只需设置某些类属性即可完成模型数据的新增功能,整个数据新增过程都由视图类 CreateView 完成。

5.2.3 修改视图 UpdateView

​ 视图类 UpdateView 是在视图类 FormView 和视图类 DetailView 的基础上实现的,它首先使用视图类 DetailView 的功能,通过路由变量查询数据表某条数据并显示在网页上,然后在视图类 FormView 的基础上,通过表单方式实现数据修改。

# urls.py
path('<age>.html',views.index.as_view(),name='index')
# views.py
class index(UpdateView):
    template_name = 'index.html'
    success_url = '/result'
    model = PersonInfo
    fields = {'name','age'}
    # 设置模型的查询字段
    slug_field = 'age'
    # 设置路由变量名
    slug_url_kwarg = 'age'
    context_object_name = 'personinfo'
    extra_context = {'title':'人员信息表'}
	<h3>{{ title }}-{{ presoninfo.name }}</h3>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="确定">
    </form>

路由 index 的变量 age 对应视图类 index 的属性 slug_url_kwargs ,用于对模型字段 age 进行数据筛选。筛选结果将会生成表单 form 和 personinfo 对象。两者数据都是来自模型 PersonInfo。

5.2.4 删除视图 DeleteView

​ 视图类 DeleteView 只能删除单挑数据,路由变量为模型主键提供查询范围,因为模型主键具有唯一性,所以通过主键查询能精准到某条数据。查询出来的数据通过 POST 请求实现数据删除。

	<h3>{{ title }}-{{ presoninfo.name }}</h3>
    <form method="post">
        {% csrf_token %}
        <div>删除{{ personinfo.name }}</div>
        <input type="submit" value="确定">
    </form>
#urls.py
path('<pk>.html',views.index.as_view(),name='index')
#views.py
class index(DeleteView):
    template_name = 'index.html'
    success_url = '/result'
    model = PersonInfo
    context_object_name = 'personinfo'
    extra_context = {'title':'人员信息表'}

在上述的路由文件里的 pk 是一个默认值,代表着数据库的主键。

5.3 日期筛选视图

​ 日期筛选视图是根据模型里的某个日期字段进行数据筛选的,然后将符合结果的数据以一定的形式显示在网页上。一共定义了 7 个日期视图类,说明如下:

  • ArchiveIndexView 是将数据表所有的数据以某个日期字段的降序方式进行排序显示的。

  • YearArchiveView 是在数据表筛选某个日期字段某年的所有数据,默认以升序的方式排序显示,年份的筛选范围由路由变量提供。

  • MonthArchiveView 是在数据表筛选某个日期字段某年某月的所有数据,默认以升序的方式排序显示,年份和月份的筛选范围都由路由变量提供。

  • WeekArchiveView 是在数据表筛选某个日期字段某年某周的所有数据,总周数是将一年的总天数除以7所得的,数据默认以升序的方式排序显示,年份和周数的筛选都是由路由变量提供的。

  • DayArchiveView 是对数据表的某个日期字段精准筛选到某年某月某天,将符合条件的数据以升序的方式排序显示,年份、月份和天数都是由变量提供的。

  • TodayArchiveView 是在视图类 DayArchiveView 的基础上进行封装处理的,它将数据表某个日期字段的筛选条件设为当天时间。符合条件的数据以升序的方式排序显示。

  • DateDetailView 是查询某年某月某日某条数据的详细信息,它在视图类 DetailView 的基础上增加了日期筛选功能,筛选条件主要有年份、月份、天数和某个模型字段,其中某个模型字段必须具有唯一性。

    ​ 从日期筛选视图类的继承关系得知,它们的继承关系都由一定的相似之处,说明它们的属性和方法在使用上不会存在太大的差异。

5.3.1 月份视图 MonthArchiveView

​ 视图类 MonthArchiveView 新增的属性和方法说明如下:

  • template_name_suffix:由 MonthArchiveView 定义,设置模版后缀名,用于设置默认模版文件。
  • date_list_period:由 BaseDateView 定义,经 BaseMonthArchiveView 重写,设置日期列表的最小单位,默认为 day。
  • get_dated_items():由 BaseDateView 定义,经 BaseMonthArchiveView 重写,根据年份和月份在数据表查询符合条件的数据。
  • ......
# views.py
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' # 指定年在 URL 中的格式
    month_format = '%m'
    paginate_by = 50 # 指定每个页面的条目数
    
# urls.py
path('<int:year>/<int:month>.html',index.as_view(),name='index')
	<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>

​ 路由 index 在路由地址里设置变量 year 和 month,而且变量的数据类型都是整型,其中路由变量 month 可以设为字符型,不同的数据类型会影响视图类 MonthrchiveView 的属性的值。

5.3.2 周期视图

​ 在一年中,无论是平年还是闰年,一共有 52 周,如果要对数据表的数据生成周报表,就需要根据当前年份的周数来计算相应的日期范围,这样可以大大降低开发效率。

​ 视图类 WeekArchiveView 的继承过程共继承 10 个类,使用方法不做讲述。

本章小结

​ Django 植入了视图类这一功能,该功能封装了视图开发常使用的代码和模式,可以在无需编写大量代码的情况下,快速完成数据视图的开发,这种以类的形式实现响应与请求处理称为 CBV。

​ 视图类是通过定义和声明类的形式实现的,根据用途划分 3 部分:数据显示视图、数据操作视图和日期筛选视图。

posted on 2024-04-11 12:18  星辰与Python  阅读(10)  评论(0编辑  收藏  举报