多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

中國最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2

django教程

Django 通用視圖

閱讀 (2330)

這里需要再次回到本書的主題: 在最壞的情況下, Web 開發是一項無聊而且單調的工作。 到目前為止,我們已經介紹了 Django 怎樣在模型和模板的層面上減小開發的單調性,但是 Web 開發在視圖的層面上,也經歷著這種令人厭倦的事情。

Django的通用視圖 可以減少這些痛苦。 它抽象出一些在視圖開發中常用的代碼和模式,這樣就可以在無需編寫大量代碼的情況下,快速編寫出常用的數據視圖。 事實上,前面章節中的幾乎所有視圖的示例都可以在通用視圖的幫助下重寫。

在第八章簡單的向大家介紹了怎樣使視圖更加的“通用”。 回顧一下,我們會發現一些比較常見的任務,比如顯示一系列對象,寫一段代碼來顯示 任何 對象內容。 解決辦法就是傳遞一個額外的參數到URLConf。

Django內建通用視圖可以實現如下功能:

  • 完成常用的簡單任務: 重定向到另一個頁面以及渲染一個指定的模板。

  • 顯示列表和某個特定對象的詳細內容頁面。 第8章中提到的 event_list 和 entry_list 視圖就是列表視圖的一個例子。 一個單一的 event 頁面就是我們所說的詳細內容頁面。

  • 呈現基于日期的數據的年/月/日歸檔頁面,關聯的詳情頁面,最新頁面。 Django Weblogs (http://www.djangoproject.com/weblog/)的年、月、日的歸檔就是使用通用視圖 架構的,就像是典型的新聞報紙歸檔。

綜上所述,這些視圖為開發者日常開發中常見的任務提供了易用的接口。

使用通用視圖

使用通用視圖的方法是在URLconf文件中創建配置字典,然后把這些字典作為URLconf元組的第三個成員。 (對于這個技巧的應用可以參看第八章向視圖傳遞額外選項。)

例如,下面是一個呈現靜態“關于”頁面的URLconf:

from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template

urlpatterns = patterns('',
    (r'^about/$', direct_to_template, {
        'template': 'about.html'
    })
)

一眼看上去似乎有點不可思議,不需要編寫代碼的視圖! 它和第八章中的例子完全一樣:direct_to_template視圖僅僅是直接從傳遞過來的額外參數獲取信息并用于渲染視圖。

因為通用視圖都是標準的視圖函數,我們可以在我們自己的視圖中重用它。 例如,我們擴展 about例子,把映射的URL從 /about//修改到一個靜態渲染 about/.html 。 我們首先修改URL配置以指向新的視圖函數:

from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template
from mysite.books.views import about_pages

urlpatterns = patterns('',
    (r'^about/$', direct_to_template, {
        'template': 'about.html'
    }),
    (r'^about/(\w+)/$', about_pages),
)

接下來,我們編寫 about_pages 視圖的代碼:

from django.http import Http404
from django.template import TemplateDoesNotExist
from django.views.generic.simple import direct_to_template

def about_pages(request, page):
    try:
        return direct_to_template(request, template="about/%s.html" % page)
    except TemplateDoesNotExist:
        raise Http404()

在這里我們象使用其他函數一樣使用 direct_to_template 。 因為它返回一個HttpResponse對象,我們只需要簡單的返回它就好了。 這里唯一有點棘手的事情是要處理找不到模板的情況。 我們不希望一個不存在的模板導致一個服務端錯誤,所以我們捕獲TemplateDoesNotExist異常并且返回404錯誤來作為替代。

這里有沒有安全性問題?

眼尖的讀者可能已經注意到一個可能的安全漏洞: 我們直接使用從客戶端瀏覽器得到的數據構造模板名稱(template="about/%s.html" % page )。乍看起來,這像是一個經典的 目錄跨越(directory traversal) 攻擊(詳情請看第20章)。 事實真是這樣嗎?

完全不是。 是的,一個惡意的 page 值可以導致目錄跨越,但是盡管 page  從請求的URL中獲取的,但并不是所有的值都會被接受。 這就是URL配置的關鍵所在: 我們使用正則表達式 \w+ 來從URL里匹配 page ,而 \w 只接受字符和數字。 因此,任何惡意的字符 (例如在這里是點 . 和正斜線 / )將在URL解析時被拒絕,根本不會傳遞給視圖函數。

對象的通用視圖

direct_to_template 毫無疑問是非常有用的,但Django通用視圖最有用的地方是呈現數據庫中的數據。 因為這個應用實在太普遍了,Django帶有很多內建的通用視圖來幫助你很容易 地生成對象的列表和明細視圖。

讓我們先看看其中的一個通用視圖: 對象列表視圖。 我們使用第五章中的 Publisher 來舉例:

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()

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ['name']

要為所有的出版商創建一個列表頁面,我們使用下面的URL配置:

from django.conf.urls.defaults import *
from django.views.generic import list_detail
from mysite.books.models import Publisher

publisher_info = {
    'queryset': Publisher.objects.all(),
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info)
)

這就是所要編寫的所有Python代碼。 當然,我們還需要編寫一個模板。 我們可以通過在額外參數字典中包含一個template_name鍵來顯式地告訴object_list視圖使用哪個模板:

from django.conf.urls.defaults import *
from django.views.generic import list_detail
from mysite.books.models import Publisher

publisher_info = {
    'queryset': Publisher.objects.all(),
    'template_name': 'publisher_list_page.html',
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info)
)

在缺少template_name的情況下,object_list通用視圖將自動使用一個對象名稱。 在這個例子中,這個推導出的模板名稱將是 "books/publisher_list.html" ,其中books部分是定義這個模型的app的名稱, publisher部分是這個模型名稱的小寫。

這個模板將按照 context 中包含的變量 object_list 來渲染,這個變量包含所有的書籍對象。 一個非常簡單的模板看起來象下面這樣:

{% extends "base.html" %}

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

(注意,這里我們假定存在一個base.html模板,它和我們第四章中的一樣。)

這就是所有要做的事。 要使用通用視圖酷酷的特性只需要修改參數字典并傳遞給通用視圖函數。 附錄D是通用視圖的完全參考資料;本章接下來的章節將講到自定義和擴展通用視圖的一些方法。

擴展通用視圖

毫無疑問,使用通用視圖可以充分加快開發速度。 然而,在多數的工程中,也會出現通用視圖不能 滿足需求的情況。 實際上,剛接觸Django的開發者最常見的問題就是怎樣使用通用視圖來處理更多的情況。

幸運的是,幾乎每種情況都有相應的方法來簡易地擴展通用視圖以處理這些情況。 這時總是使用下面的 這些方法。

制作友好的模板Context

你也許已經注意到范例中的出版商列表模板在變量 object_list 里保存所有的書籍。這個方法工作的很好,只是對編寫模板的人不太友好。 他們必須知道這里正在處理的是書籍。 更好的變量名應該是publisher_list,這樣變量所代表的內容就顯而易見了。

我們可以很容易地像下面這樣修改 template_object_name 參數的名稱:

from django.conf.urls.defaults import *
from django.views.generic import list_detail
from mysite.books.models import Publisher

publisher_info = {
    'queryset': Publisher.objects.all(),
    'template_name': 'publisher_list_page.html',
    'template_object_name': 'publisher',
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info)
)

在模板中,通用視圖會通過在template_object_name后追加一個_list的方式來創建一個表示列表項目的變量名。

使用有用的 template_object_name 總是個好想法。 你的設計模板的合作伙伴會感謝你的。

添加額外的Context

你常常需要呈現比通用視圖提供的更多的額外信息。 例如,考慮一下在每個出版商的詳細頁面顯示所有其他出版商列表。 object_detail 通用視圖為context提供了出版商信息,但是看起來沒有辦法在模板中 獲取 所有 出版商列表。

這是解決方法: 所有的通用視圖都有一個額外的可選參數 extra_context 。這個參數是一個字典數據類型,包含要添加到模板的context中的額外的對象。 所以要給視圖提供所有出版商的列表,我們就用這樣的info字典:

publisher_info = {
    'queryset': Publisher.objects.all(),
    'template_object_name': 'publisher',
    'extra_context': {'book_list': Book.objects.all()}
}

這樣就把一個 {{ book_list }} 變量放到模板的context中。 這個方法可以用來傳遞任意數據 到通用視圖模板中去,非常方便。 這是非常方便的

不過,這里有一個很隱蔽的BUG,不知道你發現了沒有?

我們現在來看一下, extra_context 里包含數據庫查詢的問題。 因為在這個例子中,我們把Publisher.objects.all() 放在URLconf中,它只會執行一次(當URLconf第一次加載的時候)。 當你添加或刪除出版商,你會發現在重啟Web服務器之前,通用視圖不會反映出這些修改(有關QuerySet何時被緩存和賦值的更多信息請參考附錄C中“緩存與查詢集”一節)。

備注

這個問題不適用于通用視圖的 queryset 參數。 因為Django知道有些特別的 QuerySet 永遠不能 被緩存,通用視圖在渲染前都做了緩存清除工作。

解決這個問題的辦法是在 _extracontext 中用一個回調(callback)來代替使用一個變量。 任何傳遞給extra_context的可調用對象(例如一個函數)都會在每次視圖渲染前執行(而不是只執行一次)。 你可以象這樣定義一個函數:

def get_books():
    return Book.objects.all()

publisher_info = {
    'queryset': Publisher.objects.all(),
    'template_object_name': 'publisher',
    'extra_context': {'book_list': get_books}
}

或者你可以使用另一個不是那么清晰但是很簡短的方法,事實上 Publisher.objects.all 本身就是可以調用的:

publisher_info = {
    'queryset': Publisher.objects.all(),
    'template_object_name': 'publisher',
    'extra_context': {'book_list': Book.objects.all}
}

注意 Book.objects.all 后面沒有括號;這表示這是一個函數的引用,并沒有真正調用它(通用視圖將會在渲染時調用它)。

顯示對象的子集

現在讓我們來仔細看看這個 queryset 。 大多數通用視圖有一個queryset參數,這個參數告訴視圖要顯示對象的集合 (有關QuerySet的解釋請看第五章的 “選擇對象”章節,詳細資料請參看附錄B)。

舉一個簡單的例子,我們打算對書籍列表按出版日期排序,最近的排在最前:

book_info = {
    'queryset': Book.objects.order_by('-publication_date'),
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info),
    (r'^books/$', list_detail.object_list, book_info),
)

這是一個相當簡單的例子,但是很說明問題。 當然,你通常還想做比重新排序更多的事。 如果你想要呈現某個特定出版商出版的所有書籍列表,你可以使用同樣的技術:

apress_books = {
    'queryset': Book.objects.filter(publisher__name='Apress Publishing'),
    'template_name': 'books/apress_list.html'
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info),
    (r'^books/apress/$', list_detail.object_list, apress_books),
)

注意 在使用一個過濾的 queryset 的同時,我們還使用了一個自定義的模板名稱。 如果我們不這么做,通用視圖就會用以前的模板,這可能不是我們想要的結果。

同樣要注意的是這并不是一個處理出版商相關書籍的最好方法。 如果我們想要添加另一個 出版商頁面,我們就得在URL配置中寫URL配置,如果有很多的出版商,這個方法就不能 接受了。 在接下來的章節我們將來解決這個問題。

用函數包裝來處理復雜的數據過濾

另一個常見的需求是按URL里的關鍵字來過濾數據對象。 之前,我們在URLconf中硬編碼了出版商的名字,但是如果我們想用一個視圖就顯示某個任意指定的出版商的所有書籍,那該怎么辦呢? 我們可以通過對object_list 通用視圖進行包裝來避免 寫一大堆的手工代碼。 按慣例,我們先從寫URL配置開始:

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info),
    (r'^books/(\w+)/$', books_by_publisher),
)

接下來,我們寫 books_by_publisher 這個視圖:

from django.shortcuts import get_object_or_404
from django.views.generic import list_detail
from mysite.books.models import Book, Publisher

def books_by_publisher(request, name):

    # Look up the publisher (and raise a 404 if it can't be found).
    publisher = get_object_or_404(Publisher, name__iexact=name)

    # Use the object_list view for the heavy lifting.
    return list_detail.object_list(
        request,
        queryset = Book.objects.filter(publisher=publisher),
        template_name = 'books/books_by_publisher.html',
        template_object_name = 'book',
        extra_context = {'publisher': publisher}
    )

這樣寫沒問題,因為通用視圖就是Python函數。 和其他的視圖函數一樣,通用視圖也是接受一些 參數并返回HttpResponse 對象。 因此,通過包裝通用視圖函數可以做更多的事。

注意

注意在前面這個例子中我們在 extra_context中傳遞了當前出版商這個參數。

處理額外工作

我們再來看看最后一個常用模式:

想象一下我們在 Author 對象里有一個 last_accessed 字段,我們用這個字段來記錄最近一次對author的訪問。 當然通用視圖 object_detail 并不能處理這個問題,但是我們仍然可以很容易地編寫一個自定義的視圖來更新這個字段。

首先,我們需要在URL配置里設置指向到新的自定義視圖:

from mysite.books.views import author_detail

urlpatterns = patterns('',
    # ...
    (r'^authors/(?P<author_id>\d+)/$', author_detail),
    # ...
)

接下來寫包裝函數:

import datetime
from django.shortcuts import get_object_or_404
from django.views.generic import list_detail
from mysite.books.models import Author

def author_detail(request, author_id):
    # Delegate to the generic view and get an HttpResponse.
    response = list_detail.object_detail(
        request,
        queryset = Author.objects.all(),
        object_id = author_id,
    )

    # Record the last accessed date. We do this *after* the call
    # to object_detail(), not before it, so that this won't be called
    # unless the Author actually exists. (If the author doesn't exist,
    # object_detail() will raise Http404, and we won't reach this point.)
    now = datetime.datetime.now()
    Author.objects.filter(id=author_id).update(last_accessed=now)

    return response

注意

除非你添加 last_accessed 字段到你的 Author 模型并創建 books/author_detail.html 模板,否則這段代碼不能真正工作。

我們可以用同樣的方法修改通用視圖的返回值。 如果我們想要提供一個供下載用的 純文本版本的author列表,我們可以用下面這個視圖:

def author_list_plaintext(request):
    response = list_detail.object_list(
        request,
        queryset = Author.objects.all(),
        mimetype = 'text/plain',
        template_name = 'books/author_list.txt'
    )
    response["Content-Disposition"] = "attachment; filename=authors.txt"
    return response

這個方法之所以工作是因為通用視圖返回的 HttpResponse 對象可以象一個字典 一樣的設置HTTP的頭部。 隨便說一下,這個 Content-Disposition 的含義是 告訴瀏覽器下載并保存這個頁面,而不是在瀏覽器中顯示它。

下一章

在這一章我們只講了Django帶的通用視圖其中一部分,不過這些方法也適用于其他的 通用視圖。 附錄C詳細地介紹了所有可用的視圖,如果你想了解這些強大的特性,推薦你閱讀一下。

這本書的高級語法部分到此結束。 在下一章, 我們講解了Django應用的部署。

關閉
程序員人生
主站蜘蛛池模板: 偷拍区自拍区 | 欧美成人毛片在线视频 | 国产欧美曰韩一区二区三区 | 成人免费毛片一区二区三区 | 亚洲欧美日韩精品永久在线 | 一级毛片在线 | 国产一级毛片外aaaa | 亚洲最新黄色网址 | 亚洲成人黄色 | 久久久久久久久久久9精品视频 | 另类小说综合 | 成人在线播放av | 国产一区二区三区福利 | 欧美人马交 | 欧美最猛黑人xxxx黑人猛交 | 亚洲伊人成人网 | 自拍 欧美 日韩 | c看欧美激情毛片 | 在线天堂中文字幕 | 岛国一区二区 | 国产精品自产拍在线观看 | 日韩天天摸天天澡天天爽视频 | 波多野结衣视频免费在线观看 | 亚洲综合区图片小说区 | 欧美日韩一区二区三区免费 | 日本国产中文字幕 | 免费国产h视频在线观看 | 日本一级淫片aaaaaa | 波多野结衣中文一区二区免费 | 在线观看 中文字幕 | 免费观看欧美一级毛片 | 在线午夜影院 | 久久国产精品久久国产精品 | 伊人久久综合网亚洲 | 印度美女freesex性hd | 亚欧综合 | 免费播放春色aⅴ视频 | 高清一区在线 | 中文字幕巨大乳在线看 | 最近无中文字幕视频 | 欧美专区亚洲 |