django 1.8 官方文檔翻譯: 3-4-2 內(nèi)建顯示視圖

Django 文檔協(xié)作翻譯小組人手緊缺奥裸,有興趣的朋友可以加入我們险掀,完全公益性質(zhì)。

交流群:467338606

網(wǎng)站:http://python.usyiyi.cn/django/index.html

基于類的內(nèi)建通用視圖

編寫(xiě)Web應(yīng)用可能是單調(diào)的湾宙,因?yàn)槟阈枰粩嗟闹貜?fù)某一種模式迷郑。 Django嘗試從model和 template層移除一些單調(diào)的情況枝恋,但是Web開(kāi)發(fā)者依然會(huì)在view(視圖)層經(jīng)歷這種厭煩。

Django的通用視圖被開(kāi)發(fā)用來(lái)消除這一痛苦嗡害。它們采用某些常見(jiàn)的習(xí)語(yǔ)和在開(kāi)發(fā)過(guò) 程中發(fā)現(xiàn)的模式然后把它們抽象出來(lái),以便你能夠?qū)懜俚拇a快速的實(shí)現(xiàn)基礎(chǔ)的視圖畦攘。

我們能夠識(shí)別一些基礎(chǔ)的任務(wù)霸妹,比如展示對(duì)象的列表,以及編寫(xiě)代碼來(lái)展示任何對(duì)象的 列表知押。此外叹螟,有問(wèn)題的模型可以作為一個(gè)額外的參數(shù)傳遞到URLconf中。

Django通過(guò)通用視圖來(lái)完成下面一些功能:

  • 為單一的對(duì)象展示列表和一個(gè)詳細(xì)頁(yè)面台盯。 如果我們創(chuàng)建一個(gè)應(yīng)用來(lái)管理會(huì)議罢绽,那么 一個(gè) TalkListView (討論列表視圖)和一個(gè) RegisteredUserListView ( 注冊(cè)用戶列表視圖)就是列表視圖的一個(gè)例子。一個(gè)單獨(dú)的討論信息頁(yè)面就是我們稱 之為 "詳細(xì)" 視圖的例子静盅。
  • 在年/月/日歸檔頁(yè)面良价,以及詳細(xì)頁(yè)面和“最后發(fā)表”頁(yè)面中,展示以數(shù)據(jù)庫(kù)為基礎(chǔ)的對(duì)象蒿叠。
    允許用戶創(chuàng)建明垢,更新和刪除對(duì)象 -- 以授權(quán)或者無(wú)需授權(quán)的方式。

總的來(lái)說(shuō)市咽,這些視圖提供了一些簡(jiǎn)單的接口來(lái)完成開(kāi)發(fā)者遇到的大多數(shù)的常見(jiàn)任務(wù)痊银。

擴(kuò)展通用視圖

使用通用視圖可以極大的提高開(kāi)發(fā)速度,是毫無(wú)疑問(wèn)的施绎。 然而在大多數(shù)工程中溯革, 總會(huì)遇到通用視圖無(wú)法滿足需求的時(shí)候。的確谷醉,大多數(shù)來(lái)自Django開(kāi)發(fā)新手 的問(wèn)題是如何能使得通用視圖的使用范圍更廣致稀。

這是通用視圖在1.3發(fā)布中被重新設(shè)計(jì)的原因之一 - 之前,它們僅僅是一些函數(shù)視圖加上 一列令人疑惑的選項(xiàng)孤紧;現(xiàn)在豺裆,比起傳遞大量的配置到URLconf中,更推薦的擴(kuò)展通用視圖的 方法是子類化它們号显,并且重寫(xiě)它們的屬性或者方法臭猜。

這就是說(shuō),通用視圖有一些限制押蚤。如果你將你的視圖實(shí)現(xiàn)為通用視圖的子類蔑歌,你就會(huì)發(fā)現(xiàn)這樣能夠更有效地編寫(xiě)你想要的代碼,使用你自己的基于類或功能的視圖揽碘。

在一些三方的應(yīng)用中次屠,有更多通用視圖的示例园匹,或者你可以自己按需編寫(xiě)。

對(duì)象的通用視圖

TemplateView確實(shí)很有用劫灶,但是當(dāng)你需要 呈現(xiàn)你數(shù)據(jù)庫(kù)中的內(nèi)容時(shí)Django的通用視圖才真的會(huì)脫穎而出裸违。因?yàn)檫@是如此常見(jiàn) 的任務(wù),Django提供了一大把內(nèi)置的通用視圖本昏,使生成對(duì)象的展示列表和詳細(xì)視圖 的變得極其容易供汛。

讓我們來(lái)看一下這些通用視圖中的"對(duì)象列表"視圖。

我們將使用下面的模型:

# models.py
from django.db import models

class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()

    class Meta:
        ordering = ["-name"]

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Author(models.Model):
    salutation = models.CharField(max_length=10)
    name = models.CharField(max_length=200)
    email = models.EmailField()
    headshot = models.ImageField(upload_to='author_headshots')

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField('Author')
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()

現(xiàn)在我們需要定義一個(gè)視圖:

# views.py
from django.views.generic import ListView
from books.models import Publisher

class PublisherList(ListView):
    model = Publisher

最后將視圖解析到你的url上:

# urls.py
from django.conf.urls import url
from books.views import PublisherList

urlpatterns = [
    url(r'^publishers/$', PublisherList.as_view()),
]

上面就是所有我們需要寫(xiě)的Python代碼了涌穆。

注意

所以怔昨,當(dāng)(例如)DjangoTemplates后端的APP_DIRS選項(xiàng)在TEMPLATES中設(shè)置為T(mén)rue時(shí),模板的位置應(yīng)該為:/path/to/project/books/templates/books/publisher_list.html宿稀。

這個(gè)模板將會(huì)依據(jù)于一個(gè)上下文(context)來(lái)渲染趁舀,這個(gè)context包含一個(gè)名為object_list 包含所有publisher對(duì)象的變量。一個(gè)非常簡(jiǎn)單的模板可能看起來(lái)像下面這樣:

{% extends "base.html" %}

{% block content %}
    <h2>Publishers</h2>
    <ul>
        {% for publisher in object_list %}
            <li>{{ publisher.name }}</li>
        {% endfor %}
    </ul>
{% endblock %}

這確實(shí)就是全部代碼了祝沸。 所有通用視圖中有趣的特性來(lái)自于修改被傳遞到通用視圖中的"信息" 字典矮烹。generic views reference文檔詳細(xì) 介紹了通用視圖以及它的選項(xiàng);本篇文檔剩余的部分將會(huì)介紹自定義以及擴(kuò)展通用 視圖的常見(jiàn)方法奋隶。

編寫(xiě)“友好的”模板上下文

你可能已經(jīng)注意到了擂送,我們?cè)趐ublisher列表的例子中把所有的publisher對(duì)象 放到 object_list 變量中。雖然這能正常工作唯欣,但這對(duì)模板作者并不是 "友好的"嘹吨。他們只需要知道在這里要處理publishers就行了。

因此境氢,如果你在處理一個(gè)模型(model)對(duì)象蟀拷,這對(duì)你來(lái)說(shuō)已經(jīng)足夠了。 當(dāng)你處理 一個(gè)object或者queryset時(shí)萍聊,Django能夠使用你定義對(duì)象顯示用的自述名(verbose name问芬,或者復(fù)數(shù)的自述名,對(duì)于對(duì)象列表)來(lái)填充上下文(context)寿桨。提供添加到默認(rèn)的 object_list 實(shí)體中此衅,但是包含完全相同的數(shù)據(jù),例如publisher_list亭螟。

如果自述名(或者復(fù)數(shù)的自述名) 仍然不能很好的符合要求挡鞍,你 可以手動(dòng)的設(shè)置上下文(context)變量的名字。在一個(gè)通用視圖上的context_object_name屬性指定了要使用的定了上下文變量:

# views.py
from django.views.generic import ListView
from books.models import Publisher

class PublisherList(ListView):
    model = Publisher
    context_object_name = 'my_favorite_publishers'

提供一個(gè)有用的context_object_name總是個(gè)好主意预烙。和你一起工作的設(shè)計(jì) 模板的同事會(huì)感謝你的墨微。

添加額外的上下文

多數(shù)時(shí)候,你只是需要展示一些額外的信息而不是提供一些通用視圖扁掸。 比如翘县,考慮到每個(gè)publisher 詳細(xì)頁(yè)面上的圖書(shū)列表的展示最域。DetailView通用視圖提供了一個(gè)publisher對(duì)象給context,但是我們?nèi)绾卧谀0逯刑砑痈郊有畔⒛兀?/p>

答案是派生DetailView锈麸,并且在get_context_data方法中提供你自己的實(shí)現(xiàn)镀脂。默認(rèn)的實(shí)現(xiàn)只是簡(jiǎn)單的 給模板添加了要展示的對(duì)象,但是你這可以這樣覆寫(xiě)來(lái)展示更多信息:

from django.views.generic import DetailView
from books.models import Publisher, Book

class PublisherDetail(DetailView):

    model = Publisher

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super(PublisherDetail, self).get_context_data(**kwargs)
        # Add in a QuerySet of all the books
        context['book_list'] = Book.objects.all()
        return context

注意

通常來(lái)說(shuō)掐隐,get_context_data會(huì)將當(dāng)前類中的上下文數(shù)據(jù)狗热,合并到所有超類中的上下文數(shù)據(jù)。要在你自己想要改變上下文的類中保持這一行為虑省,你應(yīng)該確保在超類中調(diào)用了get_context_data。如果沒(méi)有任意兩個(gè)類嘗試定義相同的鍵僧凰,會(huì)返回異常的結(jié)果探颈。然而,如果任何一個(gè)類嘗試在超類持有一個(gè)鍵的情況下覆寫(xiě)它(在調(diào)用超類之后)训措,這個(gè)類的任何子類都需要顯式于超類之后設(shè)置它伪节,如果你想要確保他們覆寫(xiě)了所有超類的話。如果你有這個(gè)麻煩绩鸣,復(fù)查你視圖中的方法調(diào)用順序怀大。

查看對(duì)象的子集

現(xiàn)在讓我們來(lái)近距離查看下我們一直在用的 model參數(shù)。model參數(shù)指定了視圖在哪個(gè)數(shù)據(jù)庫(kù)模型之上進(jìn)行操作呀闻,這適用于所有的需要 操作一個(gè)單獨(dú)的對(duì)象或者一個(gè)對(duì)象集合的通用視圖化借。然而,model參數(shù)并不是唯一能夠指明視圖要基于哪個(gè)對(duì)象進(jìn)行操作的方法 -- 你同樣可以使用queryset參數(shù)來(lái)指定一個(gè)對(duì)象列表:

from django.views.generic import DetailView
from books.models import Publisher

class PublisherDetail(DetailView):

    context_object_name = 'publisher'
    queryset = Publisher.objects.all()

指定model = Publisher等價(jià)于快速聲明的queryset = Publisher.objects.all()捡多。然而蓖康,通過(guò)使用queryset來(lái)定義一個(gè)過(guò)濾的對(duì)象列表,你可以更加詳細(xì) 的了解哪些對(duì)象將會(huì)被顯示的視圖中(參見(jiàn)執(zhí)行查詢來(lái)獲取更多關(guān)于查詢集對(duì)象的更對(duì)信息垒手,以及參見(jiàn) 基于類的視圖參考來(lái)獲取全部 細(xì)節(jié))蒜焊。

我們可能想要對(duì)圖書(shū)列表按照出版日期進(jìn)行排序來(lái)選擇一個(gè)簡(jiǎn)單的例子,并且把 最近的放到前面:

from django.views.generic import ListView
from books.models import Book

class BookList(ListView):
    queryset = Book.objects.order_by('-publication_date')
    context_object_name = 'book_list'

這是個(gè)非常簡(jiǎn)單的列子科贬,但是它很好的詮釋了處理思路泳梆。 當(dāng)然,你通常想做的不僅僅只是 對(duì)對(duì)象列表進(jìn)行排序榜掌。如果你想要展現(xiàn)某個(gè)出版商的所有圖書(shū)列表优妙,你可以使用 同樣的手法:

from django.views.generic import ListView
from books.models import Book

class AcmeBookList(ListView):

    context_object_name = 'book_list'
    queryset = Book.objects.filter(publisher__name='Acme Publishing')
    template_name = 'books/acme_list.html'

注意,除了經(jīng)過(guò)過(guò)濾之后的查詢集唐责,一起定義的還有我們自定義的模板名稱鳞溉。如果我們不這么做,通過(guò)視圖會(huì)使用和 "vanilla" 對(duì)象列表名稱一樣的模板鼠哥,這可 能不是我們想要的熟菲。

另外需要注意看政,這并不是處理特定出版商的圖書(shū)的非常優(yōu)雅的方法。 如果我們 要?jiǎng)?chuàng)建另外一個(gè)出版商頁(yè)面抄罕,我們需要添加另外幾行代碼到URLconf中允蚣,并且再多幾個(gè) 出版商就會(huì)覺(jué)得這么做不合理。我們會(huì)在下一個(gè)章節(jié)處理這個(gè)問(wèn)題呆贿。

注意

如果你在訪問(wèn) /books/acme/時(shí)出現(xiàn)404錯(cuò)誤嚷兔,檢查確保你確實(shí)有一個(gè)名字為“ACME Publishing”的出版商。通用視圖在這種情況下?lián)碛幸粋€(gè)allow_empty 的參數(shù)做入。詳見(jiàn)基于類的視圖參考冒晰。

動(dòng)態(tài)過(guò)濾

另一個(gè)普遍的需求是在給定的列表頁(yè)面中根據(jù)URL中的關(guān)鍵字來(lái)過(guò)濾對(duì)象。 前面我們把出版 商的名字硬編碼到URLconf中竟块,但是如果我們想要編寫(xiě)一個(gè)視圖來(lái)展示任何publisher的所有 圖書(shū)壶运,應(yīng)該如何處理?

相當(dāng)方便的是浪秘, ListView 有一個(gè)get_queryset() 方法來(lái)供我們重寫(xiě)蒋情。在之前,它只是返回一個(gè)queryset屬性值耸携,但是現(xiàn)在我們可以添加更多的邏輯棵癣。

讓這種方式能夠工作的關(guān)鍵點(diǎn),在于當(dāng)類視圖被調(diào)用時(shí)夺衍,各種有用的對(duì)象被存儲(chǔ)在self上狈谊;同request()(self.request)一樣,其中包含了從URLconf中獲取到的位置參數(shù) (self.args)和基于名字的參數(shù)(self.kwargs)(關(guān)鍵字參數(shù))刷后。

這里的畴,我們擁有一個(gè)帶有一組供捕獲的參數(shù)的URLconf:

# urls.py
from django.conf.urls import url
from books.views import PublisherBookList

urlpatterns = [
    url(r'^books/([\w-]+)/$', PublisherBookList.as_view()),
]

接著,我們編寫(xiě)了PublisherBookList視圖::

# views.py
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from books.models import Book, Publisher

class PublisherBookList(ListView):

    template_name = 'books/books_by_publisher.html'

    def get_queryset(self):
        self.publisher = get_object_or_404(Publisher, name=self.args[0])
        return Book.objects.filter(publisher=self.publisher)

如你所見(jiàn)尝胆,在queryset區(qū)域添加更多的邏輯非常容易丧裁;如果我們想的話,我們可以 使用self.request.user來(lái)過(guò)濾當(dāng)前用戶含衔,或者添加其他更復(fù)雜的邏輯煎娇。

同時(shí)我們可以把出版商添加到上下文中,這樣我們就可以在模板中使用它:

# ...

def get_context_data(self, **kwargs):
    # Call the base implementation first to get a context
    context = super(PublisherBookList, self).get_context_data(**kwargs)
    # Add in the publisher
    context['publisher'] = self.publisher
    return context

執(zhí)行額外的工作

我們需要考慮的最后的共同模式在調(diào)用通用視圖之前或者之后會(huì)引起額外的開(kāi)銷贪染。

想象一下缓呛,在我們的Author對(duì)象上有一個(gè)last_accessed字段,這個(gè)字段用來(lái) 跟蹤某人最后一次查看了這個(gè)作者的時(shí)間杭隙。

# models.py
from django.db import models

class Author(models.Model):
    salutation = models.CharField(max_length=10)
    name = models.CharField(max_length=200)
    email = models.EmailField()
    headshot = models.ImageField(upload_to='author_headshots')
    last_accessed = models.DateTimeField()

通用的DetailView類哟绊,當(dāng)然不知道關(guān)于這個(gè)字段的事情,但我們可以很容易 再次編寫(xiě)一個(gè)自定義的視圖痰憎,來(lái)保持這個(gè)字段的更新票髓。

首先攀涵,我們需要添加作者詳情頁(yè)的代碼配置到URLconf中,指向自定義的視圖:

from django.conf.urls import url
from books.views import AuthorDetailView

urlpatterns = [
    #...
    url(r'^authors/(?P<pk>[0-9]+)/$', AuthorDetailView.as_view(), name='author-detail'),
]

然后洽沟,編寫(xiě)我們新的視圖 -- get_object是用來(lái)獲取對(duì)象的方法 -- 因此我們簡(jiǎn)單的 重寫(xiě)它并封裝調(diào)用:

from django.views.generic import DetailView
from django.utils import timezone
from books.models import Author

class AuthorDetailView(DetailView):

    queryset = Author.objects.all()

    def get_object(self):
        # Call the superclass
        object = super(AuthorDetailView, self).get_object()
        # Record the last accessed date
        object.last_accessed = timezone.now()
        object.save()
        # Return the object
        return object

注意

這里URLconf使用參數(shù)組的名字pk - 這個(gè)名字是DetailView用來(lái)查找主鍵的值的默認(rèn)名稱以故,其中主鍵用于過(guò)濾查詢集。

如果你想要調(diào)用參數(shù)組的其它方法裆操,你可以在視圖上設(shè)置pk_url_kwarg怒详。詳見(jiàn) DetailView參考。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踪区,一起剝皮案震驚了整個(gè)濱河市昆烁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缎岗,老刑警劉巖善玫,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異密强,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蜗元,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)或渤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人奕扣,你說(shuō)我怎么就攤上這事薪鹦。” “怎么了惯豆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵池磁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我楷兽,道長(zhǎng)地熄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任芯杀,我火速辦了婚禮端考,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘揭厚。我一直安慰自己却特,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布筛圆。 她就那樣靜靜地躺著裂明,像睡著了一般。 火紅的嫁衣襯著肌膚如雪太援。 梳的紋絲不亂的頭發(fā)上闽晦,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天扳碍,我揣著相機(jī)與錄音,去河邊找鬼尼荆。 笑死左腔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捅儒。 我是一名探鬼主播液样,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼巧还!你這毒婦竟也來(lái)了鞭莽?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤麸祷,失蹤者是張志新(化名)和其女友劉穎澎怒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體阶牍,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喷面,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了走孽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惧辈。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖磕瓷,靈堂內(nèi)的尸體忽然破棺而出盒齿,到底是詐尸還是另有隱情,我是刑警寧澤困食,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布边翁,位于F島的核電站,受9級(jí)特大地震影響硕盹,放射性物質(zhì)發(fā)生泄漏符匾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一莱睁、第九天 我趴在偏房一處隱蔽的房頂上張望待讳。 院中可真熱鬧,春花似錦仰剿、人聲如沸创淡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)琳彩。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間露乏,已是汗流浹背碧浊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘟仿,地道東北人箱锐。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像劳较,于是被迫代替她去往敵國(guó)和親驹止。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容