#編寫你的第一個 Django app,第三部分(Page 8)轉載請註明連接地址 本頁教程接前面的第二部分。咱們繼續開發 web-poll app,咱們會專一於建立公共接口上 —— 「視圖」。html
##概述 在你的 Django app中,視圖是一個 web 頁面的類型,通常服務於一個特定的函數,並擁有一個特定的模板(template)。例如:在 blog app中,你可能有下面這些視圖:python
在咱們的 poll app中,咱們會用到下面四個視圖web
在 Django 中,web 頁面和其餘內容被視圖投遞,每個視圖至關於一個簡單的 Python 函數(或在基於類的視圖中是方法)。Django 會檢查被請求的 URL 來選擇一個視圖(確切的講,是URL 中域名以後的部分)。數據庫
你在網絡中可能遇到過相似「ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B」這樣的URL。你會很高興的知道 Django 容許比它更優雅的 URL 模式。 URL 模式只是 URL 的通常形式,—— 例如 /newsarchive/<year>/<month>/
. 從一個 URL 中獲取一個視圖,Django 使用咱們學過的 「URLconf」。 URLconf 映射 URL 到視圖。 本教程提供了使用 URLconfs 的基本指令,你能夠在 URL dispatcher
(這裏少一個連接) 中瞭解更多信息。django
如今讓咱們在 polls/views.py
中在多添加一些視圖,這些視圖會有些許的不一樣,他們都接收一個一樣的參數:後端
# 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)
經過在 path()添加下面的調用,將這些新視圖連接到 polls.urls
模塊:瀏覽器
# polls/urls.py from django.urls import path from . import views urlpatterns = [ # ex: /polls/ path('', views.index, name='index'), # ex: /polls/5/ path('<int:question_id>/', views.detail, name='detail'), # ex: /polls/5/results/ path('<int:question_id>/results/', views.results, name='results'), # ex: /polls/5/vote/ path('<int:question_id>/vote/', views.vote, name='vote'), ]
如今在瀏覽器中訪問一下 「/polls/34/」,它運行的是 detail()
方法,顯示了你在 URL 中提供的ID,再試着訪問一下「/polls/34/results/」 和 「/polls/34/vote/」 —— 這會顯示佔位符的結果(佔位符的結果:the placeholder results,沒理解這個的意思)和投票頁。 當有人在你的站點請求一個頁面時 —— 「/polls/34/」 ,django 會加載mysite.urls
模塊,由於它在 ROOT_URLCONF 的配置中被設置。他會找到名爲 urlpatterns
的變量並按順序遍歷模式。當找到匹配的 「polls/」
後,它會去掉匹配的文本(「polls/」
)併發送文本——「34/」——到「polls.urls」 URLconf,以便後續處理。這裏匹配到 '<int:question_id>/'
,所以去調用 detail()
視圖,就像下面同樣:網絡
detail(request=<HttpRequest object>, question_id=34)
question_id=34
這一部分來自 <int:question_id>
。使用尖括號捕獲 URL 的一部分,並將其做爲關鍵字參數發送費視圖函數。字符串的部分:question_id>
用於定義名稱,它會被用於識別匹配的模式;<int:
這一部分是一個轉換器,它決定 URL 路徑中的這一部分應該匹配什麼模式。併發
不須要在 URL 中添加如「.html」一類的東西 —— 除非你想添加,這時你能夠這樣作:app
path('polls/latest.html', views.index),
可是不要這樣,這看起來太蠢了。
搞事情 == actually do something。 每個視圖負責作兩件事情:返回一個包行請求頁面內容的 HttpResponse(這裏少一個連接) 對象, 或者 報告一個例如 Http404
的異常。剩下的就看你了。 你的視圖能夠從數據庫中讀取記錄,或者不讀。它可使用一個模板系統,例如 Django 的或者第三方的 Python 模板系統,或者不使用。它可使用你想用的 Python 庫生成一個 PDF 文件、輸出 XML、實時建立一個 ZIP 文件、任何你想建立的。
Django 想要的是一個 HttpResponse(這裏少一個連接) 或者一個異常。 由於這樣很方便,讓咱們使用咱們在前一節提到的 Django 本身的數據庫 API。在 index()視圖有一行新增的內容,它顯示系統中最新的5個投票問題,根據發佈日期使用都好分開。
# 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) # Leave the rest of the views (detail, results, vote) unchanged
但這裏會有一個問題:頁面的設計(樣式、外觀)是被硬編碼在視圖中的,若是你想要更改頁面的外觀,你就必須編輯這些 Python 代碼。因此咱們能夠在Django 的末班系統中建立一個視圖可使用的模板,將設計(外觀、樣式)從Python 中分離出來。 首先,在你的 polls
目錄中建立一個名爲 templates
的目錄。Django 會從它裏面查找模板。 你的項目的 TEMPLATES
(這裏少一個連接)設置用來告訴 Django 如何加載和渲染模板。默認配置文件設置一個DjangoTemplates
後端,其 APP_DIRS
選項被設置爲 True
。 按照慣例,DjangoTemplates
會在每一個INSTALLED_APPS
(這裏少一個連接)的子目錄中查找模板。
在剛剛建立的 templates
目錄中,建立另一個叫 polls
的目錄,並在裏面建立 indexl.html
文件。換句話說,你的模板應該放在polls/templates/polls/index.html
。 由於 app_directories
模板加載器的工做方式和上面說的同樣,你能夠引用這個模板就像 django 引用polls/index.html
同樣。
模板命名空間 如今咱們能夠直接將模板放入
polls/templates
中(而不是建立另一個polls
的子目錄),但其實這並非一個好主意。Django 會選擇他找到的第一個名字匹配的模板,而且若是你的不一樣的app中有相同名字的模板,Django 會沒法區分他們。咱們須要 django 指向正確的那一個,最簡單的方法就是經過命名空間來保證它正確。也就是說,將這些模板放入app本身命名的另一個目錄。
將下面的代碼放入模板中:
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
視圖。
polls/views.py 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
模板,並把它傳給上下文。上下文是一個將模板變量名映射到 Python 對象的字典。
經過在瀏覽器中訪問 「/polls/」來加載頁面,你會看到一個包含在前一節中建立的「What's up」問題的列表。連接指向問題的詳細頁面。
加載模板的一個經常使用習慣是,填充上下文並返回一個以渲染後的模板爲結果的 HttpResponse
(這裏少一個連接)對象。Django 提供一種便捷方法。下面是重寫後的完整的 index()視圖:
# polls/views.py 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
這裏少一個連接)(若是你沒有修改以前的detail
、results
和 vote
方法,你將須要保留 HttpResponse
)
render()函數第一個參數是一個 request 對象,第二個參數是一個模板的名字,第三個參數是可選的,是一個字典。它返回一個使用給定上下文渲染模板後返回的 HttpResponse 對象。
如今咱們來處理問題的 detail 視圖 —— 顯示給定投票的問題文本的頁面。下面是視圖的代碼:
# 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})
這裏有新的概念:若是問題 ID 不存在,那麼視圖會引起一個 Http404異常。 稍後咱們會討論你能夠在 polls/detail.html
中存放什麼內容,但若是你想要快速獲得和上面例子同樣的效果,僅僅須要一個包含:
<!-- polls/templates/polls/detail.html --> {{ question }}
的文件就可讓你作到。
一個常見的習慣是使用 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()
(這裏少一個連接) 函數,它的工做方式相似get_object_or_404()(這裏少一個連接) —— 除了用 filter()(這裏少一個連接) 替帶了 get()(這裏少一個連接)。若是列表是空的,它會引起 Http404
(這裏少一個連接)
返回咱們 poll app中的 detail()
視。給定context 變量 question
,polls/detail.html
看起來的樣子可能像下面同樣:
<!--polls/templates/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
對象上進行字典查詢,若是失敗,它會再嘗試查找屬性 —— 在這裏的例子中,屬性查找會成功,若是屬性查找失敗,Django會再嘗試進行索引列表查詢。
在 {% 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>
硬編碼的問題是,在項目中有許多模板要修改 URLs 的時候,會變得很困難。然而,你在 polls.urls
模塊的 path()
函數中定義了 name 參數,這樣你就能夠經過使用{% url %}
模板標籤,在你的 url 配置中移除對特定 URL 路徑的依賴。
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
它的工做原理是經過在 polls.urls
模塊中查找指定的 URL 定義。你能夠看到「detail」的 URL 名稱在下面被定義:
... # the 'name' value as called by the {% url %} template tag path('<int:question_id>/', views.detail, name='detail'), ...
若是你想要修改 polls中 detail 的 URL 修改爲其餘的樣子,可能就像polls/specifics/12/
,你能夠在 polls/urls.py
中完成修改,而不須要去修改模板:
... # added the word 'specifics' path('specifics/<int:question_id>/', views.detail, name='detail'), ...
教程中的項目只有一個app——polls
。在實際的 django 項目中,可能會有5個、10個、20個或更多個。Django 是如何區分他們的 URL 名字的呢?例如 polls
app有一個 detail
視圖,該項目下還有一個 blog app也有一個相同的視圖。當使用{% url %}
標籤時,如何讓 Django 知道爲 url 調用那個app的視圖?
答案是在URLconf 中添加一個命名空間。在 polls/urls.py
文件中,添加一個app名來設置app的命名空間。
# polls/urls.py from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.index, name='index'), path('<int:question_id>/', views.detail, name='detail'), path('<int:question_id>/results/', views.results, name='results'), path('<int:question_id>/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>
當你熟悉了編寫視圖,就能夠繼續學習下一節的內容了,學習簡單的表單處理和通用視圖。