第五章 探究 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:實現模型的數據刪除功能,通過內置的表單功能實現數據刪除。
? 視圖類 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 部分:數據顯示視圖、數據操作視圖和日期篩選視圖。