{"title":"编写你的第一个 Django 应用，第 3 部分","version":"5.1","locale":"zh-hans","docname":"intro/tutorial03","url":"/zh-hans/5.1/intro/tutorial03/","canonical":"https://djangodocs.dev/zh-hans/5.1/intro/tutorial03/","summary":"本教程从 教程2 结束的地方开始。我们将继续开发网络投票应用程序，并将着重于创建公共接口——“视图”。 从哪里获得帮助： 如果你在阅读本教程的过程中有任何疑问，可以前往 FAQ 的 获取帮助 的版块。 概况 Link to this heading # Django…","html":"<h1>编写你的第一个 Django 应用，第 3 部分<a class=\"heading-anchor\" href=\"#writing-your-first-django-app-part-3\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>本教程从 <a class=\"reference internal\" href=\"/zh-hans/5.1/intro/tutorial02/\"><span class=\"doc\">教程2</span></a> 结束的地方开始。我们将继续开发网络投票应用程序，并将着重于创建公共接口——“视图”。</p>\n<aside class=\"admonition-where-to-get-help admonition\">\n<p class=\"admonition-title\">从哪里获得帮助：</p>\n<p>如果你在阅读本教程的过程中有任何疑问，可以前往 FAQ 的 <a class=\"reference internal\" href=\"/zh-hans/5.1/faq/help/\"><span class=\"doc\">获取帮助</span></a> 的版块。</p>\n</aside>\n<section id=\"overview\">\n<h2>概况<a class=\"heading-anchor\" href=\"#overview\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Django 中的视图的概念是「一类具有相同功能和模板的网页的集合」。比如，在一个博客应用中，你可能会创建如下几个视图：</p>\n<ul class=\"simple\">\n<li><p>博客首页——展示最近的几项内容。</p></li>\n<li><p>内容“详情”页——详细展示某项内容。</p></li>\n<li><p>以年为单位的归档页——展示选中的年份里各个月份创建的内容。</p></li>\n<li><p>以月为单位的归档页——展示选中的月份里各天创建的内容。</p></li>\n<li><p>以天为单位的归档页——展示选中天里创建的所有内容。</p></li>\n<li><p>评论处理器——用于响应为一项内容添加评论的操作。</p></li>\n</ul>\n<p>而在我们的投票应用中，我们需要下列几个视图：</p>\n<ul class=\"simple\">\n<li><p>问题索引页——展示最近的几个投票问题。</p></li>\n<li><p>问题详情页——展示某个投票的问题和不带结果的选项列表。</p></li>\n<li><p>问题结果页——展示某个投票的结果。</p></li>\n<li><p>投票处理器——用于响应用户为某个问题的特定选项投票的操作。</p></li>\n</ul>\n<p>在 Django 中，网页和其他内容都是从视图派生而来。每一个视图表现为一个 Python 函数（或者说方法，如果是在基于类的视图里的话）。Django 将会根据用户请求的 URL 来选择使用哪个视图（更准确的说，是根据 URL 中域名之后的部分）。</p>\n<p>在你上网的过程中，很可能看见过像这样美丽的 URL：<code class=\"docutils literal notranslate\"><span class=\"pre\">ME2/Sites/dirmod.htm?sid=&amp;type=gen&amp;mod=Core+Pages&amp;gid=A6CD4967199A42D9B65B1B</span></code>。别担心，Django 里的  <em>URL 样式</em> 要比这优雅的多！</p>\n<p>URL 样式是 URL 的一般形式 - 例如：<code class=\"docutils literal notranslate\"><span class=\"pre\">/newsarchive/&lt;year&gt;/&lt;month&gt;/</span></code>。</p>\n<p>为了将 URL 和视图关联起来，Django 使用了 'URLconfs' 来配置。URLconf 将 URL 模式映射到视图。</p>\n<p>本教程只会介绍 URLconf 的基础内容，你可以看看 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/urls/\"><span class=\"doc\">URL调度器</span></a> 以获取更多内容。</p>\n</section>\n<section id=\"writing-more-views\">\n<h2>编写更多视图<a class=\"heading-anchor\" href=\"#writing-more-views\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>现在让我们向 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code> 里添加更多视图。这些视图有一些不同，因为他们接收参数：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">detail</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"s2\">&quot;You&#39;re looking at question </span><span class=\"si\">%s</span><span class=\"s2\">.&quot;</span> <span class=\"o\">%</span> <span class=\"n\">question_id</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">results</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;You&#39;re looking at the results of question </span><span class=\"si\">%s</span><span class=\"s2\">.&quot;</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"n\">response</span> <span class=\"o\">%</span> <span class=\"n\">question_id</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">vote</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"s2\">&quot;You&#39;re voting on question </span><span class=\"si\">%s</span><span class=\"s2\">.&quot;</span> <span class=\"o\">%</span> <span class=\"n\">question_id</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>把这些新视图添加进 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls.urls</span></code>  模块里，只要添加几个 <code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">url()</span></code>  函数调用就行：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">path</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">views</span>\n\n<span class=\"n\">urlpatterns</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"c1\"># ex: /polls/</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">index</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;index&quot;</span><span class=\"p\">),</span>\n    <span class=\"c1\"># ex: /polls/5/</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&lt;int:question_id&gt;/&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">detail</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;detail&quot;</span><span class=\"p\">),</span>\n    <span class=\"c1\"># ex: /polls/5/results/</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&lt;int:question_id&gt;/results/&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">results</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;results&quot;</span><span class=\"p\">),</span>\n    <span class=\"c1\"># ex: /polls/5/vote/</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&lt;int:question_id&gt;/vote/&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">vote</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;vote&quot;</span><span class=\"p\">),</span>\n<span class=\"p\">]</span>\n</code></pre></figure>\n<p>在浏览器中查看 &quot;/polls/34/&quot;。它将运行 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> 函数并显示您在 URL 中提供的任何 ID。也可以尝试 &quot;/polls/34/results/&quot; 和 &quot;/polls/34/vote/&quot;，这些将显示占位的结果和投票页面。</p>\n<p>当有人请求你网站的页面，比如说，&quot;/polls/34/&quot;，Django 会加载 <code class=\"docutils literal notranslate\"><span class=\"pre\">mysite.urls</span></code> Python 模块，因为它被 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/settings/#std-setting-ROOT_URLCONF\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">ROOT_URLCONF</span></code></a> 设置指向。它会找到名为 <code class=\"docutils literal notranslate\"><span class=\"pre\">urlpatterns</span></code> 的变量并按顺序遍历这些模式。在找到匹配项 <code class=\"docutils literal notranslate\"><span class=\"pre\">'polls/'</span></code> 之后，它会剥离匹配的文本（<code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;polls/&quot;</span></code>），然后将剩余的文本 -- <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;34/&quot;</span></code> -- 发送给 'polls.urls' URL 配置以进行进一步处理。在那里，它会匹配 <code class=\"docutils literal notranslate\"><span class=\"pre\">'&lt;int:question_id&gt;/'</span></code>，从而调用 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> 视图，如下所示：</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"go\">detail(request=&lt;HttpRequest object&gt;, question_id=34)</span>\n</code></pre></div>\n<p>问题 <code class=\"docutils literal notranslate\"><span class=\"pre\">question_id=34</span></code> 来自 <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;int:question_id&gt;</span></code>。使用尖括号 &quot;获得&quot; 网址部分后发送给视图函数作为一个关键字参数。字符串的 <code class=\"docutils literal notranslate\"><span class=\"pre\">question_id</span></code> 部分定义了要使用的名字，用来识别相匹配的模式，而 <code class=\"docutils literal notranslate\"><span class=\"pre\">int</span></code> 部分是一种转换形式，用来确定应该匹配网址路径的什么模式。冒号 (<code class=\"docutils literal notranslate\"><span class=\"pre\">:</span></code>) 用来分隔转换形式和模式名。</p>\n</section>\n<section id=\"write-views-that-actually-do-something\">\n<h2>写一个真正有用的视图<a class=\"heading-anchor\" href=\"#write-views-that-actually-do-something\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>每个视图必须要做的只有两件事：返回一个包含被请求页面内容的 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a> 对象，或者抛出一个异常，比如 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 。至于你还想干些什么，随便你。</p>\n<p>你的视图可以从数据库里读取记录，可以使用一个模板引擎（比如 Django 自带的，或者其他第三方的），可以生成一个 PDF 文件，可以输出一个 XML，创建一个 ZIP 文件，你可以做任何你想做的事，使用任何你想用的 Python 库。</p>\n<p>Django 只要求返回的是一个 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a> ，或者抛出一个异常。</p>\n<p>因为 Django 自带的数据库 API 很方便，我们曾在 <a class=\"reference internal\" href=\"/zh-hans/5.1/intro/tutorial02/\"><span class=\"doc\">教程第 2 部分</span></a> 中学过，所以我们试试在视图里使用它。我们在 <code class=\"docutils literal notranslate\"><span class=\"pre\">index()</span></code>  函数里插入了一些新内容，让它能展示数据库里以发布日期排序的最近 5 个投票问题，以空格分割：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.http</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">HttpResponse</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">index</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span>\n    <span class=\"n\">latest_question_list</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s2\">&quot;-pub_date&quot;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n    <span class=\"n\">output</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;, &quot;</span><span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">([</span><span class=\"n\">q</span><span class=\"o\">.</span><span class=\"n\">question_text</span> <span class=\"k\">for</span> <span class=\"n\">q</span> <span class=\"ow\">in</span> <span class=\"n\">latest_question_list</span><span class=\"p\">])</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"n\">output</span><span class=\"p\">)</span>\n\n\n<span class=\"c1\"># Leave the rest of the views (detail, results, vote) unchanged</span>\n</code></pre></figure>\n<p>这里有个问题：页面的设计写死在视图函数的代码里的。如果你想改变页面的样子，你需要编辑 Python 代码。所以让我们使用 Django 的模板系统，只要创建一个视图，就可以将页面的设计从代码中分离出来。</p>\n<p>首先，在你的 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 目录里创建一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">templates</span></code> 目录。Django 将会在这个目录里查找模板文件。</p>\n<p>你项目的 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/settings/#std-setting-TEMPLATES\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">TEMPLATES</span></code></a> 配置项描述了 Django 如何载入和渲染模板。默认的设置文件设置了 <code class=\"docutils literal notranslate\"><span class=\"pre\">DjangoTemplates</span></code> 后端，并将  <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/settings/#std-setting-TEMPLATES-APP_DIRS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">APP_DIRS</span></code></a>  设置成了 True。这一选项将会让 <code class=\"docutils literal notranslate\"><span class=\"pre\">DjangoTemplates</span></code>  在每个 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/settings/#std-setting-INSTALLED_APPS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">INSTALLED_APPS</span></code></a>  文件夹中寻找 &quot;templates&quot; 子目录。这就是为什么尽管我们没有像在第二部分中那样修改 DIRS 设置，Django 也能正确找到 polls 的模板位置的原因。</p>\n<p>在你刚刚创建的 <code class=\"docutils literal notranslate\"><span class=\"pre\">templates</span></code> 目录里，再创建一个目录 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code>，然后在其中新建一个文件 <code class=\"docutils literal notranslate\"><span class=\"pre\">index.html</span></code> 。换句话说，你的模板文件的路径应该是  <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/index.html</span></code> 。因为``app_directories`` 模板加载器是通过上述描述的方法运行的，所以 Django 可以引用到 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/index.html</span></code>  这一模板了。</p>\n<aside class=\"admonition-template-namespacing admonition\">\n<p class=\"admonition-title\">模板命名空间</p>\n<p>虽然我们现在可以将模板文件直接放在  <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates</span></code> 文件夹中（而不是再建立一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 子文件夹），但是这样做不太好。Django 将会选择第一个匹配的模板文件，如果你有一个模板文件正好和另一个应用中的某个模板文件重名，Django 没有办法 <em>区分</em> 它们。我们需要帮助 Django 选择正确的模板，最好的方法就是把他们放入各自的 <em>命名空间</em> 中，也就是把这些模板放入一个和 <em>自身</em> 应用重名的子文件夹里。</p>\n</aside>\n<p>将下面的代码输入到刚刚创建的模板文件中：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/index.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"cp\">{%</span> <span class=\"k\">if</span> <span class=\"nv\">latest_question_list</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n    <span class=\"cp\">{%</span> <span class=\"k\">for</span> <span class=\"nv\">question</span> <span class=\"k\">in</span> <span class=\"nv\">latest_question_list</span> <span class=\"cp\">%}</span>\n        <span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;/polls/</span><span class=\"cp\">{{</span> <span class=\"nv\">question.id</span> <span class=\"cp\">}}</span><span class=\"s\">/&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n    <span class=\"cp\">{%</span> <span class=\"k\">endfor</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;/</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">else</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;</span><span class=\"nt\">p</span><span class=\"p\">&gt;</span>No polls are available.<span class=\"p\">&lt;/</span><span class=\"nt\">p</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">endif</span> <span class=\"cp\">%}</span>\n</code></pre></figure>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>为了让教程看起来不那么长，所有的模板文件都只写出了核心代码。在你自己创建的项目中，你应该使用 <a class=\"reference external\" href=\"https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started#anatomy_of_an_html_document\">完整的 HTML 文档</a> 。</p>\n</aside>\n<p>然后，让我们更新一下 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code> 里的 <code class=\"docutils literal notranslate\"><span class=\"pre\">index</span></code> 视图来使用模板：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.http</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">HttpResponse</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.template</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">loader</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">index</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span>\n    <span class=\"n\">latest_question_list</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s2\">&quot;-pub_date&quot;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n    <span class=\"n\">template</span> <span class=\"o\">=</span> <span class=\"n\">loader</span><span class=\"o\">.</span><span class=\"n\">get_template</span><span class=\"p\">(</span><span class=\"s2\">&quot;polls/index.html&quot;</span><span class=\"p\">)</span>\n    <span class=\"n\">context</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"s2\">&quot;latest_question_list&quot;</span><span class=\"p\">:</span> <span class=\"n\">latest_question_list</span><span class=\"p\">,</span>\n    <span class=\"p\">}</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"n\">template</span><span class=\"o\">.</span><span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">context</span><span class=\"p\">,</span> <span class=\"n\">request</span><span class=\"p\">))</span>\n</code></pre></figure>\n<p>上述代码的作用是，载入 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/index.html</span></code>  模板文件，并且向它传递一个上下文(context)。这个上下文是一个字典，它将模板内的变量映射为 Python 对象。</p>\n<p>用你的浏览器访问 &quot;/polls/&quot; ，你将会看见一个无序列表，列出了我们在  <a class=\"reference internal\" href=\"/zh-hans/5.1/intro/tutorial02/\"><span class=\"doc\">教程第 2 部分</span></a> 中添加的 “What's up” 投票问题，链接指向这个投票的详情页。</p>\n<section id=\"a-shortcut-render\">\n<h3>一个快捷函数： <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/shortcuts/#django.shortcuts.render\" title=\"django.shortcuts.render\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">render()</span></code></a><a class=\"heading-anchor\" href=\"#a-shortcut-render\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>「载入模板，填充上下文，再返回由它生成的 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a>  对象」是一个非常常用的操作流程。于是 Django 提供了一个快捷函数，我们用它来重写  <code class=\"docutils literal notranslate\"><span class=\"pre\">index()</span></code>  视图：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">render</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">index</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span>\n    <span class=\"n\">latest_question_list</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s2\">&quot;-pub_date&quot;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n    <span class=\"n\">context</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s2\">&quot;latest_question_list&quot;</span><span class=\"p\">:</span> <span class=\"n\">latest_question_list</span><span class=\"p\">}</span>\n    <span class=\"k\">return</span> <span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"s2\">&quot;polls/index.html&quot;</span><span class=\"p\">,</span> <span class=\"n\">context</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>注意到，我们不再需要导入 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/templates/#module-django.template.loader\" title=\"django.template.loader\"><code class=\"xref py py-mod docutils literal notranslate\"><span class=\"pre\">loader</span></code></a>  和 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a> 。不过如果你还有其他函数（比如说 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail</span></code>, <code class=\"docutils literal notranslate\"><span class=\"pre\">results</span></code>, 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">vote</span></code> ）需要用到它的话，就需要保持  <code class=\"docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code>   的导入。</p>\n<p>The <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/shortcuts/#django.shortcuts.render\" title=\"django.shortcuts.render\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">render()</span></code></a> function takes the request object as its\nfirst argument, a template name as its second argument and a dictionary as its\noptional third argument. It returns an <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a>\nobject of the given template rendered with the given context.</p>\n</section>\n</section>\n<section id=\"raising-a-404-error\">\n<h2>抛出 404 错误<a class=\"heading-anchor\" href=\"#raising-a-404-error\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>现在，我们来处理投票详情视图——它会显示指定投票的问题标题。下面是这个视图的代码：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.http</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Http404</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">render</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"c1\"># ...</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">detail</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"k\">try</span><span class=\"p\">:</span>\n        <span class=\"n\">question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"n\">question_id</span><span class=\"p\">)</span>\n    <span class=\"k\">except</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">DoesNotExist</span><span class=\"p\">:</span>\n        <span class=\"k\">raise</span> <span class=\"n\">Http404</span><span class=\"p\">(</span><span class=\"s2\">&quot;Question does not exist&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"s2\">&quot;polls/detail.html&quot;</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"s2\">&quot;question&quot;</span><span class=\"p\">:</span> <span class=\"n\">question</span><span class=\"p\">})</span>\n</code></pre></figure>\n<p>这里有个新原则。如果指定问题 ID 所对应的问题不存在，这个视图就会抛出一个 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 异常。</p>\n<p>我们稍后再讨论你需要在 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/detail.html</span></code>  里输入什么，但是如果你想试试上面这段代码是否正常工作的话，你可以暂时把下面这段输进去：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/detail.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"cp\">{{</span> <span class=\"nv\">question</span> <span class=\"cp\">}}</span>\n</code></pre></figure>\n<p>这样你就能测试了。</p>\n<section id=\"a-shortcut-get-object-or-404\">\n<h3>一个快捷函数： <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/shortcuts/#django.shortcuts.get_object_or_404\" title=\"django.shortcuts.get_object_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_object_or_404()</span></code></a><a class=\"heading-anchor\" href=\"#a-shortcut-get-object-or-404\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>尝试用 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/models/querysets/#django.db.models.query.QuerySet.get\" title=\"django.db.models.query.QuerySet.get\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get()</span></code></a>  函数获取一个对象，如果不存在就抛出 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 错误也是一个普遍的流程。Django 也提供了一个快捷函数，下面是修改后的详情 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> 视图代码：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">,</span> <span class=\"n\">render</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"c1\"># ...</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">detail</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"n\">question</span> <span class=\"o\">=</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">(</span><span class=\"n\">Question</span><span class=\"p\">,</span> <span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"n\">question_id</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"s2\">&quot;polls/detail.html&quot;</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"s2\">&quot;question&quot;</span><span class=\"p\">:</span> <span class=\"n\">question</span><span class=\"p\">})</span>\n</code></pre></figure>\n<p>The <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/shortcuts/#django.shortcuts.get_object_or_404\" title=\"django.shortcuts.get_object_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_object_or_404()</span></code></a> function takes a Django model\nas its first argument and an arbitrary number of keyword arguments, which it\npasses to the <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/models/querysets/#django.db.models.query.QuerySet.get\" title=\"django.db.models.query.QuerySet.get\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get()</span></code></a> function of the\nmodel's manager. It raises <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> if the object doesn't\nexist.</p>\n<aside class=\"admonition-philosophy admonition\">\n<p class=\"admonition-title\">设计哲学</p>\n<p>为什么我们使用辅助函数 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/shortcuts/#django.shortcuts.get_object_or_404\" title=\"django.shortcuts.get_object_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_object_or_404()</span></code></a> 而不是自己捕获 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist\" title=\"django.core.exceptions.ObjectDoesNotExist\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ObjectDoesNotExist</span></code></a> 异常呢？还有，为什么模型 API 不直接抛出 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist\" title=\"django.core.exceptions.ObjectDoesNotExist\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ObjectDoesNotExist</span></code></a> 而是抛出 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 呢？</p>\n<p>因为这样做会增加模型层和视图层的耦合性。指导 Django 设计的最重要的思想之一就是要保证松散耦合。一些受控的耦合将会被包含在  <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/shortcuts/#module-django.shortcuts\" title=\"django.shortcuts: Convenience shortcuts that span multiple levels of Django's MVC stack.\"><code class=\"xref py py-mod docutils literal notranslate\"><span class=\"pre\">django.shortcuts</span></code></a> 模块中。</p>\n</aside>\n<p>也有 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/shortcuts/#django.shortcuts.get_list_or_404\" title=\"django.shortcuts.get_list_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_list_or_404()</span></code></a> 函数，工作原理和 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/shortcuts/#django.shortcuts.get_object_or_404\" title=\"django.shortcuts.get_object_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_object_or_404()</span></code></a> 一样，除了 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/models/querysets/#django.db.models.query.QuerySet.get\" title=\"django.db.models.query.QuerySet.get\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get()</span></code></a> 函数被换成了 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/models/querysets/#django.db.models.query.QuerySet.filter\" title=\"django.db.models.query.QuerySet.filter\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">filter()</span></code></a> 函数。如果列表为空的话会抛出 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 异常。</p>\n</section>\n</section>\n<section id=\"use-the-template-system\">\n<h2>使用模板系统<a class=\"heading-anchor\" href=\"#use-the-template-system\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>回过头去看看我们的 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> 视图。它向模板传递了上下文变量 <code class=\"docutils literal notranslate\"><span class=\"pre\">question</span></code> 。下面是 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/detail.html</span></code> 模板里正式的代码：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/detail.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">h1</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">h1</span><span class=\"p\">&gt;</span>\n<span class=\"p\">&lt;</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">for</span> <span class=\"nv\">choice</span> <span class=\"k\">in</span> <span class=\"nv\">question.choice_set.all</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">choice.choice_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">endfor</span> <span class=\"cp\">%}</span>\n<span class=\"p\">&lt;/</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n</code></pre></figure>\n<p>模板系统统一使用点符号来访问变量的属性。在示例 <code class=\"docutils literal notranslate\"><span class=\"pre\">{{</span> <span class=\"pre\">question.question_text</span> <span class=\"pre\">}}</span></code> 中，首先 Django 尝试对  <code class=\"docutils literal notranslate\"><span class=\"pre\">question</span></code> 对象使用字典查找（也就是使用 obj.get(str) 操作），如果失败了就尝试属性查找（也就是 obj.str 操作），结果是成功了。如果这一操作也失败的话，将会尝试列表查找（也就是 obj[int] 操作）。</p>\n<p>在 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/templates/builtins/#std-templatetag-for\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">for</span> <span class=\"pre\">%}</span></code></a>  循环中发生的函数调用：<code class=\"docutils literal notranslate\"><span class=\"pre\">question.choice_set.all</span></code> 被解释为 Python 代码 <code class=\"docutils literal notranslate\"><span class=\"pre\">question.choice_set.all()</span></code> ，将会返回一个可迭代的 <code class=\"docutils literal notranslate\"><span class=\"pre\">Choice</span></code> 对象，这一对象可以在 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/templates/builtins/#std-templatetag-for\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">for</span> <span class=\"pre\">%}</span></code></a>  标签内部使用。</p>\n<p>查看 <a class=\"reference internal\" href=\"/zh-hans/5.1/topics/templates/\"><span class=\"doc\">模板指南</span></a> 可以了解关于模板的更多信息。</p>\n</section>\n<section id=\"removing-hardcoded-urls-in-templates\">\n<h2>去除模板中的硬编码 URL<a class=\"heading-anchor\" href=\"#removing-hardcoded-urls-in-templates\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>还记得吗，我们在 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/index.html</span></code> 里编写投票链接时，链接是硬编码的：</p>\n<div class=\"code-block\" data-language=\"html+django\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;/polls/</span><span class=\"cp\">{{</span> <span class=\"nv\">question.id</span> <span class=\"cp\">}}</span><span class=\"s\">/&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n</code></pre></div>\n<p>这种硬编码、强耦合的方法的问题在于，在具有大量模板的项目中更改 URL 变得具有挑战性。然而，由于你在 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls.urls</span></code> 模块中的 <a class=\"reference internal\" href=\"/zh-hans/5.1/ref/urls/#django.urls.path\" title=\"django.urls.path\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">path()</span></code></a> 函数中定义了 <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> 参数，你可以通过使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">url</span> <span class=\"pre\">%}</span></code> 模板标签来消除对 url 配置中定义的特定 URL 路径的依赖：</p>\n<div class=\"code-block\" data-language=\"html+django\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{%</span> <span class=\"k\">url</span> <span class=\"s1\">&#39;detail&#39;</span> <span class=\"nv\">question.id</span> <span class=\"cp\">%}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n</code></pre></div>\n<p>这个标签的工作方式是在 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls.urls</span></code> 模块的 URL 定义中寻具有指定名字的条目。你可以回忆一下，具有名字 'detail'  的 URL 是在如下语句中定义的：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"o\">...</span>\n<span class=\"c1\"># the &#39;name&#39; value as called by the {% url %} template tag</span>\n<span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&lt;int:question_id&gt;/&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">detail</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;detail&quot;</span><span class=\"p\">),</span>\n<span class=\"o\">...</span>\n</code></pre></div>\n<p>如果你想改变投票详情视图的 URL，比如想改成 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/specifics/12/</span></code> ，你不用在模板里修改任何东西（包括其它模板），只要在 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code> 里稍微修改一下就行：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"o\">...</span>\n<span class=\"c1\"># added the word &#39;specifics&#39;</span>\n<span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;specifics/&lt;int:question_id&gt;/&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">detail</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;detail&quot;</span><span class=\"p\">),</span>\n<span class=\"o\">...</span>\n</code></pre></div>\n</section>\n<section id=\"namespacing-url-names\">\n<h2>为 URL 名称添加命名空间<a class=\"heading-anchor\" href=\"#namespacing-url-names\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>教程项目只有一个应用，<code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 。在一个真实的 Django 项目中，可能会有五个，十个，二十个，甚至更多应用。Django 如何分辨重名的 URL 呢？举个例子，<code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code>  应用有 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail</span></code> 视图，可能另一个博客应用也有同名的视图。Django 如何知道 <code class=\"docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">url</span> <span class=\"pre\">%}</span></code> 标签到底对应哪一个应用的 URL 呢？</p>\n<p>答案是：在根 URLconf 中添加命名空间。在 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code> 文件中稍作修改，加上 <code class=\"docutils literal notranslate\"><span class=\"pre\">app_name</span></code> 设置命名空间：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">path</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">views</span>\n\n<span class=\"n\">app_name</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;polls&quot;</span>\n<span class=\"n\">urlpatterns</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">index</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;index&quot;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&lt;int:question_id&gt;/&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">detail</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;detail&quot;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&lt;int:question_id&gt;/results/&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">results</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;results&quot;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s2\">&quot;&lt;int:question_id&gt;/vote/&quot;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">vote</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;vote&quot;</span><span class=\"p\">),</span>\n<span class=\"p\">]</span>\n</code></pre></figure>\n<p>现在，编辑 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/index.html</span></code> 文件，从：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/index.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{%</span> <span class=\"k\">url</span> <span class=\"s1\">&#39;detail&#39;</span> <span class=\"nv\">question.id</span> <span class=\"cp\">%}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n</code></pre></figure>\n<p>修改为指向具有命名空间的详细视图：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/index.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{%</span> <span class=\"k\">url</span> <span class=\"s1\">&#39;polls:detail&#39;</span> <span class=\"nv\">question.id</span> <span class=\"cp\">%}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n</code></pre></figure>\n<p>当你对你写的视图感到满意后，请阅读 <a class=\"reference internal\" href=\"/zh-hans/5.1/intro/tutorial04/\"><span class=\"doc\">教程的第 4 部分</span></a> 了解基础的表单处理和通用视图。</p>\n</section>","rootId":"writing-your-first-django-app-part-3","toc":[{"title":"概况","anchor":"overview","children":[]},{"title":"编写更多视图","anchor":"writing-more-views","children":[]},{"title":"写一个真正有用的视图","anchor":"write-views-that-actually-do-something","children":[{"title":"一个快捷函数： render()","anchor":"a-shortcut-render","children":[]}]},{"title":"抛出 404 错误","anchor":"raising-a-404-error","children":[{"title":"一个快捷函数： get_object_or_404()","anchor":"a-shortcut-get-object-or-404","children":[]}]},{"title":"使用模板系统","anchor":"use-the-template-system","children":[]},{"title":"去除模板中的硬编码 URL","anchor":"removing-hardcoded-urls-in-templates","children":[]},{"title":"为 URL 名称添加命名空间","anchor":"namespacing-url-names","children":[]}],"breadcrumbs":[{"docname":"intro/index","title":"开始","url":"/zh-hans/5.1/intro/"}],"prev":{"docname":"intro/tutorial02","title":"编写你的第一个 Django 应用，第 2 部分","url":"/zh-hans/5.1/intro/tutorial02/"},"next":{"docname":"intro/tutorial04","title":"编写你的第一个 Django 应用，第 4 部分","url":"/zh-hans/5.1/intro/tutorial04/"},"formats":{"html":"/zh-hans/5.1/intro/tutorial03/","markdown":"/zh-hans/5.1/intro/tutorial03.md","json":"/zh-hans/5.1/intro/tutorial03.json"},"source":"https://github.com/django/django/blob/stable/5.1.x/docs/intro/tutorial03.txt","official":"https://docs.djangoproject.com/zh-hans/5.1/intro/tutorial03/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0"],"inLocales":["en","zh-hans","fr","ja","id","it","pt-br","ko","es","el","pl"]}