第一個Django應用程序_part3
一、概述
此文延續第一個Django應用程序part2。
官方文檔:https://docs.djangoproject.com/en/1.11/intro/tutorial03/
view是Django應用程序中的“類”網頁,它通常使用一個特定的函數提供服務,並且具有一個特定的模版。例如在博客應用程序中,kennel有以下視圖:
- 博客首頁-顯示最新的幾個條目。
- 博客“詳細”頁面 --單篇博客的固定鏈接頁面。
- 基於年份的存檔頁面-顯示給定年份的所有月份。
- 基於月份的存檔頁面-顯示所有日期與給定月份的條目。
- 基於日的存檔頁面-顯示給定日期中的所有條目。
- 評論動作-處理對給定條目的發布評論。
在我們的polls應用程序中,我們將擁有以下四個視圖:
- 問題“索引”頁面-顯示最新的幾個問題。
- 問題“詳細信息”頁面detail-顯示單個Question的具體內容,不顯示該議題的當前投票結果,而是提供一個投票的表單。
- 問題“結束”頁面detail -顯示特定問題的結果。
- 投票功能vote-處理特點問題中特定選擇的投票。
在Django中,網頁和其它內容有視圖提供。每個視圖都由一個簡單的Python函數(或基於類視圖的方法)來表示。Django將通過檢查所請求的URL(確切的說,域名後的URL部分),來選擇一個視圖。
平日你上網時,可能會遇到像 “ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”這樣 "優美" 的URL。很高興是,Django允許我們使用更加優雅的URL模式。
網址格式只是URL的一般形式,例如:/newsarchive/<year>/<month>/
。
要從URL到視圖,Django使用所謂的“URLconfs”。URLconf將URL模式(描述為正則表達式)映射到視圖。
Django.urls更多信息:https://docs.djangoproject.com/en/1.11/ref/urlresolvers/#module-django.urls
二、添加視圖
現在我們再添加一些視圖到polls/views.py。
def index(request): return HttpResponse("Hello,world.You‘re at the polls index.") def detail(request,question_id): return HttpResponse("Your‘re lokking 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))
通過添加以下url()調用,將這些新views連接到polls/urls.py模塊中。
from django.conf.urls import url from . import views urlpatterns = [ #ex: /polls/ url(r‘^$‘,views.index,name=‘index‘), #ex: /polls/5/ url(r‘^(?P<question_id>[0-9]+)/$‘,views.detail,name=‘detail‘), #ex: /polls/5/results/ url(r‘^(?P<question_id>[0-9]+)/results/$‘,views.results,name=‘results‘), #ex: /polls/5/vote/ url(r‘^(?P<question_id>[0-9]+)/vote/$‘,views.vote,name=‘vote‘), ]
在瀏覽器中查看"/polls/34/"。它將運行detail()方法,並顯示您在URL中提供的任何ID(Your‘re lokking at question 34.)。
例如:"/polls/34/results/"。瀏覽器顯示結果:You‘re looking at the results of question 34.
"/polls/34/vote/"。瀏覽器顯示結果:You‘re voting on question 34.
當有人從您的網站請求一個頁面-例如"/polls/34/"時,Django將加載mysite.urls Python模塊,因為它被ROOT_URLCONF設置指向。它找到名為urlpatterns的變量,並按照順序遍歷正則表達式。在‘^polls/‘找到匹配後,它將取消匹配的文本("polls/"),並發送剩余的文本-"34/"-到‘polls.urls‘URLconf進行進一步處理。它匹配r‘^(<?Pquestion_id>[0-9]+)/$‘,請求detail()視圖的結果就像下面一樣:
detail(request=<HttpRequest object>, question_id=‘34‘)
question_id = ‘34‘部分來自(?P<question_id>[0-9]+)。使用模式周圍的括號"捕獲"該模式匹配的文本,並將其作為參數發送給視圖函數;?P<question_id>定義待處理的question_id;
[0-9]+是匹配數字的正則表達式。
由於其網址格式是正則表達式,所以您可以對他們做什麽,實際上沒有限制。而且不需要添加URL cruft,例如.html-除非你想,在這種情況下你可以這樣做:
url(r‘^polls/latest\.html$‘, views.index),
但是,不要這樣做。太愚蠢了。
三、編寫實際執行某些操作的視圖
每個視圖負責執行以下兩項操作之一:返回包含所請求頁面內容的HttpResponse對象,或引發例如Http404等異常。
你的views可以從數據庫讀取記錄。它可以使用Django的模版系統,也可以使用第三方Python模版系統。它可以生成PDF文件,輸出XML,隨時創建一個ZIP文件,任何你想要的,使用任何你想要的Python庫。
Django想要的就是這個HttpResponse。或者是一個例外。
這是一個新的index()視圖,它顯示系統中最新的5個polls問題,用逗號分隔,根據發布日期:
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)
# Leave the rest of the views (detail, results, vote) unchanged
這裏有一個問題:頁面的設計在視圖中是硬編碼。如果要更改頁面的樣式,則必須編輯此Python代碼。所以讓我們使用Django模版系統通過創建視圖可以使用的模版來將設計與Python分開。
首先,在polls目錄中創建一個名為templates的目錄。Django會在那裏尋找模版。
您的項目的TEMPLATES設置描述了Django如何價值和呈現模版。默認設置文件匹配APP_DIRS選擇設置為True的DjangoTemplates後端。按照慣例DjangoTemplates在INSTALLES_APPS中查找"templates"子目錄。
在剛剛創建的模版目錄中,創建另一名為polls的目錄,並在其中創建一個名為index.html的文件。換句話說,你的模版應該在polls/templates/polls/index.html。由於app_directories模版加載器的工作原理如上所述,您可以將Django中此模版簡單地稱為polls/index.html。
- 模版命名空間
現在,我們可以直接放在polls/templates中(而不是創建另一個polls子目錄),但是它會其實是一個糟糕的主意。Django將選擇其找到的第一個模版,其名稱與之匹配,如果您在不同的應用程序匯總具有相同的名稱模版,則Django將無法區分他們。我們需要能夠將Django指向正確的位置,而通過命名空間來確保這一點的最簡單的方法。也就是說,將這些模版放在另一個應用程序命名的目錄中。
將以下代碼放在該模版中:
{% 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 .models import Question from django.template import loader 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的模版,並傳遞一個上下文。上下文是將模版變量名名稱映射到Python對象的字典。
通過將瀏覽器指向"/polls/"來加載頁面,您應該看到包含第一個Django應用程序part2 中的"What‘s up"問題的項目符號列表。鏈接指向問題的詳細頁面。
四、快捷方式:render()
通常的做法是用上下文加載模版,然後返回渲染後的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
(HttpResponse
如果您仍然具有,和的存根方法detail
, 您將需要保留)。
render()函數將請求對象作為其第一個參數,模版名作為第二個參數,並將字典作為其可選的第三個參數。它返回使用給定上下文呈現的給定模版的HttpResponse對象。
五、觸發404錯誤
現在,我們來處理問題詳細視圖(question detail view)-顯示給定投票的問題文本的頁面,以下是視圖:
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})
如果請求的ID的問題不存在,則該視圖引發了Http404異常。
我們將在稍後討論可以放在該polls/detail.html
模板中的內容,但如果您想快速獲得上述示例的工作方式,則只需包含以下內容的文件:
polls/templates/polls/detail.html
{{ question }}
六、快捷方式:get_object_or_404()
如果對象不存在,使用get()觸發Http404是一個很常見的情況。Django提供了一個快捷方式。這裏是detail()視圖,重寫如下:
- 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_object_or_404()而不是自動捕獲ObjectDoesNotExist更高級別的異常,或者使用模型API Http404而不是ObjectDoesNotExist?
因為那會將模型圖層耦合到視圖圖層。Django最重要的設計目標之一是保持松耦合。django.shortcuts模塊引入了一些受控耦合。
還有一個get_list_or_404()函數,除了使用filter()而不是get()之外,它可以像get_object_or_404()。如果列表為空,它會觸發Http404。
七、模版系統
返回到我們的polls應用程序的detail()視圖。給定上下文變量question,以下是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模版中寫入一個question的鏈接時,鏈接部分硬件編碼如下:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
這種硬編碼,緊密耦合的方法的問題是,在具有大量模版的項目上更改URL變得具有挑戰性。但是,由於您在polls.urls模塊中的url()函數中定義了name參數,因此可以刪除對url配置中定義的特定URL路徑的依賴使用{% url %}模版標簽:
<li><a href="{% url ‘detail‘ question.id %}">{{ question.question_text }}</a></li>
這種方式是通過查找polls.urls模塊中指定的URL定義。您可以看到下面定義了"detail"的URL名稱:
url(r‘^(?P<question_id>[0-9]+)/$‘, views.detail, name=‘detail‘),
如果您想將polls詳細信息視圖的URL更改為其它內容,可能polls/specifics/12/不是在模版(或模版)中進行,而是將其更改為polls/urls.py:
url(r‘^specifics/(?P<question_id>[0-9]+)/$‘, views.detail, name=‘detail‘),
名稱空間URL名稱
在項目中只有一個應用程序polls。在實際的Django項目中,可能有五、十、二十個甚至更多個應用。Django如何區分他們之間的URL名稱?例如:polls應用程序具有detail視圖,同一項目中的應用程序也可能是一個博客。當使用{% url %}時,Django會如何知道哪個應用程序視圖為url創建模版標簽?
解決的方法是為您的URLconf添加命名空間。在polls/url.py文件中,繼續添加一個app_name來設置應用程序命名空間:
from django.conf.urls import url from . import views 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模版:
polls/templates/polls/index.html
<li><a href="{% url ‘detail‘ question.id %}">{{ question.question_text }}</a></li>
指向帶有命名空間的detail視圖:
polls/templates/polls/index.html
<li><a href="{% url ‘polls:detail‘ question.id %}">{{ question.question_text }}</a></li>
第一個Django應用程序_part3