本教程上接Django 1.10中文文檔-第一個應用Part2-模型和管理站點。我們將繼續開髮網頁投票這個應用,主要講如何創建一個對用戶開放的界面。 概覽 視圖是Django應用中的一“類”網頁,它通常使用一個特定的函數提供服務,並且具有一個特定的模板。例如,在博客應用中,可能有以下視圖: 博客首頁 ...
本教程上接Django 1.10中文文檔-第一個應用Part2-模型和管理站點。我們將繼續開髮網頁投票這個應用,主要講如何創建一個對用戶開放的界面。
概覽
視圖是Django應用中的一“類”網頁,它通常使用一個特定的函數提供服務,並且具有一個特定的模板。例如,在博客應用中,可能有以下視圖:
-
博客首頁 —— 顯示最新發表的博客;
-
博客“詳細”頁面 —— 每博客的鏈接頁面;
-
基於年份的歸檔頁面 —— 顯示特定年內所有月份發表過的博客;
-
基於月份的歸檔頁面 —— 顯示特定月份內每天發表過博客;
-
基於日期的歸檔頁面 —— 顯示特定日期內發表過的所有博客;
-
評論:處理針對某篇博客發佈的評論。
在我們的投票應用中,我們將建立下麵的四個視圖:
-
Question首頁 —— 顯示最新發佈的幾個Question;
-
Question“詳細”頁面 —— 顯示單個Question的具體內容,提供一個投票的表單,但不顯示該議題的當前投票結果;
-
Question“結果”頁面 —— 顯示特定的Question的投票結果;
-
投票功能 —— 處理對Question中Choice的投票。
在Django中,網頁的頁面和其他內容都是由視圖(views.py)來傳遞的(視圖對WEB請求進行回應)。每個視圖都是由一個Python函數(或者是基於類的視圖的方法)表示。Django通過對比請求的URL地址來選擇對應的視圖。
在你平時的網頁上,你可能經常會碰到類似“ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”的url。慶幸的是Django支持使用更加簡介的URL模式(patterns),而不需要編寫上面那種複雜的url。
URL模式就是一種URL的通用模式 —— 例如: /newsarchive/<year>/<month>/
。
Django使用‘URLconfs’的配置來為URL匹配視圖函數。 URLconf使用正則表達式將URL匹配到視圖上。
本教程提供URLconfs基本使用,更多信息請參考django.url
編輯視圖
下麵,讓我們打開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)
然後,在polls/urls.py文件中加入下麵的url模式,將其映射到我們上面新增的視圖。
# 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。訪問“/polls/34/results/”和“/polls/34/vote/”,將分別顯示預定義的偽結果和投票頁面。
上面訪問的路由過程如下:當有人訪問“/polls/34/”地址時,Django將首先載入mysite.urls模塊,因為它是settings文件里設置的ROOT_URLCONF配置文件。在模塊里找到urlpatterns變數,按順序對各項進行正則匹配。當它匹配到了^polls/,就剝離出url中匹配的文本polls/,然後將剩下的文本“34/”,傳遞給“polls.urls”進行下一步的處理。在polls.urls,又匹配到了r’^(?P<question_id>[0-9]+)/$’
,最終結果就是調用該模式對應的detail()視圖,將34作為參數傳入:
detail(request=<HttpRequest object>, question_id='34')
question_id=’34’的部分來自(?P <question_id> [0-9])
。使用模式周圍的括弧“捕獲”該模式匹配到的文本,並將其作為參數發送到視圖函數;?P<question_id>
定義一個名字用於標識匹配的模式;[0-9]+
是匹配一串數字的正則表達。
因為URL模式是正則表達式,你如何使用它們沒有什麼限制。 不需要添加像.html這樣繁瑣的URL —— 除非你執意這麼做,在這種情況下你可以這樣做:
url(r'^polls/latest\.html$', views.index),
但是,不要這樣做。這比較愚蠢。
編寫擁有實際功能的視圖
每個視圖函數只負責處理兩件事中的一件:返回一個包含所請求頁面內容的HttpResponse對象,或拋出一個諸如Http404異常。該如何去做這兩件事,就看你自己的想法了。
您的視圖可以從資料庫讀取記錄,也可以不讀取。它可以使用模板系統:如Django的或第三方Python模板系統 或不。可以生成PDF文件,輸出XML,即時創建ZIP文件,任何你想要的,使用任何你想要的Python庫。Django只要求返回的是一個HttpResponse。 或者拋出一個異常。
為了方便,讓我們使用Part1中介紹的Django自己的資料庫API。 下麵是一個新的index()視圖,它顯示系統中最新發佈的5條questions記錄,並用逗號分隔:
# 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)
# 保持其他的視圖 (detail, results, vote) 不變
這裡有一個問題:頁面的設計被硬編碼在視圖中。 如果你想更改頁面的外觀,就得編輯這段Python代碼。 因此,我們使用Django的模板系統,通過創建一個視圖能夠調用的模板,將頁面的設計從Python中分離出來。
首先,在你的polls目錄下創建一個叫做 templates的目錄。Django將在這裡查找模板。
項目的settings.py中的templates配置決定了Django如何載入渲染模板。將APP_DIRS設置為True。DjangoTemplates將在INSTALLED_APPS所包含的每個應用的目錄下查找名為”templates”子目錄。
在剛剛創建的templates目錄中,創建另一個名為polls的目錄,併在其中創建一個名為index.html的文件。換句話說,你的模板應該是polls/templates/polls/index.html。由於app_directories模板載入器如上所述工作,因此您可以在Django中簡單地引用此模板為polls/index.html(省掉前面的路徑)。
模板命名空間: 如果我們把模板直接放在polls/templates中(而不是創建另一個polls子目錄),但它實際上是一個壞主意。 Django將選擇它找到的名字匹配的第一個模板,如果你在不同的應用程式中有一個相同名稱的模板,Django將無法區分它們。我們需要能夠將Django指向正確的一個,確保這一點的最簡單的方法是通過命名空間。也就是說,將這些模板放在為應用程式本身命名的另一個目錄中。
將以下的代碼放入模板文件:
# 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 RequestContext, 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 = RequestContext(request, {
'latest_question_list': latest_question_list,
})
return HttpResponse(template.render(context))
該代碼載入名為polls/index.html的模板,並傳給它一個context。Context是一個字典,將模板變數的名字映射到Python對象。
然後你可以通過瀏覽器打開http://127.0.0.1:8000/polls 查看效果。
快捷方式:render()
常見的習慣是載入一個模板、填充一個context 然後返回一個含有模板渲染結果的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)
註意,一旦我們在所有這些視圖中完成這個操作,我們不再需要import loader和HttpResponse(如果您仍然有detail, results, and vote方法,您將需要保留HttpResponse)。
render()函數接受request對象作為其第一個參數,模板名稱作為其第二個參數,字典作為其可選的第三個參數。它返回一個HttpResponse對象,含有用給定的context 渲染後的模板。
404錯誤
現在,讓我們處理Question 詳細頁面的視圖 —— 顯示Question內容的頁面:
# 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。
使用模板系統
回到我們投票應用的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對對象問題進行字典查找。如果沒有,它嘗試一個屬性查找 - 在這種情況下工作。如果屬性查找失敗,它將嘗試列表索引查找。
方法調用發生在{% 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模塊的url()函數中定義了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
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...
如果你想把polls應用中detail視圖的URL改成其它樣子比如 polls/specifics/12/,就可以不必在該模板(或者多個模板)中修改它,只需要修改 polls/urls.py:
...
# added the word 'specifics'
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...
URL name的命名空間
教程中的這個項目只有一個應用polls。在真實的Django項目中,可能會有五個、十個、二十個或者更多的應用。 Django如何區分它們URL的名字呢? 例如,polls 應用具有一個detail 視圖,相同項目中的博客應用可能也有這樣一個視圖。當使用模板標簽{% url %}時,人們該如何做才能使得Django知道為一個URL創建哪個應用的視圖?
答案是在你的主URLconf下添加命名空間。 在mysite/urls.py文件中,添加命名空間將它修改成:
# mysite/urls.py
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^polls/', include('polls.urls', namespace="polls")),
url(r'^admin/', include(admin.site.urls)),
]
現在將你的polls/index.html改為具有命名空間的詳細視圖:
# polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
歡迎關註微信公眾號: Pythoner每日一報
-
Qt使用統一的坐標系統來定位視窗部件的位置和大小。 以屏幕的左上角為原點即(0, 0)點,從左向右為x軸正向,從上向下為y軸正向,這整個屏幕的坐標系統就用來定位頂層視窗; 此外,視窗內部也有自己的坐標系統,它依然以左上角作為原點,從左向右為x軸正向,從上向下為y軸正向,原點、x軸、y軸圍成的區域叫做 ...
-
Flask是一個非常優秀的web框架,到目前為止,我相信關於他的介紹以及非常的多,就算cnblog中,隨便一搜也會有很多內容,但還是拋磚引玉,就當是一個自我的總結 部署環境 安裝python 首先,當然是安裝python環境,去 "官網" 來下載最新的環境(我選擇最新的3.6版本) 然後一路下一步即 ...
-
ECMAScript語言的標準是由Netscape、Sun、微軟、Borland等公司基於JavaScript和JScript錘煉、定義出來的。 ECMAScript可以為不同種類的宿主環境提供核心的腳本編程能力。ECMAScript僅僅是一個描述,定義了腳本語言的所有屬性、方法和對象。 ...
-
Spring_屬性配置細節 1、若字面值包含特殊字元,可以使用<![CDATA[]] 把字面值包裹起來 例 : 2、ref屬性來建立bean之間的引用關係和級聯屬性賦值 2.1 定義User.java(見上一篇文章)和Manager.java Bean 2、2 配置spring xml文件 3、配置 ...
-
本工作室專註開發南方湛江海南七星彩投註網站系統建設開發,出租,支持帶手機版投註,以及手機APP開發,安裝版本,蘋果版。 QQ私聊:2046 771739 演示圖: ...
-
操作系統: CentOS 6.5_x64開發語言: Python 適用場景:程式異常退出後需要及時啟動的情況。 源碼地址: https://github.com/mike-zhang/processGuarder 原理 通過ps檢查進程是否存在,如果不存在則啟動 使用 ./processGuarde ...
-
maven-compiler-plugin 編譯Java源碼,一般只需設置編譯的jdk版本 maven-dependency-plugin 用於複製依賴的jar包到指定的文件夾里 maven-jar-plugin 打成jar時,設定manifest的參數,比如指定運行的Main class,還有依賴 ...
-
今天在牛客網上做了一道題,題意就是求左旋轉字元串。我使用輾轉相除法解之,一次性AC通過。實話說,每次寫演算法一次性通過,甚至一點編譯錯誤都沒有,我覺得這就是對我最好的嘉獎。 題目描述: 彙編語言中有一種移位指令叫做迴圈左移(ROL),現在有個簡單的任務,就是用字元串模擬這個指令的運算結果。對於一個給定 ...