Django 2.0.1 官方文檔翻譯: 編寫你的第一個 Django app,第三部分(Page 8)

#編寫你的第一個 Django app,第三部分(Page 8)轉載請註明連接地址 本頁教程接前面的第二部分。咱們繼續開發 web-poll app,咱們會專一於建立公共接口上 —— 「視圖」。html

##概述 在你的 Django app中,視圖是一個 web 頁面的類型,通常服務於一個特定的函數,並擁有一個特定的模板(template)。例如:在 blog app中,你可能有下面這些視圖:python

  • Blog homepage – 顯示少許最新的條目
  • Entry 「detail」 page – 一個獨立的永久連接入口
  • Year-based archive page – 顯示指定年份中全部月份的入口
  • Month-based archive page – 顯示指定月份因此天的的入口
  • Day-based archive page – 顯示指定天的全部入口
  • Comment action – 處理給定條目提交的評論

在咱們的 poll app中,咱們會用到下面四個視圖web

  • Question 「index」 page – 顯示一些最新的問題
  • Question 「detail」 page – 顯示一個問題的內容,有一個用於投票的表單但沒有結果
  • Question 「results」 page – 顯示特定問題的結果
  • Vote action – 處理特定問題中特定選擇的投票操做

在 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」問題的列表。連接指向問題的詳細頁面。

快捷方式:render()

加載模板的一個經常使用習慣是,填充上下文並返回一個以渲染後的模板爲結果的 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這裏少一個連接)(若是你沒有修改以前的detailresultsvote方法,你將須要保留 HttpResponse

render()函數第一個參數是一個 request 對象,第二個參數是一個模板的名字,第三個參數是可選的,是一個字典。它返回一個使用給定上下文渲染模板後返回的 HttpResponse 對象。

引起一個404錯誤

如今咱們來處理問題的 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_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()(這裏少一個連接) 函數,它的工做方式相似get_object_or_404()(這裏少一個連接) —— 除了用 filter()(這裏少一個連接) 替帶了 get()(這裏少一個連接)。若是列表是空的,它會引起 Http404(這裏少一個連接)

使用模板系統

返回咱們 poll app中的 detail() 視。給定context 變量 questionpolls/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 %}(這裏少一個連接)標籤。

查看 模板指南 瞭解更多管理模板的內容(這裏少一個連接)

移除模板中硬編碼的 URLs

請牢記,在咱們在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'),
...

命名空間的 URL 名字

教程中的項目只有一個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>

當你熟悉了編寫視圖,就能夠繼續學習下一節的內容了,學習簡單的表單處理和通用視圖。

相關文章
相關標籤/搜索