目錄
概覽
視圖是Django應用的網頁的“類型”,一般服務於特定的功能並且有特定的模板,在我們的投票應用中,我們有下面四個視圖:
- Question "index"頁面——展示最新的幾個問題。
- Question "detail"頁面——顯示問題內容,沒有結果但是有一個投票表單。
- Question "results"頁面——顯示一個特定問題的結果
- Vote "action"——處理在一個特定問題的進行具體的選擇
在Django中,Web頁面和其他內容是通過一個視圖提供的,每一個視圖由一個簡單的Python函數表示(或者是類方法),Django將解析被請求的Url去選擇一個視圖(准確地說,是url域名后面的部分)。
從一個url到一個視圖,Django使用所謂的‘Urlconfs’一個Urlconf映射Url模式到一個視圖,本教程提供了Urlconfs使用的基本介紹,你也可以參考django.urls獲得更多信息。
編寫視圖
在polls/views.py添加視圖:
def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
在polls.urls模塊中添加url映射:
from django.conf.urls import url from . import views urlpatterns = [ # 例: /polls/ url(r'^$', views.index, name='index'), # 例: /polls/5/ url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), # 例: /polls/5/results/ url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'), # 例: /polls/5/vote/ url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), ]
在瀏覽器打開“/polls/34”它將調用detail()方法並且顯示你在url中提供的ID,同樣的可以嘗試打開“/polls/34/results”和“/polls/34/vote”。
當有人請求你網站的一個頁面時,比如“/polls/34/”,Django將加載 mysite.urls Python模塊因為它指定在ROOT_URLCONF配置中(mysite/settings.py)。它查找名為urlpatterns的變量並且按順序遍歷正則表達式。然后發現匹配"^polls/",它會替換掉匹配的文本("polls/")並將剩余的文本-"34/"交給"polls.urls"的url配置做進一步處理。它匹配r'^(?P<question_id>[0-9]+)/$',結果調用detail()視圖:
detail(request=<HttpRequest object>, question_id='34')
這個question_id='34'部分來自(?P<question_id>[0-9]+)。使用括號通過這個模式在一個“捕獲”匹配的文本,並發送它給視圖函數作為一個參數。?P<question_id>將被用做匹配模式的標識名稱;[0-9]+匹配數字序列的正則表達式。
因為Url模式是正則表達式,所以格式沒有任何限制,比如我們設置一個.html后綴的地址:
url(r'^polls/latest\.html$', views.index),
但是沒必要這樣做。
編輯視圖實際做一些事情
每個視圖負責兩個事情之一,返回HttpResponse對象包含請求頁面的內容,或者拋出異常例如Http404。
你的視圖可能從數據庫讀取記錄,它可以使用一個模板系統例如Django的,-或者第三方Python模板系統。它可以產生一個PDF文件,輸出XML,創建一個ZIP文件,任何你想要的,都可以使用Python庫。
因為它的方便,讓我們使用Django自己的數據庫API,在第二部分教程中已經提到過了,在index()視圖,我們顯示最新的五條投票問題,以逗號分隔,按發布時間排序(polls/views.py):
from django.http import HttpResponse from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] output = ', '.join([q.question_text for q in latest_question_list]) return HttpResponse(output)
這兒有個問題,就是:頁面的設計你硬編碼到視圖中的。如果你想要改變頁面外觀,你就要編輯python代碼,讓我們來使用Django的模板系統創建一個模板把設計從Python代碼中分離出來。
首先在你的polls目錄來創建一個名叫templates的文件夾,Django將在這個路徑查找模板。
你的項目的TEMPLATES設置描述了Django將怎樣加載和渲染模板,默認的配置文件配置了一個DjangoTemplates 后端其APP_DIRS選項設置為True。按照慣例DjangoTemplates 查找每一個INSTALLED_APPS應用的子目錄“templates”。
在你剛剛創建的templates 目錄下創建另一個名叫polls的文件夾,並且創建一個名叫index.html的文件,也就是說,你的模板應該放在 polls/templates/polls/index.html
注意:現在我們可能把我們的模板文件直接放在polls/templates(而不是創建另外一個polls子目錄),但它實際上不是一個好主意。Django將選擇第一個名稱相匹配的模板,並且如果你已經有一個在不同應用名稱相同的模板,Django是無法區分他們的,我們需要一個正確的指向項目模板並確保他們有簡單的命名方式,也就是說通過將模板放置到templates的另一個目錄中。
在模板中輸入以下代碼polls/templates/polls/index.html:
{% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
現在讓我們修改在polls/views.py中修改index視圖去使用模板:
from django.http import HttpResponse from django.template import loader from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] template = loader.get_template('polls/index.html') context = { 'latest_question_list': latest_question_list, } return HttpResponse(template.render(context, request))
該代碼加載名為polls/index.html的模板並傳遞context變量,context是一個字典映射在模板的變量名。
通過在你的瀏覽器加載頁面“/polls/”,然后你應該可以看到Question的一個列表。
一個快捷方法:render()
它是一個加載模板非常常見的語法,添加一個context並返回呈現模板結果的
HttpResponse對象。Django提供一個快捷方法,現在我們來重寫index()視圖:
from django.shortcuts import render from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context)
注意我們所有的視圖都做到這一點的話,我們不在需要導入loader和HttpResponse。
這個render()函數以請求對象作為第一個參數,第二個參數是模板名稱和另外一個參數字典類作為可選參數。它返回一個指定上下文指定模板的HttpResponse對象。
拋出404錯誤
現在,讓我們解決問題的詳情視圖 - 這個頁面顯示指定投票的問題文本,編輯視圖polls/views.py:
from django.http import Http404 from django.shortcuts import render from .models import Question # ... def detail(request, question_id): try: question = Question.objects.get(pk=question_id) except Question.DoesNotExist: raise Http404("Question does not exist") return render(request, 'polls/detail.html', {'question': question})
這兒有個新概念:視圖拋出Http404異常如果一個問題的ID不存在的話。
為了快速的展示一下上面的示例,在模板templates/polls/detail.html中輸入簡單的代碼:
{{ question }}
一個快捷方法:get_object_or_404()
一個常用的語法去使用並且如果對象不存在拋出Http404異常。修改polls/views.py:
from django.shortcuts import get_object_or_404, render from .models import Question # ... def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question})
該get_object_or_404()包含一個Django模型參數和另外一個關鍵字參數,它傳遞到模型管理的get()函數,如果對象不存在拋出Http404。
還有一個函數get_list_or_404(),工作原理和get_object_or_404()是一樣的,使用filter()而不是get(),如果列表為空的話拋出404異常。
使用模板系統
回到我們投票應用的detail()視圖。給一個question的context變量,編輯polls/detail.html模板:
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul>
模板系統使用點查找的語法去訪問屬性變量,例如{{ question.question_text }},
首先Django在一個字典查找question對象,如果屬性查找失敗,它將嘗試列表索引查找。
方法調用發生在{% for %}循環:question.choice_set.all 將被解析為python代碼question.choice_set.all(),它返回一個Choice迭代對象並且試用於{% for %}模板標簽。
移除在模板中的硬編碼網址
請記住,當我們在polls/index.html 模板編寫一個鏈接時,這個鏈接是這樣寫的:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
這種硬編碼,緊密耦合的問題是,當我們的url需要變化時,將非常麻煩需要修改大量的模板。不過,既然你的polls.urls 中url()方法定義的有名稱,那么你可以使用{% url %}模板標簽:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
這種方式是通過查找在polls.urls模塊中定義的url來工作的,你可以准確的看到名為“detail”url定義如下:
... # 做為{% url %}模板標簽的“name”值 url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), ...
如果你想要修改投票的詳細視圖的url為其他的形式,也許像polls/specifics/12 ,你需要修改它在polls/urls.py:
... # 增加一個單詞 'specifics' url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'), ...
本教程的項目僅僅有一個應用,polls。在實際項目中,可能有五個,十個或者更多應用。Django如何區分他們之間的Url名稱?例如polls應用有一個detail視圖,並且可能同一個項目有一個應用是blog也有一個detail視圖。當使用{% url %} 模板標簽時Django如何知道url是哪個應用視圖的?
答案是在你的url配置中增加命名空間。在polls/urls.py文件中,增加一個app_name 去設置應用的命名空間
from django.conf.urls import url from . import views app_name = 'polls' urlpatterns = [ url(r'^$', views.index, name='index'), url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'), url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), ]
現在修改你的polls/index.html 模板,從:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
改成:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>