Django 1.10中文文檔:第一個(gè)應(yīng)用 part 4

已經(jīng)同步到gitbook,想閱讀的請轉(zhuǎn)到gitbook: Django 1.10 中文文檔

Writing your first Django app, part 4?This tutorial begins where Tutorial 3 left off. We’re continuing the Web-poll application and will focus on simple form processing and cutting down our code.緊接著Tutorial 3,我們繼續(xù)開發(fā)這個(gè)投票的web應(yīng)用,本章將關(guān)注簡單的表單處理和代碼優(yōu)化#### Write a simple form?#### 編寫一個(gè)簡單的表單Let’s update our poll detail template (“polls/detail.html”) from the last tutorial, so that the template contains an HTML <form> element:讓我們更新一下在上一個(gè)教程中編寫的投票詳細(xì)頁面的模板(“polls/detail.html”),讓它包含一個(gè)HTML<form> 元素:polls/templates/polls/detail.html<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>A quick rundown:簡要說明:+ The above template displays a radio button for each question choice. The value of each radio button is the associated question choice’s ID. The name of each radio button is "choice". That means, when somebody selects one of the radio buttons and submits the form, it’ll send the POST data choice=# where # is the ID of the selected choice. This is the basic concept of HTML forms.+ 在detail網(wǎng)頁模板中,我們?yōu)镼uestion對(duì)應(yīng)的每個(gè)Choice都添加了一個(gè)單選按鈕用于選擇。每個(gè)單選按鈕的value屬性是對(duì)應(yīng)的各個(gè)Choice的ID。每個(gè)單選按鈕的name是"choice"。這意味著,當(dāng)有人選擇一個(gè)單選按鈕并提交表單時(shí),它將發(fā)送一個(gè)POST數(shù)據(jù)choice=#,其中# 為選擇的Choice的ID。這是HTML 表單的基本概念。+ We set the form’s action to {% raw %}{% url 'polls:vote' question.id %}{% raw %}, and we set method="post". Using method="post" (as opposed to method="get") is very important, because the act of submitting this form will alter data server-side. Whenever you create a form that alters data server-side, use method="post". This tip isn’t specific to Django; it’s just good Web development practice.+ 我們設(shè)置表單的action為{% raw %}{% url 'polls:vote' question.id %}{% raw %},并設(shè)置 method="post"。使用method="post"(與其相對(duì)的是method="get")是非常重要的,因?yàn)檫@個(gè)提交表單的行為會(huì)改變服務(wù)器端的數(shù)據(jù)。 無論何時(shí),當(dāng)你需要?jiǎng)?chuàng)建一個(gè)改變服務(wù)器端數(shù)據(jù)的表單時(shí),請使用 method="post"。這不是Django的特定技巧;這是優(yōu)秀的網(wǎng)站開發(fā)實(shí)踐。+ forloop.counter indicates how many times the {% raw %}for{% endraw %} tag has gone through its loop+ forloop.counter 表示{% raw %}for{% endraw %} 標(biāo)簽已經(jīng)循環(huán)的次數(shù)+ Since we’re creating a POST form (which can have the effect of modifying data), we need to worry about Cross Site Request Forgeries. Thankfully, you don’t have to worry too hard, because Django comes with a very easy-to-use system for protecting against it. In short, all POST forms that are targeted at internal URLs should use the {% raw %}{%csrf_token %} {% endraw %}template tag.+ 由于我們創(chuàng)建的是一個(gè)POST表單(會(huì)改變服務(wù)器端的數(shù)據(jù)),那我們就會(huì)擔(dān)心跨站偽造請求的攻擊。但幸運(yùn)的是,我們不需要太擔(dān)心,因?yàn)镈jango自帶了一個(gè)簡單易用的系統(tǒng)來防御。簡而言之,所有提交到目標(biāo)URLs的POST表單,都應(yīng)該設(shè)置{% raw %}{%csrf_token %} {% endraw %}模板標(biāo)簽Now, let’s create a Django view that handles the submitted data and does something with it. Remember, in Tutorial 3, we created a URLconf for the polls application that includes this line:現(xiàn)在,讓我們來創(chuàng)建一個(gè)Django視圖來處理提交的數(shù)據(jù)。 記住,在教程 3中,我們?yōu)橥镀睉?yīng)用創(chuàng)建了一個(gè)URLconf ,包含這一行:polls/urls.pyurl(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),We also created a dummy implementation of the vote() function. Let’s create a real version. Add the following to polls/views.py:我們也創(chuàng)建了一個(gè)沒什么實(shí)際功能的vote()函數(shù)。現(xiàn)在讓我們來真的。將下面代碼添加到polls/views.py:polls/views.pyfrom django.shortcuts import get_object_or_404, renderfrom django.http import HttpResponseRedirect, HttpResponsefrom django.urls import reversefrom .models import Choice, Question# ...def vote(request, question_id):question = get_object_or_404(Question, pk=question_id)try:selected_choice = question.choice_set.get(pk=request.POST['choice'])except (KeyError, Choice.DoesNotExist):# Redisplay the question voting form.return render(request, 'polls/detail.html', {'question': question,'error_message': "You didn't select a choice.",})else:selected_choice.votes += 1selected_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=(question.id,)))This code includes a few things we haven’t covered yet in this tutorial:這段代碼有一些我們未在教程提及的知識(shí):+ request.POST is a dictionary-like object that lets you access submitted data by key name. In this case,request.POST['choice'] returns the ID of the selected choice, as a string. request.POST values are always strings.request.POST 是一個(gè)類似字典的對(duì)象,讓你可以通過關(guān)鍵字的名字獲取提交的數(shù)據(jù)。這個(gè)例子中,request.POST['choice'] 以字符串形式返回選擇的Choice的ID。request.POST 的值永遠(yuǎn)是字符串Note that Django also provides request.GET for accessing GET data in the same way – but we’re explicitly using request.POST in our code, to ensure that data is only altered via a POST call.同樣的,Django也提供了request.GET方法來獲取GET數(shù)據(jù),但我們在代碼中明確地使用request.POST,以保證數(shù)據(jù)只能通過POST調(diào)用改動(dòng)。request.POST['choice'] will raise KeyError if choice wasn’t provided in POST data. The above code checks for KeyError and redisplays the question form with an error message if choice isn’t given.如果POST數(shù)據(jù)沒有choice,request.POST['choice'] 會(huì)拋出 KeyError異常。上面的代碼會(huì)檢查 KeyError,如果沒有對(duì)應(yīng)的choice,將會(huì)重新顯示question表單和一個(gè)錯(cuò)誤信息。After incrementing the choice count, the code returns an HttpResponseRedirect rather than a normal HttpResponse. HttpResponseRedirect takes a single argument: the URL to which the user will be redirected (see the following point for how we construct the URL in this case).在增加choice的票數(shù)之后,代碼返回一個(gè)HttpResponseRedirect而不是HttpResponse,HttpResponseRedirect 只接收一個(gè)參數(shù):用戶將被重定向的URL(下面請看我們?nèi)绾螛?gòu)造這個(gè)URL)As the Python comment above points out, you should always return an HttpResponseRedirect after successfully dealing with POST data. This tip isn’t specific to Django; it’s just good Web development practice.正如上面的Python注釋指出的,你應(yīng)該在成功處理POST數(shù)據(jù)后總是返回一個(gè)HttpResponseRedirect。 這不是Django的特定技巧; 這是那些優(yōu)秀網(wǎng)站在開發(fā)實(shí)踐中形成的共識(shí)。We are using the reverse() function in the HttpResponseRedirect constructor in this example. This function helps avoid having to hardcode a URL in the view function. It is given the name of the view that we want to pass control to and the variable portion of the URL pattern that points to that view. In this case, using the URLconf we set up in Tutorial 3, this reverse() call will return a string like:'/polls/3/results/'在這個(gè)例子中,我們用 reverse() 函數(shù)作為 HttpResponseRedirect的參數(shù),這個(gè)函數(shù)避免了我們在視圖函數(shù)中硬編碼URL。它需要我們給出我們想要跳轉(zhuǎn)的視圖的名字和該視圖所對(duì)應(yīng)的URL模式中需要給該視圖提供的參數(shù)。 在本例中,使用在教程3中設(shè)定的URLconf,調(diào)用 reverse() 將返回一個(gè)這樣的字符串:'/polls/3/results/'where the 3 is the value of question.id. This redirected URL will then call the 'results' view to display the final page.3就是 question.id的值。重定向的URL將調(diào)用'results' 視圖來顯示最終的頁面。As mentioned in Tutorial 3, request is an HttpRequest object. For more on HttpRequest objects, see the request and response documentation.正如教程3提到的,request是一個(gè)HttpRequest對(duì)象,更多關(guān)于HttpRequest對(duì)象的內(nèi)容,請看request and response documentation.After somebody votes in a question, the vote() view redirects to the results page for the question. Let’s write that view:當(dāng)有人對(duì)Question進(jìn)行投票后,vote()視圖將請求重定向到Question的結(jié)果界面。讓我們來編寫這個(gè)視圖:polls/views.pyfrom django.shortcuts import get_object_or_404, renderdef results(request, question_id):question = get_object_or_404(Question, pk=question_id)return render(request, 'polls/results.html', {'question': question})This is almost exactly the same as the detail() view from Tutorial 3. The only difference is the template name. We’ll fix this redundancy later.Now, create a polls/results.html template:這幾乎和教程3中的detail()視圖一模一樣,我們將在稍后解決這個(gè)冗余問題。現(xiàn)在,我們創(chuàng)建 polls/results.html 模板: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>Now, go to /polls/1/ in your browser and vote in the question. You should see a results page that gets updated each time you vote. If you submit the form without having chosen a choice, you should see the error message.> Note> The code for our vote() view does have a small problem. It first gets the selected_choice object from the database, then computes the new value of votes, and then saves it back to the database. If two users of your website try to vote at exactly the same time, this might go wrong: The same value, let’s say 42, will be retrieved for votes. Then, for both users the new value of 43 is computed and saved, but 44 would be the expected value.> This is called a race condition. If you are interested, you can read Avoiding race conditions using F()to learn how you can solve this issue.> 注意> vote()視圖函數(shù)的代碼可能會(huì)有問題。首先它從數(shù)據(jù)庫取出所選的choice對(duì)象,然后計(jì)算votes的最新值,然后保存到數(shù)據(jù)庫。如果你的網(wǎng)站有兩個(gè)用戶在同一時(shí)間投票,這就會(huì)出錯(cuò)了:同樣的值,比如說42,將被votes同時(shí)獲取到,然后,對(duì)兩個(gè)用戶來說最終計(jì)算的值都是43,并保存了,但是實(shí)際應(yīng)該是44.> 這叫做競態(tài)條件。如果你有興趣,你可以閱讀Avoiding race conditions using F()學(xué)習(xí)如何解決這個(gè)問題。#### Use generic views: Less code is better?#### 使用普通view:代碼還是少點(diǎn)好The detail() (from Tutorial 3) and results() views are very simple – and, as mentioned above, redundant. The index() view, which displays a list of polls, is similar.教程3detail()視圖和results()都非常簡單——并且,如上面所說,代碼比較榮譽(yù)。顯示投票列表的index()視圖也和他們類似。These views represent a common case of basic Web development: getting data from the database according to a parameter passed in the URL, loading a template and returning the rendered template. Because this is so common, Django provides a shortcut, called the “generic views” system.這些視圖反映基本的Web開發(fā)中的一個(gè)常見情況:根據(jù)URL中的參數(shù)從數(shù)據(jù)庫中獲取數(shù)據(jù)、載入模板文件然后返回渲染后的模板。 由于這種情況非常普遍,Django提供了一種叫做“generic views”的系統(tǒng)可以方便地進(jìn)行處理。Generic views abstract common patterns to the point where you don’t even need to write Python code to write an app.Generic views會(huì)將常見的模式抽象化,可以使你在編寫app時(shí)甚至不需要編寫Python代碼。Let’s convert our poll app to use the generic views system, so we can delete a bunch of our own code. We’ll just have to take a few steps to make the conversion. We will:1. Convert the URLconf.2. Delete some of the old, unneeded views.3.Introduce new views based on Django’s generic views.Read on for details.讓我們將我們的投票應(yīng)用轉(zhuǎn)換成使用通用視圖系統(tǒng),這樣我們可以刪除許多我們的代碼。我們僅僅需要做以下幾步來完成轉(zhuǎn)換: 我們將:1. 轉(zhuǎn)換URLconf2. 刪除一些舊的、不再需要的代碼。3. 引進(jìn)基于Django通用視圖的新視圖。請繼續(xù)閱讀來了解詳細(xì)信息。> Why the code-shuffle?> Generally, when writing a Django app, you’ll evaluate whether generic views are a good fit for your problem, and you’ll use them from the beginning, rather than refactoring your code halfway through. But this tutorial intentionally has focused on writing the views “the hard way” until now, to focus on core concepts.> You should know basic math before you start using a calculator.> 為什么重構(gòu)代碼> 一般來說,當(dāng)編寫一個(gè)Django應(yīng)用時(shí),你應(yīng)該先評(píng)估一下通用視圖是否可以解決你的問題,你應(yīng)該在一開始使用它,而不是進(jìn)行到一半時(shí)重構(gòu)代碼。本教程目前為止是有意將重點(diǎn)放在以“艱難的方式”編寫視圖,這是為將重點(diǎn)放在核心概念上。> 就像在使用計(jì)算器之前你需要知道基本的數(shù)學(xué)一樣。#### Amend URLconf?#### 改良的URLconf?First, open the polls/urls.py URLconf and change it like so:首先,編輯 polls/urls.py URLconf,修改成如下:polls/urls.pyfrom django.conf.urls import urlfrom . import viewsapp_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'),]Note that the name of the matched pattern in the regexes of the second and third patterns has changed from <question_id> to <pk>.注意在第二個(gè)和第三個(gè)模式的正則表達(dá)式中,匹配的模式的名字由<question_id> 變成 <pk>。#### Amend views?#### 改良的view?Next, we’re going to remove our old index, detail, and results views and use Django’s generic views instead. To do so, open the polls/views.py file and change it like so:接下來我們將去除老的index, detail, and results 視圖,并替換為Django的通用視圖。要完成這些,請打開 polls/views.py,按如下修改:polls/views.pyfrom django.shortcuts import get_object_or_404, renderfrom django.http import HttpResponseRedirectfrom django.urls import reversefrom django.views import genericfrom .models import Choice, Questionclass 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 = Questiontemplate_name = 'polls/detail.html'class ResultsView(generic.DetailView):model = Questiontemplate_name = 'polls/results.html'def vote(request, question_id):... # same as above, no changes needed.We’re using two generic views here: ListView and DetailView. Respectively, those two views abstract the concepts of “display a list of objects” and “display a detail page for a particular type of object.”這里我們用了兩個(gè)通用視圖: ListViewDetailView。這兩個(gè)視圖分別抽象“顯示一個(gè)對(duì)象列表”和“顯示一個(gè)特定類型對(duì)象的詳細(xì)信息頁面”這兩種概念。+ Each generic view needs to know what model it will be acting upon. This is provided using the model attribute.+ 每個(gè)通用視圖需要知道它將作用于哪個(gè)模型。 這由model 屬性提供。+ The DetailView generic view expects the primary key value captured from the URL to be called "pk", so we’ve changed question_id to pk for the generic views.DetailView期望從URL中捕獲名為"pk"的主鍵值,因此我們把polls/urls.py中question_id改成了pk以使通用視圖可以找到主鍵值 。By default, the DetailView generic view uses a template called <app name>/<model name>_detail.html. In our case, it would use the template "polls/question_detail.html". The template_name attribute is used to tell Django to use a specific template name instead of the autogenerated default template name. We also specify the template_name for the results list view – this ensures that the results view and the detail view have a different appearance when rendered, even though they’re both a DetailView behind the scenes.默認(rèn)情況下,通用視圖 DetailView 使用一個(gè)叫做<app name>/<model name>_detail.html的模板。在我們的例子中,它是 "polls/question_detail.html"模板。template_name屬性是用來告訴Django使用一個(gè)指定的模板名字,而不是自動(dòng)生成的默認(rèn)名字。 我們也為results列表視圖指定了template_name —— 這確保results視圖和detail視圖在渲染時(shí)具有不同的外觀,即使它們在后臺(tái)都是同一個(gè) DetailView 。Similarly, the ListView generic view uses a default template called <app name>/<model name>_list.html; we use template_name to tell ListView to use our existing "polls/index.html" template.類似地,ListView 使用一個(gè)叫做<app name>/<model name>_list.html的默認(rèn)模板;我們使用template_name 來告訴ListView 使用我們自己的"polls/index.html"模板。In previous parts of the tutorial, the templates have been provided with a context that contains the question and latest_question_list context variables. For DetailView the question variable is provided automatically – since we’re using a Django model (Question), Django is able to determine an appropriate name for the context variable. However, for ListView, the automatically generated context variable is question_list. To override this we provide the context_object_name attribute, specifying that we want to use latest_question_list instead. As an alternative approach, you could change your templates to match the new default context variables – but it’s a lot easier to just tell Django to use the variable you want.在之前的教程中,提供模板文件時(shí)都帶有一個(gè)包含question 和 latest_question_list 變量的context。對(duì)于DetailView ,question變量會(huì)自動(dòng)提供—— 因?yàn)槲覀兪褂肈jango 的模型 (Question), Django 能夠?yàn)閏ontext 變量決定一個(gè)合適的名字。然而對(duì)于ListView, 自動(dòng)生成的context 變量是question_list。為了覆蓋這個(gè)行為,我們提供 context_object_name 屬性,表示我們想使用latest_question_list。作為一種替換方案,你可以改變你的模板來匹配新的context變量 —— 但直接告訴Django使用你想要的變量會(huì)省事很多。Run the server, and use your new polling app based on generic views.啟動(dòng)服務(wù)器,使用一下基于通用視圖的新投票應(yīng)用。For full details on generic views, see the generic views documentation.更多關(guān)于通用視圖的詳細(xì)信息,請查看 通用視圖文檔.When you’re comfortable with forms and generic views, read part 5 of this tutorial to learn about testing our polls app.當(dāng)你熟悉了表單和通用視圖,請看教程5學(xué)習(xí)如何測試我們的投票應(yīng)用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容