Photo by Austin Distel on Unsplash
在一般的Django專案開發,可以看到Django應用程式(APP)下的views.py檔案中,依照功能的需求撰寫了許多的檢視函式(View Function),這種寫法就稱為函式導向的檢視(Function-based Views),雖然較為直覺,但是隨著Django專案的規模越來越大,這樣的寫法就像是在Python專案中寫了許多的函式(Function)一樣,對於未來的維護及擴充性,較不理想。所以,這時候就會使用Python物件導向(OOP)的概念,將許多的函式(Function)模組化為類別(Class)及方法(Method),來提高程式碼的重用性及維護性。
Django也是相同的道理,它提供了許多內建的檢視類別,讓開發人員能夠藉由覆寫(Overriding) 的方式,將函式導向的檢視(Function-based Views),封裝成類別導向的檢視(Class-based Views),不但具有物件導向(OOP)的優點外,Django在背後也幫我們做了許多的事情,讓程式碼的寫法能夠更加簡潔。
因此,本文將利用5個常用的Django檢視類別,來實作待辦清單(To Do List)網站,藉此與函式導向的檢視(Function-based Views)寫法作比較,重點包含:
- 前置作業
- Django ListView(清單檢視類別)
- Django CreateView(新增檢視類別)
- Django UpdateView(修改檢視類別)
- Django DeleteView(刪除檢視類別)
- Django DetailView(內容檢視類別)
一、前置作業
在進行今天的實作前,先來說明一下本文所使用的待辦清單(To Do List)資料模型,如下範例:
from django.db import models
from django.utils import timezone
class Todo(models.Model):
title = models.CharField(max_length=100) #標題
finish = models.BooleanField(default=False) #是否完成
created = models.DateField(default=timezone.now) #建立日期
所以有了資料模型後,就可以在forms.py檔案中,建立待辦清單(To Do List)所需的資料模型表單(ModelForm),如下範例:
from django import forms
from .models import Todo
class TodoModelForm(forms.ModelForm):
class Meta:
model = Todo
fields = ('title', 'finish')
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '待辦事項'})
}
二、Django ListView(清單檢視類別)
通常使用在查詢的功能。其中包含了許多相關的屬性(Attribute)及方法(Method),讓開發人員可以透過覆寫(Overriding)的方式,自訂所需的資料。
首先,開啟Django應用程式(APP)下的views.py檔案,引用及繼承Django ListView類別,接著,就可以利用覆寫(Overriding) model屬性,來指定查詢的資料模型,如下範例:
首先,開啟Django應用程式(APP)下的views.py檔案,引用及繼承Django ListView類別,接著,就可以利用覆寫(Overriding) model屬性,來指定查詢的資料模型,如下範例:
from django.shortcuts import render
from .models import Todo
from django.views.generic import (
ListView
)
class TodoListView(ListView):
model = Todo # Todo.objects.all()
這時候Django就會自動查詢Todo資料模型中,所有的資料,也就是第9行註解的效果。而如果有特定的資料篩選條件,則可以覆寫(Overriding) queryset屬性,如下範例:
from django.shortcuts import render
from .models import Todo
from django.views.generic import (
ListView
)
class TodoListView(ListView):
model = Todo
queryset = Todo.objects.filter(finish=False) # 指定查詢條件
範例中篩選尚未完成的待辦事項來顯示。接下來,要將查詢結果傳送到Django Template(樣板),可以使用template_name屬性來指定,如下範例:
from django.shortcuts import render
from .models import Todo
from django.views.generic import (
ListView
)
class TodoListView(ListView):
model = Todo
queryset = Todo.objects.filter(finish=False) # 指定查詢條件
template_name = 'todo/todo_list.html' #樣板路徑
這樣的寫法,就稱為類別導向的檢視(Class-based Views)寫法。完成後,開啟Django應用程式(APP)下的urls.py檔案,來進行網址的設定,如下範例:
from django.urls import path
from .views import (
TodoListView
)
app_name = 'todo'
urlpatterns = [
path('', TodoListView.as_view(), name='list'),
]
這裡和函式導向的檢視(Function-based Views)不一樣的地方是,Django類別導向的檢視(Class-based Views)使用as_view()來進行轉換。
開啟剛剛所指定的清單樣板(todo_list.html),透過迴圈將資料顯示出來,如下範例:
開啟剛剛所指定的清單樣板(todo_list.html),透過迴圈將資料顯示出來,如下範例:
{% extends 'base.html' %}
{% block content %}
<table class="table">
<thead>
<tr>
<th>待辦事項</th>
<th></th>
</tr>
</thead>
<tbody>
{% for todo in object_list %}
<tr>
<td>{{ todo.title }}</td>
<td style="text-align:right;">
<a href="{% url 'todo:update' todo.id %}" class="btn btn-success">修改</a>
<a href="{% url 'todo:delete' todo.id %}" class="btn btn-danger">刪除</a>
<a href="{% url 'todo:detail' todo.id %}" class="btn btn-info">明細</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
範例中可以看到Django類別導向的檢視(Class-based Views)預設將查詢結果的物件以object_list傳入樣板中。
三、Django CreateView(新增檢視類別)
在新增資料時,所使用的檢視類別,包含了傳遞表單、表單驗證及儲存資料等方法(Method),同樣可以透過覆寫(Overriding)的方式來進行客製化。
由於本文的新增資料表單放置在清單樣板(todo_list.html)中,所以在Django ListView(清單檢視類別)中,需傳遞資料模型表單(ModelForm)至清單樣板(todo_list.html),而傳遞的方式可以透過覆寫(Overriding) get_context_data()方法來達成,如下範例:
由於本文的新增資料表單放置在清單樣板(todo_list.html)中,所以在Django ListView(清單檢視類別)中,需傳遞資料模型表單(ModelForm)至清單樣板(todo_list.html),而傳遞的方式可以透過覆寫(Overriding) get_context_data()方法來達成,如下範例:
from django.shortcuts import render
from .models import Todo
from django.views.generic import (
ListView
)
class TodoListView(ListView):
model = Todo
queryset = Todo.objects.filter(finish=False) # 指定查詢條件
template_name = 'todo/todo_list.html' #樣板路徑
# 要傳遞的資料
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"] = TodoModelForm() #資料模型表單
return context
接著,建立Django CreateView(新增檢視類別),指定儲存時,所要新增的資料模型及儲存成功後所要導向的網址,如下範例:
class TodoCreateView(CreateView):
model = Todo
form_class = TodoModelForm # 使用的表單類別
success_url = '/todo' # 儲存成功後要導向的網址
只要寫這幾行程式碼,Django就會在儲存時,驗證及取得表單資料,並且寫入資料庫中,非常的強大。
開啟Django應用程式(APP)下的urls.py檔案,加入Django CreateView(新增檢視類別)網址,如下範例:
開啟Django應用程式(APP)下的urls.py檔案,加入Django CreateView(新增檢視類別)網址,如下範例:
from django.urls import path
from .views import (
TodoListView,
TodoCreateView
)
app_name = 'todo'
urlpatterns = [
path('', TodoListView.as_view(), name='list'),
path('create', TodoCreateView.as_view(), name='create'),
]
在清單樣板(todo_list.html)中,建立新增功能的表單,並且在action的地方指定儲存後所要導向的網址,如下範例:
{% extends 'base.html' %}
{% block content %}
<form action="/todo/create" method="POST">
{% csrf_token %}
<div class="row">
<div class="col">
{{ form.title }}
</div>
<div class="col">
<input type="submit" class="btn btn-primary" value="儲存">
</div>
</div>
</form>
<br />
<table class="table">
<thead>
<tr>
<th>待辦事項</th>
<th></th>
</tr>
</thead>
<tbody>
{% for todo in object_list %}
<tr>
<td>{{ todo.title }}</td>
<td style="text-align:right;">
<a href="{% url 'todo:update' todo.id %}" class="btn btn-success">修改</a>
<a href="{% url 'todo:delete' todo.id %}" class="btn btn-danger">刪除</a>
<a href="{% url 'todo:detail' todo.id %}" class="btn btn-info">明細</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
四、Django UpdateView(修改檢視類別)
主要用於修改資料的功能,用法和Django CreateView(新增檢視類別)大同小異,如下範例:
class TodoUpdateView(UpdateView):
model = Todo
form_class = TodoModelForm # 使用的表單類別
template_name = 'todo/todo_update.html' # 修改樣板
success_url = '/todo' # 儲存成功後要導向的網址
Django UpdateView(修改檢視類別),會利用網址所傳遞的pk(主鍵),找到該筆資料,並且顯示在修改樣板(todo_update.html)的表單中,除此之外,在修改完資料儲存時,同樣會驗證及取得表單資料,更新至資料庫中。
接著,開啟Django應用程式(APP)下的urls.py檔案,加入Django UpdateView(修改檢視類別)網址,如下範例:
接著,開啟Django應用程式(APP)下的urls.py檔案,加入Django UpdateView(修改檢視類別)網址,如下範例:
from django.urls import path
from .views import (
TodoListView,
TodoCreateView,
TodoUpdateView
)
app_name = 'todo'
urlpatterns = [
path('', TodoListView.as_view(), name='list'),
path('create', TodoCreateView.as_view(), name='create'),
path('update/<int:pk>', TodoUpdateView.as_view(), name='update'),
]
其中要特別注意的是,在修改功能的網址,主鍵參數一定要命名為pk,這也是在撰寫Django類別導向的檢視(Class-based Views)規範,否則會出現錯誤。
在修改樣板(todo_update.html)中,利用表單來顯示要修改的資料,如下範例:
在修改樣板(todo_update.html)中,利用表單來顯示要修改的資料,如下範例:
{% endblock %}{% extends 'base.html' %} {% block content %}
五、Django DeleteView(刪除檢視類別)
用在刪除資料的功能,使用方式和Django UpdateView(修改檢視類別)相似,指定刪除的資料模型、樣板及刪除成功後所要導向的網址,同樣會在使用者點擊確定刪除按鈕時,利用網址所傳入的主鍵參數(pk),來刪除資料,如下範例:
class TodoDeleteView(DeleteView):
model = Todo
template_name = 'todo/todo_delete.html' # 刪除樣板
success_url = '/todo' # 刪除成功後要導向的網址
而urls.py檔案的部分,加入Django DeleteView(刪除檢視類別)網址,如下範例:
from django.urls import path
from .views import (
TodoListView,
TodoCreateView,
TodoUpdateView,
TodoDeleteView
)
app_name = 'todo'
urlpatterns = [
path('', TodoListView.as_view(), name='list'),
path('create', TodoCreateView.as_view(), name='create'),
path('update/<int:pk>', TodoUpdateView.as_view(), name='update'),
path('delete/<int:pk>', TodoDeleteView.as_view(), name='delete'),
]
接著開啟刪除樣板(todo_delete.html),顯示要刪除的資料,讓使用者進行確認,如下範例:
{% endblock %}{% extends 'base.html' %} {% block content %} 是否確定刪除{{ object.title }}?
六、Django DetailView(內容檢視類別)
用來顯示特定資料的詳細內容,只需要覆寫(Overriding)資料模型(model)及樣板名稱(template_name)屬性即可,如下範例:
class TodoDetailView(DetailView):
model = Todo
template_name = 'todo/todo_detail.html'
開啟Django應用程式(APP)下的urls.py檔案,加入Django DetailView(內容檢視類別)網址,如下範例:
from django.urls import path
from .views import (
TodoListView,
TodoCreateView,
TodoUpdateView,
TodoDeleteView,
TodoDetailView
)
app_name = 'todo'
urlpatterns = [
path('', TodoListView.as_view(), name='list'),
path('create', TodoCreateView.as_view(), name='create'),
path('update/<int:pk>', TodoUpdateView.as_view(), name='update'),
path('delete/<int:pk>', TodoDeleteView.as_view(), name='delete'),
path('detail/<int:pk>', TodoDetailView.as_view(), name='detail'),
]
最後,建立內容樣板(todo_detail.html),來顯示資料的詳細內容,如下範例:
{% extends 'base.html' %} {% block content %}
- 標題:{{ object.title }}
- 是否完成:{{ object.finish }}
- 建立日期:{{ object.created|date:'Y/m/d' }}
七、小結
以上就是5個常用的Django Class-based Views(類別導向的檢視)使用方式,大家可以和[Django教學7]善用Django ModelForm快速開發CRUD應用程式教學文章中所使用的Function-based Views(函式導向的檢視)比較一下,可以看到程式碼簡潔了許多,不過相對的就有更多的規範需要遵守,但是「透過遵循Django所訂定的原則及覆寫(Overriding)的方式,讓專案的寫法與架構更趨近於一致,能夠大幅提升未來的維護性」。
如果您喜歡我的文章,請幫我按五下Like(使用Google或Facebook帳號免費註冊),支持我創作教學文章,回饋由LikeCoin基金會出資,完全不會花到錢,感謝大家。
如果您喜歡我的文章,請幫我按五下Like(使用Google或Facebook帳號免費註冊),支持我創作教學文章,回饋由LikeCoin基金會出資,完全不會花到錢,感謝大家。
GitHub網址:https://github.com/mikeku1116/django-class-based-todo
- Python學習資源整理
- [Django教學6]Django Template(樣板)整合Bootstrap實戰教學
- [Django教學12]利用輕量的Django tastypie來打造屬於自己的API
- [Django教學13]Django Allauth套件整合Google登入驗證實作教學
- [Python物件導向]Python封裝(Encapsulation)實用教學
- Python多型(Polymorphism)實用教學
- Python繼承(Inheritance)實用教學
- 淺談Python類別(Class)
- 3個必須瞭解的Python屬性觀念
- 解析Python物件導向設計的3種類型方法(Instance,Class,Static Method)
您好,謝謝撰寫優質的文章。
回覆刪除關於這一行 (create),我發現必須要加 tailing slash (/todo"/"),不然會被多 redirect 一次
success_url = '/todo/' # 儲存成功後要導向的網址
不知道這樣是不是正常的,
謝謝。
https://i.imgur.com/6PwkcFj.png
回覆刪除都照著做還是遇到一堆bug.... sucks
回覆刪除直接下載版主的github,可以省去打字的錯誤
回覆刪除https://github.com/mikeku1116/django-class-based-todo
sucks
回覆刪除