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

已經同步到gitbook,想閱讀的請轉到gitbook: Django 1.10 中文文檔

Writing your first Django app, part 3?

This tutorial begins where Tutorial 2 left off. We’re continuing the Web-poll application and will focus on creating the public interface – “views.”

緊接著教程二,我們繼續開發投票這個web應用,并將注意力集中在創建對外訪問的“視圖”界面上。

Overview?

概覽?

A view is a “type” of Web page in your Django application that generally serves a specific function and has a specific template. For example, in a blog application, you might have the following views:

  • Blog homepage – displays the latest few entries.
  • Entry “detail” page – permalink page for a single entry.
  • Year-based archive page – displays all months with entries in the given year.
  • Month-based archive page – displays all days with entries in the given month.
  • Day-based archive page – displays all entries in the given day.
  • Comment action – handles posting comments to a given entry.

視圖是Django應用中的一“類”網頁,它通常有一個特定的函數以及一個特定的模板。例如,在博客應用中,可能有以下視圖:

  • 博客首頁 —— 顯示最新發表的文章鏈接。
  • 博客“詳細”頁面 —— 單篇文章的詳細頁面。
  • 基于年份的歸檔頁面 —— 顯示某給定年份里所有月份發表過的博客。
  • 基于月份的歸檔頁面 —— 顯示在給定月份中發表過所有文章。
  • 基于日期的歸檔頁面 —— 顯示在給定日期中發表過的所有文章鏈接。
  • 評論 —— 評論某博客

In our poll application, we’ll have the following four views:

  • Question “index” page – displays the latest few questions.
  • Question “detail” page – displays a question text, with no results but with a form to vote.
  • Question “results” page – displays results for a particular question.
  • Vote action – handles voting for a particular choice in a particular question.

在我們的投票應用中,將有以下四個視圖:

  • Question首頁 —— 顯示最新發布的幾個Question。
  • Question“詳細”頁面 —— 顯示單個Question的具體內容,有一個投票的表單,但沒有投票結果。
  • Question“結果”頁面 —— 顯示某Question的投票結果。
  • 投票功能 —— 可對Question中某個Choice的進行投票。

In Django, web pages and other content are delivered by views. Each view is represented by a simple Python function (or method, in the case of class-based views). Django will choose a view by examining the URL that's requested (to be precise, the part of the URL after the domain name).

在Django中,網頁的頁面和其他內容都是由視圖來傳遞的(視圖對WEB請求進行回應)。 每個視圖都是由一個簡單的Python函數(或者是基于類的視圖的方法)。Django通過檢查請求的URL(準確地說,是URL里域名之后的那部分)來選擇使用哪個視圖。

Now in your time on the web you may have come across such beauties as “ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”. You will be pleased to know that Django allows us much more elegant URL patterns than that.

平日你上網時,可能會遇到像 “ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”這樣優美的URL。 你將會愉快地了解到,Django允許我們使用更加優雅的URL模式。

A URL pattern is simply the general form of a URL - for example: /newsarchive/<year>/<month>/.

URL模式就是一個URL的通用形式 —— 例如: /newsarchive/<year>/<month>/。

To get from a URL to a view, Django uses what are known as ‘URLconfs’. A URLconf maps URL patterns (described as regular expressions) to views.

Django使用叫做‘URLconfs’的配置來為URL匹配視圖。 一個URLconf負責將URL模式(由正則表達式編寫)匹配到視圖。

This tutorial provides basic instruction in the use of URLconfs, and you can refer to django.urls for more information.

本教程有URLconfs的基本使用方法,你可以在 django.urls看到更詳細的信息 。

Writing more views?
Now let’s add a few more views to polls/views.py
. These views are slightly different, because they take an argument:

編寫更多的視圖

現在讓我們給polls/views.py添加一些更多的視圖。這些視圖和之前的略有不同,因為它們另帶了一個參數:

polls/views.py

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

Wire these new views into the polls.urls
module by adding the following url() calls:

通過下面的url() 調用將這些新的視圖和polls.urls模塊關聯起來:

polls/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    # ex: /polls/5/
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]

Take a look in your browser, at “/polls/34/”. It’ll run the detail()
method and display whatever ID you provide in the URL. Try “/polls/34/results/” and “/polls/34/vote/” too – these will display the placeholder results and voting pages.

看看你的瀏覽器,輸入“/polls/34/”它將運行detail()方法并顯示你在URL中提供的ID。 再試一下“/polls/34/results/”和“/polls/34/vote/” —— 它們將顯示出對應的結果界面和投票界面。

When somebody requests a page from your website – say, “/polls/34/”, Django will load the mysite.urls Python module because it’s pointed to by the ROOT_URLCONF
setting. It finds the variable named urlpatterns
and traverses the regular expressions in order. After finding the match at '^polls/', it strips off the matching text ("polls/") and sends the remaining text – "34/" – to the ‘polls.urls’ URLconf for further processing. There it matches r'^(?P<question_id>[0-9]+)/$', resulting in a call to the detail() view like so:

當有人請求你的網站的一個頁面時 —— 比如“/polls/34/”,根據ROOT_URLCONF 的設置,Django將加載mysite.urls Python 模塊。它查找到名為urlpatterns的變量并按順序匹配其中的正則表達式。當匹配到'^polls/',它截斷了匹配到的字符串('polls/'),然后將剩下的字符串– "34/" – 傳遞到‘polls.urls’這個URLconf進一步處理。在那它匹配到了 r'^(?P<question_id>[0-9]+)/$',然后再以以下方式調用detail()函數:

detail(request=<HttpRequest object>, question_id='34')

The question_id='34' part comes from (?P<question_id>[0-9]+). Using parentheses around a pattern “captures” the text matched by that pattern and sends it as an argument to the view function; ?P<question_id> defines the name that will be used to identify the matched pattern; and [0-9]+ is a regular expression to match a sequence of digits (i.e., a number).

question_id='34'部分來自(?P<question_id>[0-9]+)。使用模式周圍的括號“捕獲”該模式匹配的文本,并將其作為參數發送到視圖函數;?P<question_id> 定義一個名字,它將用于標識匹配的模式;[0-9]+是匹配一串數字的正則表達式。

Because the URL patterns are regular expressions, there really is no limit on what you can do with them. And there’s no need to add URL cruft such as .html – unless you want to, in which case you can do something like this:

因為URL模式是正則表達式,你如何使用它們沒有什么限制。 不需要添加像.html這樣繁瑣的URL —— 除非你執意這么做,比如說你可以寫成這樣:

url(r'^polls/latest\.html$', views.index),

But, don’t do that. It’s silly.

但是,別這做,這很沒必要。

Write views that actually do something?

**寫點有實際作用的視圖

Each view is responsible for doing one of two things: returning an HttpResponse object containing the content for the requested page, or raising an exception such as Http404. The rest is up to you.

每個視圖負責一件事:要么返回一個包含所請求頁面內容的HttpResponse對象,要么拋出一個諸如404的異常。這取決于你。

Your view can read records from a database, or not. It can use a template system such as Django’s – or a third-party Python template system – or not. It can generate a PDF file, output XML, create a ZIP file on the fly, anything you want, using whatever Python libraries you want.

你的視圖可以從數據庫中讀取記錄,或者不讀取數據庫。你可以用Django自帶的模板系統或第三方的模板系統——甚至不用模板。 你還可以動態地生成一個PDF文件、輸出XML文件、創建一個ZIP文件,使用你想用的Python 庫生成任何你想要的。

All Django wants is that HttpResponse. Or an exception.

Django只要求返回一個HttpResponse,或者拋出異常。

Because it’s convenient, let’s use Django’s own database API, which we covered in Tutorial 2. Here’s one stab at a new index() view, which displays the latest 5 poll questions in the system, separated by commas, according to publication date:

讓我們來用用教程第二部分學到的數據庫API,它是非常方便的。下面是一個新的index()視圖,它列出了最近的5個投票Question記錄,并用逗號隔開,以發布日期排序:

polls/views.py

from django.http import HttpResponse

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

# Leave the rest of the views (detail, results, vote) unchanged

There’s a problem here, though: the page’s design is hard-coded in the view. If you want to change the way the page looks, you’ll have to edit this Python code. So let’s use Django’s template system to separate the design from Python by creating a template that the view can use.

這里有一個問題:頁面的設計被硬編碼在視圖中。 如果你想更改頁面的外觀,就得編輯這段Python代碼。 因此,讓我們使用Django的模板系統,通過創建一個視圖能夠調用的模板,將頁面的html代碼從Python中分離出來。

First, create a directory called templates in your polls
directory. Django will look for templates in there.

首先,在你的polls目錄下創建一個叫做 templates的目錄。Django將在這里查找模板。

Your project’s TEMPLATES setting describes how Django will load and render templates. The default settings file configures a DjangoTemplates
backend whose APP_DIRS option is set to True. By convention DjangoTemplates looks for a “templates” subdirectory in each of the INSTALLED_APPS.

你項目的TEMPLATES 設置決定了Django如何加載和渲染模板。默認配置下,Django模板引擎在APP_DIRS設置為True。Django模板引擎會根據[INSTALLED_APPS]目錄下查找名為templates的子目錄。

Within the templates directory you have just created, create another directory called polls, and within that create a file called index.html. In other words, your template should be at polls/templates/polls/index.html. Because of how the app_directories template loader works as described above, you can refer to this template within Django simply as polls/index.html.

在你剛剛創建的templates目錄中,創建另外一個目錄polls,并在其中創建一個文件index.html。也就是說,你的模板應該位于 polls/templates/polls/index.html。由于app_directories 模板加載器按照上面描述的方式工作,在Django中你可以簡單地用polls/index.html引用這個模板。

Template namespacing

Now we might be able to get away with putting our templates directly in polls/templates (rather than creating another polls subdirectory), but it would actually be a bad idea. Django will choose the first template it finds whose name matches, and if you had a template with the same name in a different application, Django would be unable to distinguish between them. We need to be able to point Django at the right one, and the easiest way to ensure this is by namespacing them. That is, by putting those templates inside another directory named for the application itself.

模板命名空間

現在,我們可以直接將我們的模板放在polls/templates中(而不用創建另外一個polls子目錄),但實際上這是個壞主意。Django將選擇它找到的名字匹配的第一個模板文件,如果你在不同的應用有相同名字的模板文件,Django將不能區分它們。我們需要將Django指向正確的模板,最簡單的方式是使用命名空間。具體實現方式是,將這些模板文件放在以應用的名字來命名的另一個目錄下。

Put the following code in that template:

將以下代碼寫入剛創建的模板:

polls/templates/polls/index.html
{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

Now let’s update our index view in polls/views.py to use the template:

現在我們用剛剛的模板來更新polls/views.py的視圖函數:
polls/views.py

from django.http import HttpResponse
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))

That code loads the template called polls/index.html
and passes it a context. The context is a dictionary mapping template variable names to Python objects.

那段代碼加載了 polls/index.html模板,并傳遞了一個context對象,context是一個字典, 將模板的變量和python對象一一對應。

Load the page by pointing your browser at “/polls/”, and you should see a bulleted-list containing the “What’s up” question from Tutorial 2. The link points to the question’s detail page.

瀏覽器訪問“/polls”,你講看到一個列表,包含了我們在教程二創建的 “What’s up” question,這個鏈接指向了Question的詳細頁

A shortcut: [render()](https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#django.shortcuts.render)?

小技巧: [render()]

It’s a very common idiom to load a template, fill a context and return an HttpResponse object with the result of the rendered template. Django provides a shortcut. Here’s the full index() view, rewritten:

加載模板、填充一個context 然后返回一個含有模板渲染結果的HttpResponse對象是非常頻繁的。
Django為此提供一個快捷方式。下面是重寫后的index()視圖:

polls/views.py

from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

Note that once we’ve done this in all these views, we no longer need to import loader and HttpResponse (you’ll want to keep HttpResponse if you still have the stub methods for detail, results, and vote).

注意,一旦我們在所有的視圖上都應用這個快捷函數,我們將不再需要導入loader and HttpResponse(如果你沒有改變先前的detail、results和 vote方法,你將需要在導入中保留HttpResponse
)。

The render() function takes the request object as its first argument, a template name as its second argument and a dictionary as its optional third argument. It returns an HttpResponse object of the given template rendered with the given context.

render()函數將請求對象作為它的第一個參數,模板的名字作為它的第二個參數,一個字典作為它可選的第三個參數。
它返回一個 HttpResponse對象,含有用給定的context 渲染后的模板。

Raising a 404 error?

Now, let’s tackle the question detail view – the page that displays the question text for a given poll. Here’s the view:

現在讓我們來處理question detail視圖——顯示某question內容的頁面,下面是該視圖:

polls/views.py

from django.http import Http404
from django.shortcuts import render

from .models import Question
# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

The new concept here: The view raises the Http404 exception if a question with the requested ID doesn’t exist.

新概念:這個視圖拋出了Http404異常,如果請求的question ID不存在的情況下。

We’ll discuss what you could put in that polls/detail.html
template a bit later, but if you’d like to quickly get the above example working, a file containing just:

稍后我們將討論polls/detail.html模板可以寫點什么。但是如果你想快速讓上面的例子工作,如下就可以:

polls/templates/polls/detail.html

{{ question }}

A shortcut: get_object_or_404()?

快捷方式:get_object_or_404()?

It’s a very common idiom to use get() and raise Http404 if the object doesn’t exist. Django provides a shortcut. Here’s the detail() view, rewritten:

當用 get()時,如果對象不存在拋出 Http404異常是很常用的。Django提供了一個快捷方式,重寫后的detail()視圖:

polls/views.py

from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

The get_object_or_404() function takes a Django model as its first argument and an arbitrary number of keyword arguments, which it passes to the get() function of the model’s manager. It raises Http404 if the object doesn’t exist.

get_object_or_404() 函數將Django模型作為它的第一個參數,任意數量關鍵詞參數,它將傳遞給作為模型管理器的get()函數,如果對象不存在,它就引發一個 Http404異常。

**Philosophy
Why do we use a helper function get_object_or_404() instead of automatically catching theObjectDoesNotExist exceptions at a higher level, or having the model API raise Http404 instead of ObjectDoesNotExist?

為什么我們要用一個輔助函數 get_object_or_404()而不是在上層捕獲ObjectDoesNotExist 異常,或者讓模型的API引發 Http404而不是ObjectDoesNotExist

Because that would couple the model layer to the view layer. One of the foremost design goals of Django is to maintain loose coupling. Some controlled coupling is introduced in the django.shortcuts module.

因為那樣會將模型層與視圖層耦合。Django 最重要的設計目標就是保持松耦合。一些可控的耦合在 django.shortcuts 有介紹。

There’s also a get_list_or_404() function, which works just as get_object_or_404() – except usingfilter() instead of get(). It raises Http404 if the list is empty.

還有一個 get_list_or_404() 函數, 原理和get_object_or_404() 一樣——差別在與它用filter()而不是 get(). 當列表為空時,它拋出Http404 異常。

Use the template system?

使用模板系統

Back to the detail() view for our poll application. Given the context variable question, here’s what the polls/detail.html template might look like:

回到我們投票應用的detail()視圖。 根據context 變量question,下面是polls/detail.html模板可能的樣子:
polls/templates/polls/detail.html

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

The template system uses dot-lookup syntax to access variable attributes. In the example of {% raw %}{{question.question_text }}{% endraw %}, first Django does a dictionary lookup on the object question. Failing that, it tries an attribute lookup – which works, in this case. If attribute lookup had failed, it would’ve tried a list-index lookup.

模板系統使用點號查找語法來訪問變量的屬性。 在 {% raw %}{{question.question_text }}{% endraw %}這個例子中,Django首先將在question對象當做字典查詢。如果失敗,Django會接著嘗試按屬性查詢 —— 在這個例子中,屬性查詢會成功。如果屬性查詢也失敗,Django將嘗試列表索引查詢。

Method-calling happens in the {% raw %} [{% for %}] {% endraw %}(https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#std:templatetag-for) loop: question.choice_set.all
is interpreted as the Python codequestion.choice_set.all()
, which returns an iterable of Choice
objects and is suitable for use in the {% raw %} {%for %}
{% endraw %} tag.

方法調用發生在 {% raw %} {%for %}
{% endraw %} 循環中:question.choice_set.all被解釋為Python的代碼question.choice_set.all(),它返回一個由Choice對象組成的可迭代對象,并將其用于{% raw %} {%for %} {% endraw %} 標簽。

See the template guide for more about templates.

查看template guide 了解更多模板信息

Removing hardcoded URLs in templates?

模板中去除硬編碼

Remember, when we wrote the link to a question in the polls/index.html template, the link was partially hardcoded like this:

請記住,當我們在polls/index.html模板中編寫一個指向Question的鏈接時,鏈接中一部分是硬編碼的:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

The problem with this hardcoded, tightly-coupled approach is that it becomes challenging to change URLs on projects with a lot of templates. However, since you defined the name argument in the url() functions in the polls.urls module, you can remove a reliance on specific URL paths defined in your url configurations by using the{% raw %} {% url %}{% endraw %} template tag:

這種緊耦合的硬編碼有一個問題,就是如果我們想在模板眾多的項目中修改URLs,將會變得非常困難。 但是,如果你在polls.urls模塊的 url() 函數中定義了name 參數,你可以通過使用{% raw %} {% url %}{% endraw %}模板標簽來移除對你的URL配置中定義的特定的URL的依賴:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

The way this works is by looking up the URL definition as specified in the polls.urls module. You can see exactly where the URL name of ‘detail’ is defined below:

它的工作原理是在polls.urls模塊里查找指定的URL。你可以看到名為‘detail’的URL的準確定義:

...
# the 'name' value as called by the {% url %} template tag
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...

If you want to change the URL of the polls detail view to something else, perhaps to something like polls/specifics/12/ instead of doing it in the template (or templates) you would change it in polls/urls.py:

如果你想把polls應用中detail視圖的URL改成其它樣子比如 polls/specifics/12/,就可以不必在該模板(或者多個模板)中修改它,只需要修改 polls/urls.py:

...
# added the word 'specifics'
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...

Namespacing URL names?

URL命名空間

The tutorial project has just one app, polls. In real Django projects, there might be five, ten, twenty apps or more. How does Django differentiate the URL names between them? For example, the polls app has a detail view, and so might an app on the same project that is for a blog. How does one make it so that Django knows which app view to create for a url when using the {% raw %} {% url %}{% endraw %} template tag?

教程中的這個項目只有一個應用polls。在真實的Django項目中,可能會有五個、十個、二十個或者更多的應用。 Django如何區分它們URL的名字呢? 例如,polls 應用具有一個detail 視圖,相同項目中的博客應用可能也有這樣一個視圖。當使用模板標簽{% raw %} {% url %}{% endraw %} 時,人們該如何做才能使得Django知道為一個URL創建哪個應用的視圖?

The answer is to add namespaces to your URLconf. In the polls/urls.py file, go ahead and add an app_name
to set the application namespace:

答案是在你的主URLconf下添加命名空間。 在mysite/urls.py文件中,增加一個app_name的變量作為命名空間:

polls/urls.py

from django.conf.urls import url

from . import views

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

Now change your polls/index.html template from:

現在修改polls/index.html 模板:

polls/templates/polls/index.html

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

to point at the namespaced detail view:

并指向具有命名空間的detail的視圖函數

polls/templates/polls/index.html

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

When you’re comfortable with writing views, read part 4 of this tutorial to learn about simple form processing and generic views.

當你對你寫的視圖感到滿意后,請閱讀教程4來了解簡單的表單處理和通用視圖。

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

推薦閱讀更多精彩內容