django 1.8 官方文檔翻譯: 1-2-4 編寫你的第一個Django應用,第4部分

編寫你的第一個 Django 程序 第4部分

本教程上接 教程 第3部分 。咱們將 繼續開發 Web-poll 應用而且關注在處理簡單的窗體和優化咱們的代碼。html

編寫一個簡單的窗體

讓咱們把在上一篇教程中編寫的 poll 的 detail 模板更新下,在模板中包含 HTML 的 <form> 組件:python

<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>

簡單的總結下:數據庫

  • 上面的模板中爲每一個投票選項設置了一個單選按鈕。每一個單選按鈕的 value 是投票選項對應的 ID 。每一個單選按鈕的 name 都是 「choice」。這意味着,當有人選擇了一個單選按鈕並提交了表單,將會發送 的 POST 數據是 choice=3。這是 HTML 表單中的基本概念。django

  • 咱們將 form 的 action 設置爲 {% url 'polls:vote' poll.id %},以及設置了 method="post" 。使用 method="post" ( 而不是 method="get") 是很是重要的,由於這種提交表單的方式會改變服務器端的數據。 當你建立一個表單爲了修改服務器端的數據時,請使用 method="post" 。這不是 Django 特定的技巧;這是優秀的 Web 開發實踐。瀏覽器

  • forloop.counter 表示 for 標籤在循環中已經循環過的次數服務器

  • 因爲咱們要建立一個POST form ( 具備修改數據的功能 ),咱們須要擔憂跨站點請求僞造 ( Cross Site Request Forgeries )。 值得慶幸的是,你沒必要太擔憂這一點,由於 Django 自帶了一個很是容易使用的系統來防護它。 總之,全部的 POST form 針對內部的 URLs 時都應該使用 {% csrf_token %} 模板標籤。函數

如今,讓咱們來建立一個 Django 視圖來處理提交的數據。 記得嗎?在 教程 第3部分 中,咱們爲 polls 應用建立了一個 URLconf 配置中包含有這一行代碼:oop

url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),

咱們還建立了一個虛擬實現的 vote() 函數。讓咱們建立一個真實版本吧。在 polls/views.py 中添加以下代碼:post

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,)))

在這代碼中有些內容還未在本教程中提到過:學習

request.POST 是一個相似字典的對象,可讓你 經過關鍵字名稱來獲取提交的數據。在本例中, request.POST['choice'] 返回了所選擇的投票項目的 ID ,以字符串的形式。 request.POST 的值永遠是字符串形式的。

請注意 Django 也一樣的提供了經過 request.GET 獲取 GET 數據的方法 – 可是在代碼中咱們明確的使用了 request.POST 方法,以確保數據是經過 POST 方法來修改的。

若是 choice 未在 POST 數據中提供 request.POST['choice'] 將拋出 KeyError 當未給定 choice 對象時上面的代碼若檢測到拋出的是 KeyError 異常就會向 poll 顯示一條錯誤信息。

在增長了投票選項的統計數後,代碼返回一個 HttpResponseRedirect 對象而不是常見的 HttpResponse 對象。 HttpResponseRedirect 對象須要一個參數:用戶將被重定向的 URL (請繼續看下去在這狀況下咱們是如何構造 URL ) 。

就像上面用 Python 做的註釋那樣,當成功的處理了 POST 數據後你應該老是返回一個 HttpResponseRedirect 對象。 這個技巧不是特定於 Django 的;它是優秀的 Web 開發實踐。

在本例中,咱們在 HttpResponseRedirect 的構造方法中使用了 reverse() 函數。 此函數有助於避免在視圖中硬編碼 URL 的功能。它指定了咱們想要的跳轉的視圖函數名以及視圖函數中 URL 模式相應的可變參數。在本例中,咱們使用了教程 第3部分中的 URLconf 配置, reverse() 將會返回相似以下所示的字符串

'/polls/3/results/'

... 在此 3 就是 p.id 的值。該重定向 URL 會調用 'results' 視圖並顯示最終頁面。

正如在教程 第3部分提到的,request 是一個 HttpRequest 對象。想了解 HttpRequest 對象更多的內容,請參閱 request 和 response 文檔 。

當有人投票後,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() 視圖徹底同樣。 惟一的區別就是模板名稱。 稍後咱們會解決這個冗餘問題。

如今,建立一個 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>

如今,在瀏覽器中訪問 /polls/1/ 並完成投票。每次投票後你將會看到結果頁數據都有更新。 若是你沒有選擇投票選項就提交了,將會看到錯誤的信息。

使用通用視圖:優化代碼

detail() ( 在 教程 第3部分 中) 和 results() 視圖 都很簡單 – 而且還有上面所提到的冗餘問題。index() 用於顯示 polls 列表的 index() 視圖 (也在教程 第3部分中),也是存在相似的問題。

這些視圖表明瞭基本的 Web 開發中一種常見的問題: 根據 URL 中的參數從數據庫中獲取數據,加載模板並返回渲染後的內容。因爲這類現象很 常見,所以 Django 提供了一種快捷方式,被稱之爲「通用視圖」系統。

通用視圖抽象了常見的模式,以致於你不須要編寫 Python 代碼來編寫一個應用。

讓咱們把 poll 應用修改爲使用通用視圖系統的應用,這樣咱們就能刪除刪除一些咱們本身的代碼了。 咱們將採起如下步驟來進行修改:

  • 修改 URLconf 。

  • 刪除一些舊的,沒必要要的視圖。

  • 修正 URL 處理到對應的新視圖。

請繼續閱讀了解詳細的信息。

爲何要重構代碼?

一般狀況下,當你編寫一個 Django 應用時,你會評估下通用視圖是否適合解決你的問題, 若是適合你就應該從一開始就使用它,而不是進行到一半才重構你的代碼。 可是本教程直到如今都故意集中介紹「硬編碼」視圖,是爲了專一於核心概念上。

就像你在使用計算器前須要知道基本的數學知識同樣。

修改 URLconf

首先,打開 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'),
)

修改 views

在這咱們將使用兩個通用視圖: ListView 和 DetailView 。這兩個視圖分別用於顯示兩種抽象概念 「顯示一系列對象的列表」 和 「顯示一個特定類型的對象的詳細信息頁」。

  • 每一個視圖都須要知道使用哪一個模型數據。所以須要提供將要使用的 model 參數。

  • DetailView 通用視圖指望從 URL 中捕獲名爲 "pk" 的主鍵值,所以咱們將 poll_id 改成 pk 。

默認狀況下, DetailView 通用視圖使用名爲 <應用名>/<模型名>detail.html 的模板。在咱們的例子中,將使用名爲 "polls/polldetail.html" 的模板。 templatename 參數是告訴 Django 使用指定的模板名,而不是使用自動生成的默認模板名。 咱們也指定了 results 列表視圖的 templatename – 這確保了 results 視圖和 detail 視圖渲染時會有不一樣的外觀,雖然它們有一個 DetailView 隱藏在幕後。

一樣的,~django.views.generic.list.ListView 通用視圖使用的默認模板名爲 <應用名>/<模型名>list.html ;咱們指定了 templatename 參數告訴 ListView 使用已經存在的 "polls/index.html" 模板。

在以前的教程中,模板提供的上下文中包含了 poll 和 latestpolllist 上下文變量。在 DetailView 中 poll 變量是自動提供的 – 由於咱們使用了一個 Django 模型 (Poll) ,Django 可以爲上下文變量肯定適合的名稱。 另外 ListView 自動生成的上下文變量名是 polllist 。若要覆蓋此變量咱們須要提供 contextobjectname 選項, 咱們想要使用 latestpoll_list 來替代它。做爲一種替代方式,你能夠改變你的模板來 匹配新的默認的上下文變量 – 但它是一個很是容易地告訴 Django 使用你想要的變量的方式。

如今你能夠在 polls/views.py 中刪除 index() , detail() 和 results() 視圖了。 咱們不須要它們了 – 它們已替換爲通用視圖了。你也能夠刪除再也不須要的 HttpResponse 導入包了。

運行服務器,而且使用下基於通用視圖的新投票應用。

有關通用視圖的完整詳細信息,請參閱 通用視圖文檔.

當你熟悉了窗體和通用視圖後,請閱讀 教程 第5部分 來學習測試咱們的投票應用。

譯者:Django 文檔協做翻譯小組,原文:Part 4: Forms and generic views

本文以 CC BY-NC-SA 3.0 協議發佈,轉載請保留做者署名和文章出處。

Django 文檔協做翻譯小組人手緊缺,有興趣的朋友能夠加入咱們,徹底公益性質。交流羣:467338606。

相關文章
相關標籤/搜索