[譯]Django first steps Part4


咱們繼續polls應用html

1 寫一個簡單的form表單

更新咱們的「polls/detail.html」,增長一個form:修改polls/templates/polls/detail.html文件:web

<h1>
    {{ question.question_text }}
</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %}
{% for choice in question.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>

代碼說明:正則表達式

  • 上面的template爲每一個問題的選擇提供了一個radio button,每一個radio button的內容和choice的id聯繫在一塊兒,這就是說,有人選擇了問題的答案,而後提交,它將會經過post的方法提交choice=#,#表示所選中的choice的id,這就是最基本的html form表單;數據庫

  • 咱們設置表單的動做是{% url 'polls:vote' question.id %},提交方法是post,這是十分重要的,由於這個動做將會把數據提交到服務器,這不是django特例,而是一個良好的web開發習慣;django

  • forloop.counter表示for循環中的循環次數服務器

  • 一旦咱們建立了一個post form表單,咱們須要考慮跨站請求問題,在django中只須要使用一個temaplate tag就能解決此問題,即在form表單中增長{%csrf_token %}app

如今咱們來建立一個view來提交這個表單,還記得在part3裏邊的一條url記錄嗎?函數

url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),

咱們也建立一個vote()的函數,增長到polls/views.py:oop

def vote(request,question_id):
    question = get_object_or_404(models.Question,pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST["choice"])
    except (KeyError,models.Choice.DoesNotExist):
        return render(request,'polls/detail.html',{
            'question':question,
            'error_message':'You do not select a choice.',
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results',args=(question.id,)))

代碼說明:post

  • request.POST是一個類字典對象,能夠經過key訪問。在這個例子中,request.POST['choice'] 返回選擇到的choice ID(以字符串的形式)。request.POST的值都是字符串,Djdango也提供request.GET用戶獲取數據。

  • request.POST['choice']若是沒有取到數據,就會產生一個KeyError。並返回錯誤信息error_message。

  • 自增vote數量,返回HttpResponseRedirect。HttpResponseRedirect有一個單獨的參數:就是想要跳轉的URL (see the following point for how we construct the URL in this case).This tip isn’t specific to Django; it’s just good Web development practice.

  • 咱們使用 reverse() 函數在 HttpResponseRedirect 參數中,這個函數可以避免view中的硬編碼。它可以將url name轉化爲url字符串或正則表達式。在這個例子中,reverse() 返回字符串以下所示:

'/polls/3/results/'

修改views.py中的results函數:

def results(request,question_id):
    question = get_object_or_404(models.Question,pk=question_id)
    return render(request,"polls/results.html",{
        "question":question
    })

它和part3中的detail()很類似,只是template不同而已;
修改polls/templates/polls/results.html這個模板:

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">Vote again?</a>

如今咱們到/polls/1/ 來爲問題投票,每次投完票就能看見結果頁面。若是大家有選擇直接提交,就會看到錯誤信息。

注意:
咱們的vote() view還有個小問題,先從數據庫獲得selected_choice對象,而後計算數,而後返回數據庫。若是有兩個用戶同時投票,就會出現問題。這叫競賽,若是要解決這個問題,可使用F()來解決此問題。


2 使用generic views: 更少代碼

像前面提到的detail() 和results() views 很是簡單,還有index()view,這些 views 是一般Web 開發模式:從數據庫中獲得數據,經過url加載模板並渲染模板。由於這種模式太尋常了,django又給你們提供了快捷方式:「generic views」 系統;

下面把咱們的polls應用轉變爲generic views系統,咱們須要刪除一大推代碼,分紅下面幾步:

  • 轉換URLconf

  • 刪除一些老的,沒用的views

  • 來一些Django’s generic views

爲何把代碼搞來搞去?

一般的,當寫一個Django app, 你須要先評估generic views 是否適合解決你的問題, 從開始就使用它們要比中間再改來的好。但在咱們的例子中是爲了讓你瞭解核心思想,就像在你使用計算器以前你要先學會基本運算。

修改URLconf

打開polls/urls.py並修改它:

from django.conf.urls import url
from . import views

app_name = 'polls'
urlpatterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),
    
    url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
    url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]

咱們注意到將原來的<question_id> 改爲了 <pk>

修改views

刪除原來老的index, detail, and results三個views,使用新的generic views來代替:

from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views import generic

from .models import Choice, Question


class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by('-pub_date')[:5]


class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'


class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'

def vote(request,question_id):
    ...

咱們使用了兩個generic views: ListView and DetailView. 分別表示 「display a list of objects」 和「display a detail page for a particular type of object.」。

  • 每一個generic view須要知道它所對應的models名稱

  • DetailView從url中的pk參數獲取主鍵值,因此咱們已經在url中將question_id改爲了pk了。

在默認狀況下, DetailView generic view 將會使用一個叫作 / _detail.html的template文件,在咱們的例子中,它使用"polls/question_detail.html"。template_name 能夠指定template的名稱。

類似的狀況, the ListView generic view 使用默認模板文件 / _list.html;咱們使用 template_name 來告訴ListView咱們已經存在的"polls/index.html" template。

在上節中,咱們的模板文件中有 question 和latest_question_list這種變量. 在DetailView 中 ,一旦咱們使用django model,question 變量可以自動提供 Django會自動提供一個合適的環境變量名稱。 然而, 對於ListView自動生成如question_list的變量名稱。使用指定context_object_name變量,咱們也可以自定義變量名稱。咱們能夠在tempalte中將就django的自動產生變量,可是使用咱們本身定義的變量更加容易。

運行服務器,就能看到基於generic views的應用了,要詳細瞭解generic views本身看文檔。當你熟悉了 forms and generic views後,能夠參看part 5查看如何測試django應用。

相關文章
相關標籤/搜索