django開發系列:視圖高級之類視圖

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 #第二頁的最後一個索引

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章