玩 Django — Part 5(Generic View)

Andy Lu
11 min readApr 13, 2018

開發網頁的時候,經常會有重複的工作需要完成,例如列出資料、建立表單等等… Django提供Generic View,來解決這樣的問題。

Django 提供 下列四種通用views

  1. Base views
  2. Generic display views
  3. Generic editing views
  4. Generic date views

Base Views

View

View是所有class-based base的父類別,所有通用view都從此類別繼承。此類別可以從 django.views import得來

TemplateView

將自定義的模板(Template)呈現出來,並且可以包含Model的query

RedirectView

重新導向至指定的URL。

在指定的URL中,也可以包含Python的字典風格的字串格式(Dictionary-style string formatting),所以在RedirectView中,就可以由URL中輸入的字串重新導向到不同的位置,或是呈現不同的內容。

Generic display views

Django 提供兩種呈現資料的通用views,這兩種呈現資料的view是一般在網頁開發中最常遇到的。

DetailView

當DetailView被執行時,self.object 將會包含View正在操作的物件。

例如:

  • 將前面所使用的Coffee model利用 DetailView來呈現,首先,先在model內新增一個SlugField
*** models.py ***
from django.db import models
class Coffee(models.Model):
name = models.CharField(max_length=60)
price = models.IntegerField()
slug = models.SlugField(max_length=40)
  • views.py 只需要兩行程式碼即可完成:
*** views.py ***
from django.views.generic.detail import DetailView
from mycafe.models import Coffee
class CoffeeDetailView(DetailView):
model = Coffee
  • URL部分,只需要將對應slug的正規輸入法寫好,即可讀取指定Model中的項目。如下:
*** urls.py ***
from django.conf.urls import url, include
from .views import CoffeeDetailView
urlpatterns = [
url(r'^detail/(?P<slug>[-\w]+)/$', CoffeeDetailView.as_view()),
]
  • 在Template中只要利用 {{object.field_name}} 即可讀取欄位的資料。需要注意的是,DetailView會去存取
    templates\[app_name]\[model_name]_detail.html所以我們的CoffeeDetailView就會需要建立一個coffee_detail.html
*** coffee_detail.html ***
<h1>Coffee name: {{ object.name }}</h1>
<p>$: {{ object.price }}</p>

ListView

  • ListView 是用來呈現資料列表的。
  • 當此View被執行時 self.object_list 會包含此View正在執行的物件。
<h1>Coffee</h1>
<ul>
{% for coffee in object_list %}
<li>{{ coffee.name }}, Price: {{ coffee.price }}</li>
{% empty %}
<li>No coffee exists.</li>
{% endfor %}

</ul>

Generic editing views

FormView

  • FormView是用來顯示Form的一個View。
  • 只要在FormView的class中填入 template_nameform_class 以及 success_url 就可以將Form呈現出。

例如:

*** forms.py ***
class CoffeeForm(forms.ModelForm):
class Meta:
model = Coffee
fields = '__all__'
*** views.py ***
from django.views.generic.edit import FormView
class NewCoffeeView(FormView):
template_name = 'coffee_new.html'
form_class = CoffeeForm
success_url = '/coffee/thanks/'

CreateView

  • CreateView是由Model直接建立Form,跟FormView不同的是,不用預先設計ModelForm。

例如:

*** views.py ***
from django.views.generic.edit import CreateView
class NewCoffeeView2(CreateView):
model = Coffee
field = '__all__'
  • CreateView會依照app name以及model name自行去尋找template 檔案。
    如上面的例子,Template將會預設在 templates/mycafe/coffee_form.html 這個路徑。

UpdateView

  • UpdateView可以用來編輯已經存在的資料,用法與CreateView相同,只需要帶入 modelfield ,若Model並未定義 get_absolute_url,則需多加一個 success_url

例如:

*** views.py ***
from django.views.generic.edit import UpdateView
class CoffeeUpdateView(UpdateView):
model = Coffee
fields = ['name', 'price']
success_url = '/coffee/thanks/'

DeleteView

  • DeleteView顧名思義是將資料刪除的Generic view,當收到request method為POST時,則將該資料刪除,若收到GET則呈現此View。
  • 此View目的是提供一個確認的頁面,方便使用者確認刪除。
  • DeleteView只需要提供 model success_url
*** views.py ***
from django.urls import reverse_lazy
class CoffeeDeleteView(DeleteView):
model = Coffee
success_url = reverse_lazy('menu')
  • 另外,在使用DeleteView時,預設的Template是
    app_name\[model_name]_confirm_delete.html
    我們以在mycafe app底下的Coffee model為例,我們必須預先準備一個 templates\mycafe\coffee_confirm_delete.html,如下:
*** coffee_confirm_delete.html ***<form action="" method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object.name }}"?</p>
<input type="submit" value="Confirm" />
</form>

Generic date views

  • Generic data views 是用來處裡日期相關的資料。

ArchiveIndexView

  • ArchiveIndexView是所有Generic Date Views的最上層,直接在URL中使用,如下:
*** urls.py ***
from django.conf.urls import url
from django.views.generic.dates import ArchiveIndexView

from mycafe.models import Coffee

urlpatterns = [
url(r'^archive/$',
ArchiveIndexView.as_view(model=Coffee, date_field="pub_date"), name="coffee_archive"),
]
  • 在使用ArchiveIndexView之前,Model必須包含DateField()
*** models.py ***
from django.db import models
class Coffee(models.Model):
name = models.CharField(max_length=60)
price = models.IntegerField()
slug = models.SlugField(max_length=40)
pub_date = models.DateField()

YearArchiveView

  • YearArchiveView 讓使用者可以在URL中輸入特定的年份,並依據年份搜尋資料,且按照月份排序,預設是不支援未來的資料,若需顯示未來的資料,則將 allow_future 設定為 True 即可
  • 例如:
*** views.py ***
class CoffeeYearArchiveView(YearArchiveView):
queryset = Coffee.objects.all()
date_field = "pub_date"
make_object_list = True
allow_empty = True
  • 說明: 在ArchiveView中提供需要查詢的 queryset,以及欄位 date_field ,若需要將資料列出來,則將 make_object_list 設定為 True ;若當資料不存在時,仍然需要處裡,則將 allow_empty設定為 True
  • 同時,template的路徑為:
    templates\[app_name]\[model_name]_archive_year.html
  • URL的設定如下:
from django.conf.urls import url

from mycafe.views import CoffeeYearArchiveView

urlpatterns = [
url(r'^(?P<year>[0-9]{4})/$', CoffeeYearArchiveView.as_view()),
]

MonthArchiveView

  • MonthArchiveView 可以顯示特定月份的所有的資料。
  • 使用類似於YearArchiveView

WeekArchiveView

  • WeekArchiveView 可以顯示特定周的所有資料。
  • 使用類似於YearArchiveView

DayArchiveView

  • DayArchiveView 可以顯示特定日期的所有資料。
  • 使用類似於YearArchiveView

TodayArchiveView

  • TodayArchiveView 可以顯示今日的所有資料。
  • 與DayArchiveView完全相同,差別在於會自動帶入今日的日期。

小結

如同文章開始所說,開發網站的時候常常會有很多重複的工作,django的Generic view可以幫我們很大一個忙,節省很多時間在設計基本的項目。

不過Generic View也不是萬能的,畢竟只算是 “通用” 的View,所以有需要自己實做的項目,還是自己來比較實際。

--

--

Andy Lu

Android/Flutter developer, Kotlin Expert, like to learn and share.