第三節:視圖(Views)和模板(Templates)


目錄

概覽

視圖是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)

注意我們所有的視圖都做到這一點的話,我們不在需要導入loaderHttpResponse 

這個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'),
...

Url名稱的命名空間

本教程的項目僅僅有一個應用,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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM