本教程上接 教程 第3部分 。我們將 繼續(xù)開(kāi)發(fā) Web-poll 應(yīng)用并且關(guān)注在處理簡(jiǎn)單的窗體和優(yōu)化我們的代碼。
讓我們把在上一篇教程中編寫(xiě)的 poll 的 detail 模板更新下,在模板中包含 HTML 的
<h1>{{ poll.question }}</h1> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} <form action="{% url 'polls:vote' poll.id %}" method="post"> {% csrf_token %} {% for choice in poll.choice_set.all %} <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br /> {% endfor %} <input type="submit" value="Vote" /> </form>
簡(jiǎn)單的總結(jié)下:
“choice”
choice=3
{% url 'polls:vote' poll.id %},以及設(shè)置了
{% csrf_token %}
現(xiàn)在,讓我們來(lái)創(chuàng)建一個(gè) Django 視圖來(lái)處理提交的數(shù)據(jù)。 記得嗎?在 教程 第3部分 中,我們?yōu)?polls 應(yīng)用創(chuàng)建了一個(gè) URLconf 配置中包含有這一行代碼:
url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
我們還創(chuàng)建了一個(gè)虛擬實(shí)現(xiàn)的 vote() 函數(shù)。讓我們創(chuàng)建一個(gè)真實(shí)版本吧。在 polls/views.py 中添加如下代碼:
from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect, HttpResponse from django.core.urlresolvers import reverse from polls.models import Choice, Poll # ... def vote(request, poll_id): p = get_object_or_404(Poll, pk=poll_id) try: selected_choice = p.choice_set.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): # Redisplay the poll voting form. return render(request, 'polls/detail.html', { 'poll': p, 'error_message': "You didn't select a choice.", }) else: selected_choice.votes += 1 selected_choice.save() # Always return an HttpResponseRedirect after successfully dealing # with POST data. This prevents data from being posted twice if a # user hits the Back button. return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
在這代碼中有些內(nèi)容還未在本教程中提到過(guò):
request.POST 是一個(gè)類(lèi)似字典的對(duì)象,可以讓你 通過(guò)關(guān)鍵字名稱(chēng)來(lái)獲取提交的數(shù)據(jù)。在本例中, request.POST['choice'] 返回了所選擇的投票項(xiàng)目的 ID ,以字符串的形式。 request.POST 的值永遠(yuǎn)是字符串形式的。
請(qǐng)注意 Django 也同樣的提供了通過(guò) request.GET 獲取 GET 數(shù)據(jù)的方法 – 但是在代碼中我們明確的使用了 request.POST 方法,以確保數(shù)據(jù)是通過(guò) POST 方法來(lái)修改的。
如果 choice 未在 POST 數(shù)據(jù)中提供 request.POST['choice'] 將拋出 KeyError 當(dāng)未給定 choice 對(duì)象時(shí)上面的代碼若檢測(cè)到拋出的是 KeyError 異常就會(huì)向 poll 顯示一條錯(cuò)誤信息。
在增加了投票選項(xiàng)的統(tǒng)計(jì)數(shù)后,代碼返回一個(gè) HttpResponseRedirect 對(duì)象而不是常見(jiàn)的 HttpResponse 對(duì)象。 HttpResponseRedirect 對(duì)象需要一個(gè)參數(shù):用戶(hù)將被重定向的 URL (請(qǐng)繼續(xù)看下去在這情況下我們是如何構(gòu)造 URL ) 。
就像上面用 Python 作的注釋那樣,當(dāng)成功的處理了 POST 數(shù)據(jù)后你應(yīng)該總是返回一個(gè) HttpResponseRedirect 對(duì)象。 這個(gè)技巧不是特定于 Django 的;它是優(yōu)秀的 Web 開(kāi)發(fā)實(shí)踐。
在本例中,我們?cè)?HttpResponseRedirect 的構(gòu)造方法中使用了 reverse() 函數(shù)。 此函數(shù)有助于避免在視圖中硬編碼 URL 的功能。它指定了我們想要的跳轉(zhuǎn)的視圖函數(shù)名以及視圖函數(shù)中 URL 模式相應(yīng)的可變參數(shù)。在本例中,我們使用了教程 第3部分中的 URLconf 配置, reverse() 將會(huì)返回類(lèi)似如下所示的字符串
'/polls/3/results/'
... 在此 3 就是 p.id 的值。該重定向 URL 會(huì)調(diào)用 'results' 視圖并顯示最終頁(yè)面。
正如在教程 第3部分提到的,request 是一個(gè) HttpRequest 對(duì)象。想了解 HttpRequest 對(duì)象更多的內(nèi)容,請(qǐng)參閱 request 和 response 文檔 。
request
當(dāng)有人投票后,vote() 視圖會(huì)重定向到投票結(jié)果頁(yè)。讓我們來(lái)編寫(xiě)這個(gè)視圖
vote()
def results(request, poll_id): poll = get_object_or_404(Poll, pk=poll_id) return render(request, 'polls/results.html', {'poll': poll})
這幾乎和 教程 第3部分 中的 detail() 視圖完全一樣。 唯一的區(qū)別就是模板名稱(chēng)。 稍后我們會(huì)解決這個(gè)冗余問(wèn)題。
現(xiàn)在,創(chuàng)建一個(gè) polls/results.html 模板:
<h1>{{ poll.question }}</h1> <ul> {% for choice in poll.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
現(xiàn)在,在瀏覽器中訪(fǎng)問(wèn) /polls/1/ 并完成投票。每次投票后你將會(huì)看到結(jié)果頁(yè)數(shù)據(jù)都有更新。 如果你沒(méi)有選擇投票選項(xiàng)就提交了,將會(huì)看到錯(cuò)誤的信息。
detail() ( 在 教程 第3部分 中) 和 results() 視圖 都很簡(jiǎn)單 – 并且還有上面所提到的冗余問(wèn)題。index() 用于顯示 polls 列表的 index() 視圖 (也在教程 第3部分中),也是存在類(lèi)似的問(wèn)題。
index()
這些視圖代表了基本的 Web 開(kāi)發(fā)中一種常見(jiàn)的問(wèn)題: 根據(jù) URL 中的參數(shù)從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù),加載模板并返回渲染后的內(nèi)容。由于這類(lèi)現(xiàn)象很 常見(jiàn),因此 Django 提供了一種快捷方式,被稱(chēng)之為“通用視圖”系統(tǒng)。
通用視圖抽象了常見(jiàn)的模式,以至于你不需要編寫(xiě) Python 代碼來(lái)編寫(xiě)一個(gè)應(yīng)用。
讓我們把 poll 應(yīng)用修改成使用通用視圖系統(tǒng)的應(yīng)用,這樣我們就能刪除刪除一些我們自己的代碼了。 我們將采取以下步驟來(lái)進(jìn)行修改:
請(qǐng)繼續(xù)閱讀了解詳細(xì)的信息。
為什么要重構(gòu)代碼? 通常情況下,當(dāng)你編寫(xiě)一個(gè) Django 應(yīng)用時(shí),你會(huì)評(píng)估下通用視圖是否適合解決你的問(wèn)題, 如果適合你就應(yīng)該從一開(kāi)始就使用它,而不是進(jìn)行到一半才重構(gòu)你的代碼。 但是本教程直到現(xiàn)在都故意集中介紹“硬編碼”視圖,是為了專(zhuān)注于核心概念上。 就像你在使用計(jì)算器前需要知道基本的數(shù)學(xué)知識(shí)一樣。
為什么要重構(gòu)代碼?
通常情況下,當(dāng)你編寫(xiě)一個(gè) Django 應(yīng)用時(shí),你會(huì)評(píng)估下通用視圖是否適合解決你的問(wèn)題, 如果適合你就應(yīng)該從一開(kāi)始就使用它,而不是進(jìn)行到一半才重構(gòu)你的代碼。 但是本教程直到現(xiàn)在都故意集中介紹“硬編碼”視圖,是為了專(zhuān)注于核心概念上。
就像你在使用計(jì)算器前需要知道基本的數(shù)學(xué)知識(shí)一樣。
首先,打開(kāi) polls/urls.py 的 URLconf 配置文件并修改成如下所示樣子
from django.conf.urls import patterns, url from django.views.generic import DetailView, ListView from polls.models import Poll urlpatterns = patterns('', url(r'^$', ListView.as_view( queryset=Poll.objects.order_by('-pub_date')[:5], context_object_name='latest_poll_list', template_name='polls/index.html'), name='index'), url(r'^(?P<pk>\d+)/$', DetailView.as_view( model=Poll, template_name='polls/detail.html'), name='detail'), url(r'^(?P<pk>\d+)/results/$', DetailView.as_view( model=Poll, template_name='polls/results.html'), name='results'), url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'), )
在這我們將使用兩個(gè)通用視圖: ListView 和 DetailView 。這兩個(gè)視圖分別用于顯示兩種抽象概念 “顯示一系列對(duì)象的列表” 和 “顯示一個(gè)特定類(lèi)型的對(duì)象的詳細(xì)信息頁(yè)”。
默認(rèn)情況下, DetailView 通用視圖使用名為 <應(yīng)用名>/<模型名>_detail.html 的模板。在我們的例子中,將使用名為 "polls/poll_detail.html" 的模板。 template_name 參數(shù)是告訴 Django 使用指定的模板名,而不是使用自動(dòng)生成的默認(rèn)模板名。 我們也指定了 results 列表視圖的 template_name – 這確保了 results 視圖和 detail 視圖渲染時(shí)會(huì)有不同的外觀,雖然它們有一個(gè) DetailView 隱藏在幕后。
同樣的,~django.views.generic.list.ListView 通用視圖使用的默認(rèn)模板名為 <應(yīng)用名>/<模型名>_list.html ;我們指定了 template_name 參數(shù)告訴 ListView 使用已經(jīng)存在的 "polls/index.html" 模板。
在之前的教程中,模板提供的上下文中包含了 poll 和 latest_poll_list 上下文變量。在 DetailView 中 poll 變量是自動(dòng)提供的 – 因?yàn)槲覀兪褂昧艘粋€(gè) Django 模型 (Poll) ,Django 能夠?yàn)樯舷挛淖兞看_定適合的名稱(chēng)。 另外 ListView 自動(dòng)生成的上下文變量名是 poll_list 。若要覆蓋此變量我們需要提供 context_object_name 選項(xiàng), 我們想要使用 latest_poll_list 來(lái)替代它。作為一種替代方式,你可以改變你的模板來(lái) 匹配新的默認(rèn)的上下文變量 – 但它是一個(gè)非常容易地告訴 Django 使用你想要的變量的方式。
現(xiàn)在你可以在 polls/views.py 中刪除 index() , detail() 和 results() 視圖了。 我們不需要它們了 – 它們已替換為通用視圖了。你也可以刪除不再需要的 HttpResponse 導(dǎo)入包了。
運(yùn)行服務(wù)器,并且使用下基于通用視圖的新投票應(yīng)用。
有關(guān)通用視圖的完整詳細(xì)信息,請(qǐng)參閱 通用視圖文檔.
當(dāng)你熟悉了窗體和通用視圖后,請(qǐng)閱讀 教程 第5部分 來(lái)學(xué)習(xí)測(cè)試我們的投票應(yīng)用。
譯者:Django 文檔協(xié)作翻譯小組,原文:Part 4: Forms and generic views。 本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。 Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。
譯者:Django 文檔協(xié)作翻譯小組,原文:Part 4: Forms and generic views。
本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。
Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。
鍍金池立足為學(xué)員提供精品優(yōu)選課程及個(gè)性化定制服務(wù)。
掃碼關(guān)注鍍金池微信號(hào)手機(jī)訂課,優(yōu)惠多多