django 的類檢視和函式檢視-雜談

娃哈哈店長發表於2020-01-07
  • 在django很老的版本時候,只有function-based views,但問題是是基於函式的檢視太過於簡單,很難去擴充,自定義它們,沒法達到檢視重用的地步。

  • 為了解決這個問題,class-based views誕生了。所以,現在的django有基於函式或者基於類這兩種檢視。

url和檢視關係

一般先開啟瀏覽器,然後在瀏覽器的位址列裡輸入一個網址,也就是URL,然後回車,我們就可以在瀏覽器裡看到這個網址返回的內容。這是我們能看得見的過程,還有一些我們看不見的過程,那就是:當我們在瀏覽器裡輸入網址(URL)時,回車,然後瀏覽器就會向目標網址傳送一個HTTP請求,伺服器收到請求之後就會給這個請求做出一個響應,這個響應就是把對應的內容透過瀏覽器渲染出來,呈現給我們看。

這個過程就是請求與響應:

簡單來說也就是

瀏覽器傳送請求給伺服器伺服器對請求進行處理再返回http相應最後返回html文件給前端

請求和響應

表現形式如下:

urlpatterns = [

    path(正規表示式, views檢視函式,引數,別名),

]

括號裡的引數說明:

1、一個正規表示式字串

2、一個可呼叫物件,通常為一個檢視函式或一個指定檢視函式路徑的字串

3、可選的要傳遞給檢視函式的預設引數(字典形式)

4、一個可選的name引數(別名)

比如我想構造三個URL,網站首頁(http://www.django.cn/)、新聞(http://www.django.cn/news/)、論壇(http://www.django.cn/bbs/),我們可以這麼做

urlpatterns = [

path('', views.index), #裡面留空,代表首頁

path('news/',views.news),#news

path('bbs/',views.bbs),#bbs

]

URL就是這麼構造的,我們的域名www.django.cn不需要寫,完整的URL應該要這麼寫:path(正規表示式, views檢視函式,引數,別名), 裡面的正規表示式, views檢視函式,是必須要寫的,而引數,別名是可選的。我們在有特殊需要的時候才寫。關於URL詳細介紹和使用方法可以檢視文章:路由配置系統URLconf

透過上面我們可以看到,每個URL都對應一個views檢視函式名,檢視函式名不能相同,否則會報錯。檢視函式,Django中約定寫在APP應用裡的views.py檔案裡。然後在urls.py檔案裡透過下面的方式匯入:

from APP應用名 import views

from APP應用名.vews import 函式名或類名

檢視函式是一個簡單的Python 函式,它接受Web請求並且返回Web響應。響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文件,或者一張圖片. . . 是任何東西都可以。無論檢視本身包含什麼邏輯,都要返回響應。這個檢視函式程式碼一般約定是放置在專案或應用程式目錄中的名為views.py的檔案中。

http請求中產生兩個核心物件:

1、http請求---->HttpRequest物件,使用者請求相關的所有資訊(物件)

2、http響應---->HttpResponse物件,響應字串

之前我們在歡迎頁面這章的時候有操作過一次。我們回顧一下:

首先,開啟開啟bolg目錄下的views.py檔案,寫一個hello檢視函式,在裡面輸入:

from django.http import HttpResponse

def hello(request):

   """

  寫一個hello函式,透過request接收URL或者說是http請求資訊,

  然後給這個請求返回一個HttpResponse物件

  """

    return HttpResponse('歡迎使用Django!')

例子裡,我們用到的request,就是HttpRequest物件。HttpResponse("歡迎使用Django!"),就是HttpRequest物件,它向http請求響應了一段字串物件。

我們開啟myblog目錄下的urls.py檔案中先匯入檢視函式,然後構造一個URL,程式碼如下:

from blog import views  #匯入檢視函式

urlpatterns = [

    ...

    path('', views.hello),   #這個是我們構造的URL

]

程式碼寫完之後,啟動專案就可以在瀏覽器裡看到檢視函式返回的字串"歡迎使用Django!"

每一個URL都會對應一個檢視函式,當一個使用者請求訪問Django站點的一個頁面時,然後就由Django路由系統(URL配置檔案)去決定要執行哪個檢視函式使用的演算法。

透過URL對應關係匹配 ->找到對應的函式(或者類)->返回字串(或者讀取Html之後返回渲染的字串)這個過程也就是我們Django請求的生命週期。

檢視函式,就是圍繞著HttpRequest和HttpResponse這兩個物件進行的。

類檢視和函式檢視的區別

函式檢視

以函式的方式定義的檢視稱為函式檢視,函式檢視便於理解。但是遇到一個檢視對應的路徑提供了多種不同HTTP請求方式的支援時,便需要在一個函式中編寫不同的業務邏輯,程式碼可讀性與複用性都不佳。

比如我之前網站就是用到的函式檢視:

from django.shortcuts import render

from apps.blog.models import Article, Category, Tag

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage

from django.http import Http404

from django.conf import settings

categories = Category.objects.all()  # 獲取全部的分類物件

tags = Tag.objects.all()  # 獲取全部的標籤物件

months = Article.objects.datetimes('pub_time', 'month', order='DESC')

# Create your views here.

def home(request):  # 主頁

    posts = Article.objects.filter(status='p', pub_time__isnull=False)  # 獲取全部(狀態為已釋出,釋出時間不為空)Article物件

    paginator = Paginator(posts, settings.PAGE_NUM)  # 每頁顯示數量

    page = request.GET.get('page')  # 獲取URL中page引數的值

    try:

        post_list = paginator.page(page)

    except PageNotAnInteger:

        post_list = paginator.page(1)

    except EmptyPage:

        post_list = paginator.page(paginator.num_pages)

    return render(request, 'home.html', {'post_list': post_list, 'category_list': categories, 'months': months})

def detail(request, id):

    try:

        post = Article.objects.get(id=str(id))

        post.viewed()  # 更新瀏覽次數

        tags = post.tags.all()

        next_post = post.next_article()  # 上一篇文章物件

        prev_post = post.prev_article()  # 下一篇文章物件

    except Article.DoesNotExist:

        raise Http404

    return render(

        request, 'post.html',

        {

            'post': post,

            'tags': tags,

            'category_list': categories,

            'next_post': next_post,

            'prev_post': prev_post,

            'months': months

        }

    )

def search_category(request, id):

    posts = Article.objects.filter(category_id=str(id))

    category = categories.get(id=str(id))

    paginator = Paginator(posts, settings.PAGE_NUM)  # 每頁顯示數量

    try:

        page = request.GET.get('page')  # 獲取URL中page引數的值

        post_list = paginator.page(page)

    except PageNotAnInteger:

        post_list = paginator.page(1)

    except EmptyPage:

        post_list = paginator.page(paginator.num_pages)

    return render(request, 'category.html',

                  {'post_list': post_list,

                   'category_list': categories,

                   'category': category,

                   'months': months

                  }

    )

def search_tag(request, tag):

    posts = Article.objects.filter(tags__name__contains=tag)

    paginator = Paginator(posts, settings.PAGE_NUM)  # 每頁顯示數量

    try:

        page = request.GET.get('page')  # 獲取URL中page引數的值

        post_list = paginator.page(page)

    except PageNotAnInteger:

        post_list = paginator.page(1)

    except EmptyPage:

        post_list = paginator.page(paginator.num_pages)

    return render(request, 'tag.html', {

        'post_list': post_list,

        'category_list': categories,

        'tag': tag,

        'months': months

        }

    )

def archives(request, year, month):

    posts = Article.objects.filter(pub_time__year=year, pub_time__month=month).order_by('-pub_time')

    paginator = Paginator(posts, settings.PAGE_NUM)  # 每頁顯示數量

    try:

        page = request.GET.get('page')  # 獲取URL中page引數的值

        post_list = paginator.page(page)

    except PageNotAnInteger:

        post_list = paginator.page(1)

    except EmptyPage:

        post_list = paginator.page(paginator.num_pages)

    return render(request, 'archive.html', {

        'post_list': post_list,

        'category_list': categories,

        'months': months,

        'year_month': year+'年'+month+'月'

        }

    )

但是後面在寫評論、登陸等等功能的時候,感覺業務邏輯處理的很亂,而且我也很迫切地想盡快把網站地功能做的更完善,就搞得幾天都摸不著頭緒不知道怎麼前進。也可能是我一開始寫的時候,沒有想那麼遠,後來越做越複雜了,在日後的開放中也感覺類檢視的方便。於是我就換成了類檢視:

import markdown

import time

from django.views import generic

from django.conf import settings

from django.utils.text import slugify

from django.shortcuts import render, HttpResponse, render_to_response

from django.views.decorators.csrf import csrf_exempt

from django.shortcuts import get_object_or_404, get_list_or_404

from .models import Article, Category, Tag

from markdown.extensions.toc import TocExtension  # 錨點的擴充

from django.core.cache import cache

# Create your views here.

class IndexView(generic.ListView):

    model = Article

    template_name = 'index.html'

    context_object_name = 'articles'

    paginate_by = getattr(settings, 'BASE_PAGE_BY', None)

    paginate_orphans = getattr(settings, 'BASE_ORPHANS', 0)

    def get_ordering(self):

        ordering = super(IndexView, self).get_ordering()

        sort = self.kwargs.get('sort')

        if sort == 'v':

            return ('-views', '-update_date', '-id')

        return ordering

class DetailView(generic.DetailView):

    model = Article

    template_name = 'article.html'

    context_object_name = 'article'

    def get_object(self):

        obj = super(DetailView, self).get_object()

        # 設定瀏覽量增加時間判斷,同一篇文章兩次瀏覽超過半小時才重新統計閱覽量,作者瀏覽忽略

        u = self.request.user

        ses = self.request.session

        the_key = 'is_read_{}'.format(obj.id)

        is_read_time = ses.get(the_key)

        if u != obj.author:

            if not is_read_time:

                obj.update_views()

                ses[the_key] = time.time()

            else:

                now_time = time.time()

                t = now_time - is_read_time

                if t > 60 * 30:

                    obj.update_views()

                    ses[the_key] = time.time()

        # 文章可以使用markdown書寫,帶格式的文章,像csdn寫markdown文章一樣

        # md = markdown.Markdown(extensions=[

        #     'markdown.extensions.extra',

        #     'markdown.extensions.codehilite',

        #     TocExtension(slugify=slugify),

        # ])

        # obj.body = md.convert(obj.body)

        # obj.toc = md.toc

        return obj

class CategoryView(generic.ListView):

    model = Article

    template_name = 'category.html'

    context_object_name = 'articles'

    paginate_by = getattr(settings, 'BASE_PAGE_BY', None)

    paginate_orphans = getattr(settings, 'BASE_ORPHANS', 0)

    def get_ordering(self):

        ordering = super(CategoryView, self).get_ordering()

        sort = self.kwargs.get('sort')

        if sort == 'v':

            return ('-views', '-update_date', '-id')

        return ordering

    def get_queryset(self, **kwargs):

        queryset = super(CategoryView, self).get_queryset()

        cate = get_object_or_404(Category, slug=self.kwargs.get('slug'))

        return queryset.filter(category=cate)

    def get_context_data(self, **kwargs):

        context_data = super(CategoryView, self).get_context_data()

        cate = get_object_or_404(Category, slug=self.kwargs.get('slug'))

        context_data['search_tag'] = '文章分類'

        context_data['search_instance'] = cate

        return context_data

def global_setting(request):

    return{

        "AUTHOR_NAME" : settings.AUTHOR_NAME,

        "AUTHOR_DESC" : settings.AUTHOR_DESC,

        "AUTHOR_EMAIL" : settings.AUTHOR_EMAIL,

        "AUTHOR_TITLE" : settings.AUTHOR_TITLE,

    }

def md2html(request):   #md2html工具頁

    return render(request, 'tools_html/md2html.html', 

        { 

        }

    )

除了個別的網站還是用的函式以外基本網站的主體都換成了類檢視。

他們在rul的配置上也是有區別的:

普通檢視函式:

from .views import index

urlpatterns = [  url(r'^index/$', index, name='index'),]

通用類檢視:

from .views import ClassListView

urlpatterns = [ url(r'^index/$', ClassListView.as_view(), name='index'),]

使用函式式圖的時候要定義多個views可以使用 from xx import xx as x1這樣可以避免檢視的重複

比如在model裡可以給類寫一個get_ordering函式然後根據前端給的引數不同在不同地來進行排序。

之前地介面中由於後端地功能性太少了,就導致前端裡地計算也很繁瑣。

反正當我開發評論功能地時候就亂成一鍋粥了。

  • 類檢視相比函式檢視的化重複性更高一些,使用函式式圖的時候總感覺只能render一個介面,導致後面網站更加複雜後,業務邏輯關係就很複雜了。

最後,感謝tendcode的幫助和解答。。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
文章!!首發於我的部落格Stray_Camel(^U^)ノ~YO

相關文章