django開發系列:視圖高級之類視圖
類視圖
在寫視圖的時候,Django除了使用函數作爲視圖
,也可以使用類作爲視圖
。使用類視圖可以使用類的一些特性
,比如繼承
等。
View:django.views.generic.View是主要的類視圖。
所有的類視圖都是繼承於他。如果寫自己的類視圖,也可以繼承於他。
然後再根據當前請求的method,來實現不同的方法。
比如這個視圖只能使用get的方式來請求,那麼就可以在這個類中定義get(self,request,*args,**kwargs)方法。以此類推,如果只需要實現post方法,那麼就只需要在類中實現post(self,request,*args,**kwargs)。
from django.views import View
class BookDetailView(View):
def get(self,request,*args,**kwargs):
return render(request,'detail.html')
類視圖寫完後,還應該在urls.py中進行映射
,映射的時候就需要調用View的類方法as_view()
來進行轉換。
urlpatterns = [
path("detail/<book_id>/",views.BookDetailView.as_view(),name='detail')
]
如果向傳遞參數
,可以如下操作
# urls.py
urlpatterns = [
path('index/<id>', views.Index.as_views(), name = 'index')
]
# views.py
class Index(View):
def get(self, id, *args, **kwargs):
pass
除了get方法,View還支持以下方法[‘get’,‘post’,‘put’,‘patch’,‘delete’,‘head’,‘options’,‘trace’]。
如果用戶訪問了View中沒有定義的方法。比如你的類視圖只支持get方法,而出現了post方法,會出現405錯誤
。那麼就會把這個請求轉發給http_method_not_allowed(request,*args,**kwargs)
。示例代碼如下:
class Index(View):
def post(self,request,*args,**kwargs):
return HttpResponse("post!")
def http_method_not_allowed(self, request, *args, **kwargs):
return HttpResponse("當前採用的method是:{}".format(request.method))
urls.py中的映射如下:
path("idnex/",views.Index.as_view(),name='add_book')
如果你在瀏覽器中訪問index/,因爲瀏覽器訪問採用的是get方法,而index/只支持post方法,因此以上視圖會當前採用的method是:GET
其實不管是get請求還是post請求,都會走dispatch(request,*args,**kwargs)方法,所以如果實現這個方法,將能夠對所有請求都處理到。
TemplateView
django.views.generic.TemplateView,這個類視圖是專門用來返回模版的。
如果渲染的這個模版不需要傳遞任何的參數,那麼建議在urls中使用TemplateView
# urls.py
from django.vies.generic import TemplateView
urlpatterns = [
path('', TemplateView.as_view(template_name='about.html'))
]
在這個類中,有兩個屬性是經常需要用到的
template_name,這個屬性是用來存儲模版的路徑,TemplateView會自動的渲染這個變量指向的模版。
get_context_data,這個方法是用來返回上下文數據的,也就是在給模版傳的參數的。
# myapp.views.py
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = "home.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["name"] = "jack"
return context
在urls.py中的映射代碼如下:
from django.urls import path
from myapp.views import HomePageView
urlpatterns = [
path('', HomePageView.as_view()),
]
ListView
在網站開發中,經常會出現需要列出某個表中的一些數據作爲列表展示出來。比如文章列表,圖書列表等等。可以使用ListView
# views.py,跟下面的視圖函數是否同一個app看自己需要
from django.views.generic import ListView
class ArticleListView(ListView):
model = Article # 指定模型
template_name = 'article_list.html' # 執行渲染模板
paginate_by = 10 # 指定列表顯示多少條數據
context_object_name = 'articles' # 指定這個列表模型在模板中的上下文參數名稱
ordering = 'create_time' # 指定這個列表的排序方式
page_kwarg = 'page' # 獲取第幾頁的數據的參數名稱(?page=xxx)。默認是`page`。
def get_context_data(self, **kwargs): # 獲取上下文的數據。
context = super(ArticleListView, self).get_context_data(**kwargs)
print(context)
return context
def get_queryset(self): # 如果你提取數據的時候,並不是要把所有數據都返回,那麼你可以重寫這個方法。將一些不需要展示的數據給過濾掉
# 默認 return Article.objects.all()
return Article.objects.filter(id__lte=89) # 過濾
models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
create_time = models.DateTimeField(auto_now_add=True)
views.py
from django.shortcuts import render
from django.http import HttpResponse
from .models import Article
def add_article(request):
articles = []
for x in range(0,102):
article = Article(title='標題: %s'%x, content='內容: %s'%x)
articles.append(article)
Article.objects.bulk_create(articles)
return HttpResponse("yes")
urls.py
urlpatterns = [
path('add/', views.add_article)
path('about/', views.ArticleListView.as_view())
article_list.html
<body>
<ul>
{% for article in articles %}
<li> {{ article.title }} </li>
{% endfor %}
</ul>
</body>
Paginator和Page類
Paginator和Page類都是用來做分頁的。他們在Django中的路徑爲django.core.paginator.Paginator和django.core.paginator.Page。
在上面的ListView的def get_context_data)中的context = super(ArticleListView, self).get_context_data(* *kwargs),print(context)打印出來的鍵值對就有上述類
Paginator屬性
count:總共有多少條數據。
num_pages:總共有多少頁。
page_range:頁面的區間。比如有三頁,那麼返回range(1,4)。
class ArticleListView(ListView):
model = Article
template_name = "article.html"
paginate_by = 25
context_object_name = "articles"
ordering = "create_time"
page_kwarg = "page"
def get_context_data(self, *, object_list=None, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
paginator = context.get("paginator") # 鍵是paginator
print("paginator.count: {}\npaginator.num_pages: {}\npaginator.page_range: {}\n".format(paginator.count, paginator.num_pages, paginator.page_range))
return context
def get_queryset(self):
return Article.objects.all()
輸出
paginator.count: 103
paginator.num_pages: 5
paginator.page_range: range(1, 6)
1
2
3
Page常用屬性和方法
has_next():是否還有下一頁。
has_previous():是否還有上一頁。
next_page_number():下一頁的頁碼。最後一頁會拋出異常raise EmptyPage((‘That page contains no results’))
previous_page_number():上一頁的頁碼。 第一頁使用會拋出異常raise EmptyPage((‘That page number is less than 1’))
number:當前頁。
start_index:當前這一頁的第一條數據的索引值。
end_index:當前這一頁的最後一條數據的索引值。
代碼
class ArticleListView(ListView):
model = Article
template_name = "article.html"
paginate_by = 25
context_object_name = "articles"
ordering = "create_time"
page_kwarg = "page"
def get_context_data(self, *, object_list=None, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
# paginator = context.get("paginator")
# print("paginator.count: {}\npaginator.num_pages: {}\npaginator.page_range: {}\n".format(paginator.count, paginator.num_pages, paginator.page_range))
# print(context)
page = context.get("page_obj") # 鍵是page_obj
print(page.has_next())
print(page.has_previous())
print(page.next_page_number())
print(page.previous_page_number())
print(page.number)
print(page.start_index())
print(page.end_index())
return context
def get_queryset(self):
return Article.objects.all()
使用URLl訪問附帶了查詢字符串?page=2因爲默認第一頁的話page.previous_page_number()會拋異常。
True # 是否有下一頁
True # 是否有上一頁
3 # 後一頁頁數
1 # 前一頁頁數
2 # 第幾頁
26 #第二頁的第一個索引
50 #第二頁的最後一個索引