首頁 後端開發 Python教學 Django通用視圖講解

Django通用視圖講解

Jan 17, 2017 pm 02:13 PM
django

在最壞的情況下, 
Web 開發是一項無聊且單調的工作。到目前為止,我們已經介紹了 Django如何在模型和模板的層面上減少開發的單調性,但是 Web開發在視圖的層面上,也經歷著這種令人厭倦的事情。


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


在第八章簡單的向大家介紹了怎樣使視圖更加的「通用」。回顧一下,我們會發現一些比較常見的任務,例如顯示一系列對象,寫一段程式碼來顯示任何物件內容。解決方法就是傳遞一個額外的參數到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('',
('^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('',
('^about/$', direct_to_template, {
'template': 'about.html'
}),
**('^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對象,我們只需要簡單的回傳它就好了。有一個稍微複雜的地方,要處理沒有找到模板的情況。我們不希望一個不存在的模板引發伺服器錯誤,所以我們捕捉TempalteDoesNotExist異常並回傳404錯誤。


這裡有沒有安全性問題?


眼尖的讀者可能已經注意到一個可能的安全漏洞:我們直接使用從客戶端瀏覽器來的資料建構範本名稱(template="about/%s.html"%page)。乍看之下,這像是經典的目錄遍歷(directory
traversal)攻擊(詳情請看第十九章)。事實真是這樣嗎?


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


物件的通用視圖

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


讓我們先來看看其中一個通用的視圖:物件​​清單視圖。我們使用第五章中的Publisher來舉例:

class Publisher(models.Model):
name = models.CharField(maxlength=30)
address = models.CharField(maxlength=50)
city = models.CharField(maxlength=60)
state_province = models.CharField(maxlength=30)
country = models.CharField(maxlength=50)
website = models.URLField()
def __str__(self):
return self.name
class Meta:
ordering = ["-name"]
class Admin:
pass
登入後複製

要為所有的書籍創建一個列表頁面,我們使用下面的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視圖使用哪個模板,但是由於Django在不給定模板的時候會用物件的名稱推導出一個。在這個例子中,這個推導出的模板名稱將是"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 %}
登入後複製

这就是所有要做的事。要使用通用视图酷酷的特性只需要修改参数字典并传递给通用视图函数。附录D是通用视图的完全参考资料;本章接下来的章节将讲到自定义和扩展通用视图的一些方法。


扩展通用视图

毫无疑问,使用通用视图可以充分加快开发速度。然而,在多数的工程中,也会出现通用视图不能满足需求的情况。实际上,刚接触Django的开发者最常见的问题就是怎样使用通用视图来处理更多的情况。


幸运的是,几乎每种情况都有相应的方法来简单的扩展通用视图来处理它。这时总是使用下面的这些方法。


制作友好的模板Context

你也许已经注意到范例中的出版商列表模板在变量object_list里保存所有的书籍。这个方法工作的很好,只是对编写模板的人不太友好:他们不得不去了解他们现在处理的数据是什么,比方说在这里是书籍。用象publisher_list这样的变量名会更好一点,这样变量的值看起来就很清楚了。


我们可以很容易的象下面这样修改template_object_name参数的名称:

publisher_info = {
"queryset" : Publisher.objects.all(),
**"template_object_name" : "publisher",**
}
urlpatterns = patterns(&#39;&#39;,
(r&#39;^publishers/$&#39;, list_detail.object_list, publisher_info)
)
登入後複製

使用有用的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永远不能被缓存,通用视图在渲染前都做了缓存清除工作。


解决这个问题的办法是在extra_context中用一个回调(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的解释请看第五章的
“选择对象”章节,详细资料请参看附录C)。


举一个简单的例子,我们打算对书籍列表按出版日期排序,最近的排在最前:

book_info = {
"queryset" : Book.objects.all().order_by("-publication_date"),
}
urlpatterns = patterns(&#39;&#39;,
(r&#39;^publishers/$&#39;, list_detail.object_list, publisher_info),
**(r&#39;^books/$&#39;, list_detail.object_list, book_info),**
)
登入後複製

这是一个相当简单的例子,但是很说明问题。当然,你通常还想做比重新排序更多的事。如果你想要呈现某个特定出版商出版的所有书籍列表,你可以使用同样的技术:

**apress_books = {**
**"queryset": Book.objects.filter(publisher__name="Apress Publishing"),**
**"template_name" : "books/apress_list.html"**
**}**
urlpatterns = patterns(&#39;&#39;,
(r&#39;^publishers/$&#39;, list_detail.object_list, publisher_info),
**(r&#39;^books/apress/$&#39;, list_detail.object_list, apress_books),**
)
登入後複製

注意在使用一个过滤的queryset的同时,我们还使用一个自定义的模板名称。如果我们不这么做,通用视图就会用以前的模板,这可能不是我们想要的结果。


同样要注意的是这并不是一个处理出版商相关书籍的最好方法。如果我们想要添加另一个出版商页面,我们就得在URL配置中写URL配置,如果有很多的出版商,这个方法就不能接受了。在接下来的章节我们将来解决这个问题。


备注


如果你在请求/books/apres/时出现404错误,请检查以确保你的数据库中出版商中有名为Apress
Publishing的记录。通用视图有一个allow_empty参数可以用来处理这个情况,详情请看附录D。


用函数包装来处理复杂的数据过滤

另一个常见的需求是按URL里的关键字来过滤数据对象。在前面我们用在URL配置中硬编码出版商名称的方法来做这个,但是我们想要用一个视图就能显示某个出版商的所有书籍该怎么办呢?我们可以通过对object_list通用视图进行包装来避免写一大堆的手工代码。按惯例,我们先从写URL配置开始:

urlpatterns = patterns(&#39;&#39;,
(r&#39;^publishers/$&#39;, list_detail.object_list, publisher_info),
**(r&#39;^books/(w+)/$&#39;, books_by_publisher),**
)
登入後複製

接下来,我们写books_by_publisher这个视图:(上面的代码中正则表达式有误,在 ‘w’前要加反斜杠)

from django.http import Http404
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&#39;t be found).
try:
publisher = Publisher.objects.get(name__iexact=name)
except Publisher.DoesNotExist:
raise Http404
# 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 = "books",
extra_context = {"publisher" : publisher}
)
登入後複製

这是因为通用视图就是Python函数。和其他的视图函数一样,通用视图也是接受一些参数并返回HttpResponse对象。因此,通过包装通用视图函数可以做更多的事。


注意


注意到在前面这个例子中我们在extra_context传递了当前出版商这个参数。这在包装时通常是一个好注意;它让模板知道当前显示内容的上一层对象。


处理额外工作

我们再来看看最后一个常用模式:在调用通用视图前后做些额外工作。


想象一下我们在Author对象里有一个last_accessed字段,我们用这个字段来更正对author的最近访问时间。当然通用视图object_detail并不能处理这个问题,我们可以很容易的写一个自定义的视图来更新这个字段。


首先,我们需要在URL配置里设置指向到新的自定义视图:

from mysite.books.views import author_detail
urlpatterns = patterns(&#39;&#39;,
#...
**(r&#39;^authors/(?P<author_id>d+)/$&#39;, author_detail),**
)
登入後複製

接下来写包装函数:

import datetime
from mysite.books.models import Author
from django.views.generic import list_detail
from django.shortcuts import get_object_or_404
def author_detail(request, author_id):
# Look up the Author (and raise a 404 if she&#39;s not found)
author = get_object_or_404(Author, pk=author_id)
# Record the last accessed date
author.last_accessed = datetime.datetime.now()
author.save()
# Show the detail page
return list_detail.object_detail(
request,
queryset = Author.objects.all(),
object_id = author_id,
)
登入後複製

注意


除非你添加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通用视图讲解的内容,更多相关内容请关注PHP中文网(www.php.cn)!


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

怎麼看django版本 怎麼看django版本 Dec 01, 2023 pm 02:25 PM

查看django版本步驟:1、開啟終端機或命令提示字元視窗;2、確保已經安裝了Django,如果沒有安裝Django,可以使用套件管理工具來進行安裝,輸入pip install django命令即可;3、安裝完成後,可以使用python -m django --version來查看Django的版本。

Django vs. Flask:Python Web框架的比較分析 Django vs. Flask:Python Web框架的比較分析 Jan 19, 2024 am 08:36 AM

Django和Flask都是PythonWeb框架中的佼佼者,它們都有著自己的優點和適用場景。本文將對這兩個框架進行比較分析,並提供具體的程式碼範例。開發簡介Django是一個全功能的Web框架,它的主要目的是為了快速開發複雜的Web應用。 Django提供了許多內建的功能,例如ORM(物件關聯映射)、表單、認證、管理後台等。這些功能使得Django在處理大型

Django框架的優點和缺點:您需要知道的一切 Django框架的優點和缺點:您需要知道的一切 Jan 19, 2024 am 09:09 AM

Django是一個完整的開發框架,該框架涵蓋了Web開發生命週期的各個方面。目前,這個框架是全球最受歡迎的Web框架之一。如果你打算使用Django來建立自己的Web應用程序,那麼你需要了解Django框架的優點和缺點。以下是您需要知道的一切,包括具體程式碼範例。 Django優點:1.快速開發-Djang可以快速開發Web應用程式。它提供了豐富的庫和內

如何升級Django版本:步驟與注意事項 如何升級Django版本:步驟與注意事項 Jan 19, 2024 am 10:16 AM

如何升級Django版本:步驟和注意事項,需要具體程式碼範例引言:Django是一個功能強大的PythonWeb框架,它持續地進行更新和升級,以提供更好的效能和更多的功能。然而,對於使用較舊版Django的開發者來說,升級Django可能會面臨一些挑戰。本文將介紹如何升級Django版本的步驟和注意事項,並提供具體的程式碼範例。一、備份項目檔案在升級Djan

django是前端還是後端 django是前端還是後端 Nov 21, 2023 pm 02:36 PM

django是後端。詳細介紹:儘管Django主要是後端框架,但它與前端開發密切相關。透過Django的模板引擎、靜態檔案管理和RESTful API等功能,前端開發人員可以與後端開發人員合作,共同建立功能強大、可擴展的Web應用程式。

django版本區別是什麼 django版本區別是什麼 Nov 20, 2023 pm 04:33 PM

差異是:1、Django 1.x系列:這是Django的早期版本,包括1.0、1.1、1.2、1.3、1.4、1.5、1.6、1.7、1.8和1.9等版本。這些版本主要提供基本的Web開發功能;2、Django 2.x系列:這是Django的中期版本,包括2.0、2.1、2.2等版本;3、Django 3.x系列:這是Django的最新版本系列,包括3.0、3等版本。

怎麼查看django版本 怎麼查看django版本 Nov 30, 2023 pm 03:08 PM

查看django版本的方法:1、透過命令列查看,在終端機或命令列視窗中輸入「python -m django --version」命令;2、在Python互動式環境中查看,輸入「import django print(django. get_version())」代碼;3、檢查Django專案的設定文件,找到名為INSTALLED_APPS的列表,其中包含已安裝的應用程式資訊。

Django是前端還是後端?一探究竟! Django是前端還是後端?一探究竟! Jan 19, 2024 am 08:37 AM

Django是一個由Python編寫的web應用框架,它強調快速開發和乾淨方法。儘管Django是web框架,但要回答Django是前端還是後端這個問題,需要深入理解前後端的概念。前端是指使用者直接和互動的介面,後端是指伺服器端的程序,他們透過HTTP協定進行資料的互動。在前端和後端分離的情況下,前後端程式可以獨立開發,分別實現業務邏輯和互動效果,資料的交

See all articles