說是完結,馬上又開始寫進階篇了。
本章不會爲博客項目增加新功能,但是也同樣重要,因爲我們要學習高逼格的基於類的視圖。
什麼是類視圖
前面章節中寫的所有視圖都是基於函數的,即def
;而類視圖是基於類的,即class
。
有編程基礎的同學都知道,類是面向對象技術中非常重要的概念。具有複雜數據、功能的類,可以通過繼承輕而易舉的將自身特性傳遞給另一個類,從而實現代碼的高效複用。
相比以前的函數視圖,類視圖有以下優勢:
- HTTP方法(
GET
,POST
等)相關的代碼,可以通過方法而不是條件分支來組織 - 可以通過諸如mixins(多重繼承)之類的面向對象技術將代碼分解爲可重用組件
說的都是什麼意思?通過例子來感受一下。
列表
函數和類
假設我們有一個博客列表,列表既有GET方法、又有POST方法,那麼用視圖函數看起來像這樣:
views.py
def article_list_example(request):
"""處理GET請求"""
if request.method == 'GET':
articles = ArticlePost.objects.all()
context = {'articles': articles}
return render(request, 'article/list.html', context)
而在類視圖中,則變爲這樣:
views.py
from django.views import View
class ArticleListView(View):
"""處理GET請求"""
def get(self, request):
articles = ArticlePost.objects.all()
context = {'articles': articles}
return render(request, 'article/list.html', context)
從本質上講,基於類的視圖允許你使用不同的類實例方法(即上面的def get()
)響應不同的HTTP請求方法,而不需要使用條件分支代碼。這樣做的好處是把不同的HTTP請求都分離到獨立的函數中,邏輯更加清晰,並且方便複用。
需要注意的是,因爲Django的URL解析器希望將請求發送到函數而不是類,所以類視圖有一個 as_view()
方法,該方法返回一個函數,當請求匹配關聯模式的URL時,則調用該函數。
即,視圖函數的url原本寫爲:
urls.py
...
urlpatterns = [
path('...', views.article_list_example, name='...'),
]
類視圖的url需改寫爲:
urls.py
...
urlpatterns = [
path('...', views.ArticleListView.as_view(), name='...'),
]
通用視圖
像列表這樣的功能在web開發中是很常見的,開發者會一遍又一遍寫幾乎相同的列表邏輯。Django的通用視圖正是爲緩解這種痛苦而開發的。它們對常用模式進行抽象,以便你快速編寫公共視圖,而無需編寫太多代碼。
因此用列表通用視圖改寫如下:
views.py
from django.views.generic import ListView
class ArticleListView(ListView):
# 上下文的名稱
context_object_name = 'articles'
# 查詢集
queryset = ArticlePost.objects.all()
# 模板位置
template_name = 'article/list.html'
列表繼承了父類ListView
,也就獲得了父類中的處理列表的方法,因此你可以看到,我們在自己的類中沒有寫任何處理的邏輯,僅僅是賦值了幾個變量而已。
動態過濾
從數據庫中篩選特定的內容也是常見的需求,類視圖如何實現呢?
你可能想到了,將上面代碼中改爲queryset = ArticlePost.objects.filter()
就可以了。
除此之外,更好的辦法是覆寫get_queryset()
方法:
views.py
...
class ArticleListView(ListView):
context_object_name = 'articles'
template_name = 'article/list.html'
def get_queryset(self):
"""
查詢集
"""
queryset = ArticlePost.objects.filter(title='Python')
return queryset
例子中只是過濾出標題爲“Python”的文章而已,有些大材小用了;但是你可以在get_queryset()
中寫複雜的聯合查詢邏輯,滿足個性化的功能。
添加上下文
在博客列表的設計時,我們返回給模板的上下文除了articles
以外,還有很多額外的信息,如order
、search
;在類視圖中同樣可以實現,改寫get_context_data()
方法即可:
views.py
...
class ArticleListView(ListView):
...
def get_context_data(self, **kwargs):
# 獲取原有的上下文
context = super().get_context_data(**kwargs)
# 增加新上下文
context['order'] = 'total_views'
return context
除此之外,ListView
還有些別的方法可以覆寫,深入瞭解可以看這裏:官方文檔
混入類
混入類(Mixin)是指具有某些功能、通常不獨立使用、提供給其他類繼承功能的類。嗯,就是“混入”的字面意思。
前面的列表視圖中已經有get_context_data()
方法了。假設需要寫一個功能類似的視頻列表,就可以用Mixin來避免重複代碼:
views.py
...
class ContextMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['order'] = 'total_views'
return context
class ArticleListView(ContextMixin, ListView):
...
class VideoListView(ContextMixin, ListView):
...
通過混入,兩個子類都獲得了get_context_data()
方法。
從語法上看,混入是通過多重繼承實現的。有區別的是,Mixin是作爲功能添加到子類中的,而不是作爲父類。
實際上Django內置了很多通用的Mixin類,實現了大部分常用的功能,點這裏深入瞭解:官方文檔
詳情頁
既然列表都有通用視圖,詳情頁當然也有對應的DetailView
。
用類視圖寫一個簡單的詳情頁:
views.py
from django.views.generic import DetailView
class ArticleDetailView(DetailView):
queryset = ArticlePost.objects.all()
context_object_name = 'article'
template_name = 'article/detail.html'
然後配置url:
urls.py
...
urlpatterns = [
# 詳情類視圖
path('detail-view/<int:pk>/', views.ArticleDetailView.as_view(), name='...'),
]
注意這裏傳入的參數不是id
而是pk
,這是視圖的要求(也可以傳入slug
)。pk
是數據表的主鍵,在默認情況下其實就是id
。
這就寫好了!
也可以添加任何別的功能,比如統計瀏覽量:
views.py
...
class ArticleDetailView(DetailView):
...
def get_object(self):
"""
獲取需要展示的對象
"""
# 首先調用父類的方法
obj = super(ArticleDetailView, self).get_object()
# 瀏覽量 +1
obj.total_views += 1
obj.save(update_fields=['total_views'])
return obj
方法get_object()
的作用是獲取需要展示的對象。首先調用父類方法,將這個對象賦值給obj
變量,然後再對其進行統計瀏覽量的操作,最後將對象返回。相當於在原有的方法中把自己的邏輯“塞”了進去。
關於DetailView
更多特性看這裏:官方文檔
編輯
除了能夠展示信息,通用視圖還包含CreateView
、UpdateView
、DeleteView
等編輯數據的類。
如果要新建文章,則視圖可以這麼寫:
views.py
from django.views.generic.edit import CreateView
class ArticleCreateView(CreateView):
model = ArticlePost
fields = '__all__'
# 或者只填寫部分字段,比如:
# fields = ['title', 'content']
template_name = 'article/create_by_class_view.html'
創建create_by_class_view.html
文件(目錄在哪,你應該已經很清楚了),寫入:
create_by_class_view.html
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
最後添加url:
urls.py
urlpatterns = [
path('create-view/', views.ArticleCreateView.as_view(), name='...'),
]
雖然外觀簡陋(這不是重點),但現在這個視圖確實已經能夠創建新文章了!
UpdateView
和DeleteView
這裏就不再贅述了,以後用到的地方再進行講解。
想提前瞭解的同學戳這裏:官方文檔
總結
有沒有感受到代碼隔離和繼承的強大?沒有?以後的章節會逐漸使用類編寫視圖,你會慢慢體會的。
類視圖的內容非常豐富,短短一篇文章只能蜻蜓點水而已。讀者在編程中遇到困難了,官方文檔是你最好的教程。
如果你有耐心從頭到尾閱讀類視圖的官方文檔,那當然是最好的了。
- 有疑問請在杜賽的個人網站留言,我會盡快回復。
- 或Email私信我:[email protected]
- 項目完整代碼:Django_blog_tutorial
轉載請告知作者並註明出處。