Django 1.10中文文檔:第一個應用 part 4

已經同步到gitbook,想閱讀的請轉到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,我們繼續開發這個投票的web應用,本章將關注簡單的表單處理和代碼優化#### Write a simple form?#### 編寫一個簡單的表單Let’s update our poll detail template (“polls/detail.html”) from the last tutorial, so that the template contains an HTML <form> element:讓我們更新一下在上一個教程中編寫的投票詳細頁面的模板(“polls/detail.html”),讓它包含一個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網頁模板中,我們為Question對應的每個Choice都添加了一個單選按鈕用于選擇。每個單選按鈕的value屬性是對應的各個Choice的ID。每個單選按鈕的name是"choice"。這意味著,當有人選擇一個單選按鈕并提交表單時,它將發送一個POST數據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.+ 我們設置表單的action為{% raw %}{% url 'polls:vote' question.id %}{% raw %},并設置 method="post"。使用method="post"(與其相對的是method="get")是非常重要的,因為這個提交表單的行為會改變服務器端的數據。 無論何時,當你需要創建一個改變服務器端數據的表單時,請使用 method="post"。這不是Django的特定技巧;這是優秀的網站開發實踐。+ forloop.counter indicates how many times the {% raw %}for{% endraw %} tag has gone through its loop+ forloop.counter 表示{% raw %}for{% endraw %} 標簽已經循環的次數+ 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.+ 由于我們創建的是一個POST表單(會改變服務器端的數據),那我們就會擔心跨站偽造請求的攻擊。但幸運的是,我們不需要太擔心,因為Django自帶了一個簡單易用的系統來防御。簡而言之,所有提交到目標URLs的POST表單,都應該設置{% raw %}{%csrf_token %} {% endraw %}模板標簽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:現在,讓我們來創建一個Django視圖來處理提交的數據。 記住,在教程 3中,我們為投票應用創建了一個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:我們也創建了一個沒什么實際功能的vote()函數。現在讓我們來真的。將下面代碼添加到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:這段代碼有一些我們未在教程提及的知識:+ 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 是一個類似字典的對象,讓你可以通過關鍵字的名字獲取提交的數據。這個例子中,request.POST['choice'] 以字符串形式返回選擇的Choice的ID。request.POST 的值永遠是字符串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數據,但我們在代碼中明確地使用request.POST,以保證數據只能通過POST調用改動。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數據沒有choice,request.POST['choice'] 會拋出 KeyError異常。上面的代碼會檢查 KeyError,如果沒有對應的choice,將會重新顯示question表單和一個錯誤信息。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的票數之后,代碼返回一個HttpResponseRedirect而不是HttpResponseHttpResponseRedirect 只接收一個參數:用戶將被重定向的URL(下面請看我們如何構造這個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注釋指出的,你應該在成功處理POST數據后總是返回一個HttpResponseRedirect。 這不是Django的特定技巧; 這是那些優秀網站在開發實踐中形成的共識。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/'在這個例子中,我們用 reverse() 函數作為 HttpResponseRedirect的參數,這個函數避免了我們在視圖函數中硬編碼URL。它需要我們給出我們想要跳轉的視圖的名字和該視圖所對應的URL模式中需要給該視圖提供的參數。 在本例中,使用在教程3中設定的URLconf,調用 reverse() 將返回一個這樣的字符串:'/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將調用'results' 視圖來顯示最終的頁面。As mentioned in Tutorial 3, request is an HttpRequest object. For more on HttpRequest objects, see the request and response documentation.正如教程3提到的,request是一個HttpRequest對象,更多關于HttpRequest對象的內容,請看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:當有人對Question進行投票后,vote()視圖將請求重定向到Question的結果界面。讓我們來編寫這個視圖: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()視圖一模一樣,我們將在稍后解決這個冗余問題。現在,我們創建 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()視圖函數的代碼可能會有問題。首先它從數據庫取出所選的choice對象,然后計算votes的最新值,然后保存到數據庫。如果你的網站有兩個用戶在同一時間投票,這就會出錯了:同樣的值,比如說42,將被votes同時獲取到,然后,對兩個用戶來說最終計算的值都是43,并保存了,但是實際應該是44.> 這叫做競態條件。如果你有興趣,你可以閱讀Avoiding race conditions using F()學習如何解決這個問題。#### Use generic views: Less code is better?#### 使用普通view:代碼還是少點好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()都非常簡單——并且,如上面所說,代碼比較榮譽。顯示投票列表的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開發中的一個常見情況:根據URL中的參數從數據庫中獲取數據、載入模板文件然后返回渲染后的模板。 由于這種情況非常普遍,Django提供了一種叫做“generic views”的系統可以方便地進行處理。Generic views abstract common patterns to the point where you don’t even need to write Python code to write an app.Generic views會將常見的模式抽象化,可以使你在編寫app時甚至不需要編寫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.讓我們將我們的投票應用轉換成使用通用視圖系統,這樣我們可以刪除許多我們的代碼。我們僅僅需要做以下幾步來完成轉換: 我們將:1. 轉換URLconf2. 刪除一些舊的、不再需要的代碼。3. 引進基于Django通用視圖的新視圖。請繼續閱讀來了解詳細信息。> 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.> 為什么重構代碼> 一般來說,當編寫一個Django應用時,你應該先評估一下通用視圖是否可以解決你的問題,你應該在一開始使用它,而不是進行到一半時重構代碼。本教程目前為止是有意將重點放在以“艱難的方式”編寫視圖,這是為將重點放在核心概念上。> 就像在使用計算器之前你需要知道基本的數學一樣。#### 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>.注意在第二個和第三個模式的正則表達式中,匹配的模式的名字由<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.”這里我們用了兩個通用視圖: ListViewDetailView。這兩個視圖分別抽象“顯示一個對象列表”和“顯示一個特定類型對象的詳細信息頁面”這兩種概念。+ Each generic view needs to know what model it will be acting upon. This is provided using the model attribute.+ 每個通用視圖需要知道它將作用于哪個模型。 這由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.默認情況下,通用視圖 DetailView 使用一個叫做<app name>/<model name>_detail.html的模板。在我們的例子中,它是 "polls/question_detail.html"模板。template_name屬性是用來告訴Django使用一個指定的模板名字,而不是自動生成的默認名字。 我們也為results列表視圖指定了template_name —— 這確保results視圖和detail視圖在渲染時具有不同的外觀,即使它們在后臺都是同一個 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 使用一個叫做<app name>/<model name>_list.html的默認模板;我們使用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.在之前的教程中,提供模板文件時都帶有一個包含question 和 latest_question_list 變量的context。對于DetailView ,question變量會自動提供—— 因為我們使用Django 的模型 (Question), Django 能夠為context 變量決定一個合適的名字。然而對于ListView, 自動生成的context 變量是question_list。為了覆蓋這個行為,我們提供 context_object_name 屬性,表示我們想使用latest_question_list。作為一種替換方案,你可以改變你的模板來匹配新的context變量 —— 但直接告訴Django使用你想要的變量會省事很多。Run the server, and use your new polling app based on generic views.啟動服務器,使用一下基于通用視圖的新投票應用。For full details on generic views, see the generic views documentation.更多關于通用視圖的詳細信息,請查看 通用視圖文檔.When you’re comfortable with forms and generic views, read part 5 of this tutorial to learn about testing our polls app.當你熟悉了表單和通用視圖,請看教程5學習如何測試我們的投票應用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容