1. 程式人生 > >第一個Django應用程序_part3

第一個Django應用程序_part3

首頁 基於 iter render AS 工作方式 rdquo 更改 文檔

一、概述

此文延續第一個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並且HttpResponseHttpResponse如果您仍然具有,和的存根方法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