aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/learn/server-side/django
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
commit33058f2b292b3a581333bdfb21b8f671898c5060 (patch)
tree51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/learn/server-side/django
parent8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff)
downloadtranslated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip
initial commit
Diffstat (limited to 'files/zh-cn/learn/server-side/django')
-rw-r--r--files/zh-cn/learn/server-side/django/authentication/index.html710
-rw-r--r--files/zh-cn/learn/server-side/django/deployment/index.html675
-rw-r--r--files/zh-cn/learn/server-side/django/django_assessment_blog/index.html313
-rw-r--r--files/zh-cn/learn/server-side/django/forms/index.html671
-rw-r--r--files/zh-cn/learn/server-side/django/generic_views/index.html630
-rw-r--r--files/zh-cn/learn/server-side/django/index.html60
-rw-r--r--files/zh-cn/learn/server-side/django/introduction/index.html268
-rw-r--r--files/zh-cn/learn/server-side/django/models/index.html449
-rw-r--r--files/zh-cn/learn/server-side/django/sessions/index.html190
-rw-r--r--files/zh-cn/learn/server-side/django/skeleton_website/index.html372
-rw-r--r--files/zh-cn/learn/server-side/django/testing/index.html917
-rw-r--r--files/zh-cn/learn/server-side/django/tutorial_local_library_website/index.html70
-rw-r--r--files/zh-cn/learn/server-side/django/web_application_security/index.html180
-rw-r--r--files/zh-cn/learn/server-side/django/主页构建/index.html358
-rw-r--r--files/zh-cn/learn/server-side/django/开发环境/index.html406
-rw-r--r--files/zh-cn/learn/server-side/django/管理站点/index.html339
16 files changed, 6608 insertions, 0 deletions
diff --git a/files/zh-cn/learn/server-side/django/authentication/index.html b/files/zh-cn/learn/server-side/django/authentication/index.html
new file mode 100644
index 0000000000..c6536c3309
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/authentication/index.html
@@ -0,0 +1,710 @@
+---
+title: 'Django 教程 8: 用户授权与许可'
+slug: learn/Server-side/Django/Authentication
+translation_of: Learn/Server-side/Django/Authentication
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Sessions", "Learn/Server-side/Django/Forms", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">在<font><font>本教程中,我们将向您展示如何允许用户使用自己的帐户登录到您的网站,以及如何根据用户是否已登录及其</font></font><em><font><font>权限</font></font></em><font><font>来控制他们可以执行和查看的内容</font><font>。</font><font>作为演示的一部分,我们将扩展</font></font><a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website" style='color: rgb(63, 135, 166); margin: 0px; padding: 0px; border: 0px; text-decoration: none; font-family: "Open Sans", arial, x-locale-body, sans-serif; font-size: 20px; font-style: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 255, 255);'><font><font>LocalLibrary</font></font></a><font><font>网站,添加登录页面和注销页面,以及用户和员工特定的页面以查看已借阅的图书</font></font>。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>完成之前的所有教程主题,包括<a href="/zh-CN/docs/Learn/Server-side/Django/Sessions">Django 教程 7:Sessions 框架</a>。</td>
+ </tr>
+ <tr>
+ <th scope="row">目的:</th>
+ <td>了解如何设置和使用用户身份验证和权限。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概观">概观</h2>
+
+<p>Django 提供了一个身份验证和授权(“权限”)系统,该系统构建在<a href="/zh-CN/docs/Learn/Server-side/Django/Sessions">上一个教程</a>中讨论的会话框架之上,允许您验证用户凭据,并定义每个用户可允许执行的操作。该框架包括用户<code>Users</code>和分组<code>Groups</code>的内置模型(一次向多个用户应用权限的通用方法),用于登录用户的权限/标志,以指定用户是否可以执行任务,表单和视图,以及查看限制内容的工具。</p>
+
+<div class="note">
+<p><strong>注意</strong>: Django身份验证系统的目标非常通用,因此不提供其他Web身份验证系统中,所提供的某些功能。某些常见问题的解决方案,可作为第三方软件包提供。例如,限制登录尝试,和针对第三方的身份验证(例如 OAuth)。</p>
+</div>
+
+<p>在本教程中,我们将向您展示,如何在<a href="/zh-CN/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a>网站中,启用用户身份验证,创建您自己的登录和注销页面,为模型添加权限,以及控制对页面的访问。我们将使用身份验证/权限,来显示用户和图书馆员借用图书的列表。</p>
+
+<p>身份验证系统非常灵活,您可以根据需要,从头开始构建 URLs,表单,视图和模板,只需调用提供的API,即可登录用户。但是,在本文中,我们将在登录和注销页面,使用 Django 的“库存” 身份验证视图和表单。我们仍然需要创建一些模板,但这很简单。</p>
+
+<p>我们还将向您展示如何创建权限,以及检查视图和模板中的登录状态和权限。</p>
+
+<h2 id="启用身份验证">启用身份验证</h2>
+
+<p>我们在<a href="/zh-CN/docs/Learn/Server-side/Django/skeleton_website">创建框架网站</a>时(在教程2中),自动启用了身份验证,因此您此时,无需再执行任何操作。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 当我们使用 <code>django-admin startproject </code>命令,以创建应用程序时,所有必要的配置都已完成。当我们第一次调用 <code>python manage.py migrate</code> 时,创建了用户和模型权限的数据库表。</p>
+</div>
+
+<p>配置在项目文件(<strong>locallibrary/locallibrary/settings.py</strong>)的<code>INSTALLED_APPS</code>和<code>MIDDLEWARE</code>部分中设置,如下所示:</p>
+
+<pre class="brush: python">INSTALLED_APPS = [
+ ...
+<strong>    'django.contrib.auth', </strong>#Core authentication framework and its default models.
+<strong>    'django.contrib.contenttypes', #</strong>Django content type system (allows permissions to be associated with models).
+ ....
+
+MIDDLEWARE = [
+ ...
+<strong>    'django.contrib.sessions.middleware.SessionMiddleware',</strong> #Manages sessions across requests
+ ...
+<strong>    'django.contrib.auth.middleware.AuthenticationMiddleware',</strong> #Associates users with requests using sessions.
+ ....
+</pre>
+
+<h2 id="创建用户和分组">创建用户和分组</h2>
+
+<p>在教程 4 中,当我们查看 <a href="/zh-CN/docs/Learn/Server-side/Django/Admin_site">Django 管理站点</a>时,您已经创建了第一个用户(这是一个超级用户,使用命令 <code>python manage.py createsuperuser </code>创建)。我们的超级用户已经过身份验证,并拥有所有权限,因此我们需要创建一个测试用户,来代表普通网站用户。我们将使用管理站点,来创建我们的 locallibrary 组別和网站登录,因为这是最快的方法之一。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 您还可以用编程方式创建用户,如下所示。您会必须这样做,例如,如果要开发一个界面,能允许用户创建自己的登录(您不应该授予用户访问管理站点的权限)。</p>
+
+<pre class="brush: python">from django.contrib.auth.models import User
+
+# Create user and save to the database
+user = User.objects.create_user('myusername', 'myemail@crazymail.com', 'mypassword')
+
+# Update fields and then save again
+user.first_name = 'John'
+user.last_name = 'Citizen'
+user.save()
+</pre>
+</div>
+
+<p>下面,我们首先创建一个分组,然后创建一个用户。即使我们还没有为我们的图书馆成员添加任何权限,如果我们以后需要,将它们添加到分组中,要比单独添加到每个成员要容易得多。</p>
+
+<p>启动开发服务器,并到本地 Web 浏览器中的管理站点(<a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a>)。使用超级用户帐户的凭据,登录该站点。 Admin 站点的最上级显示所有模型,按 “django application” 排序。在 “身份验证和授权” <strong>Authentication and Authorisation </strong>部分 ,您可以单击用户 <strong>Users ,</strong>或分组 <strong>Groups </strong>链接,以查看其现有记录。</p>
+
+<p><img alt="Admin site - add groups or users" src="https://mdn.mozillademos.org/files/14091/admin_authentication_add.png" style="border-style: solid; border-width: 1px; display: block; height: 364px; margin: 0px auto; width: 661px;"></p>
+
+<p>首先,我们为图书馆成员,创建一个新的分组。</p>
+
+<ol>
+ <li>单击“添加” <strong>Add </strong>按钮(“分组” Group 旁边)以创建新的分组;在分组的名称<strong> Name</strong> ,输入“Library Members”。<img alt="Admin site - add group" src="https://mdn.mozillademos.org/files/14093/admin_authentication_add_group.png" style="border-style: solid; border-width: 1px; display: block; height: 561px; margin: 0px auto; width: 800px;"></li>
+ <li>我们不需要该组的任何权限,因此只需按<strong>SAVE</strong>(您将进入分组列表)。</li>
+</ol>
+
+<p>现在让我们创建一个用户:</p>
+
+<ol>
+ <li>回到管理站点的主页</li>
+ <li>单击“用户”旁边的“添加”按钮 <strong>Add</strong>,以打开“添加用户”对话框。<img alt="Admin site - add user pt1" src="https://mdn.mozillademos.org/files/14095/admin_authentication_add_user_prt1.png" style="border-style: solid; border-width: 1px; display: block; height: 409px; margin: 0px auto; width: 800px;"></li>
+ <li>为测试用户输入适当的用户名(<strong>Username)</strong>和密码/密码确认<strong>(Password/Password confirmation</strong> )</li>
+ <li>按 <strong>SAVE</strong> 创建用户。
+ <p> </p>
+
+ <p>管理站点将创建新用户,并立即转到 “更改用户” 屏幕,您可以在其中更改用户名(<strong>username</strong>),并添加用户模型的可选字段的信息。这些字段包括名字,姓氏,电子邮件地址,用户状态和权限(仅应设置活动标志<strong> Active</strong>)。再往下,您可以指定用户的分组和权限,并查看与用户相关的重要日期(例如,他们的加入日期和上次登录日期)。</p>
+ <img alt="Admin site - add user pt2" src="https://mdn.mozillademos.org/files/14097/admin_authentication_add_user_prt2.png" style="border-style: solid; border-width: 1px; display: block; height: 635px; margin: 0px auto; width: 800px;"></li>
+ <li>在“分组”(<em>Groups</em>)部分中,从“可用分组”(<em>Available groups</em>)列表中,选择“图书馆成员”分组 <strong>Library Member</strong>,然后点击这些框之间的<strong>右箭头</strong>,将其移动到“选择的分组”(<em>Chosen groups</em>)框中。<img alt="Admin site - add user to group" src="https://mdn.mozillademos.org/files/14099/admin_authentication_user_add_group.png" style="border-style: solid; border-width: 1px; display: block; height: 414px; margin: 0px auto; width: 933px;"></li>
+ <li>我们不需要在此处执行任何其他操作,因此只需再次选择<strong> SAVE</strong> ,即可转到用户列表。</li>
+</ol>
+
+<p>就是这样!现在您有一个 “普通的图书馆成员” 帐户,您可以使用该帐户进行测试(一旦我们实现了页面,使他们能够登录)。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 您应该尝试创建另一个图书馆用户。此外,为图书馆管理员创建一个分组,并添加一个用户!</p>
+</div>
+
+<h2 id="设置身份验证视图">设置身份验证视图</h2>
+
+<p>Django 提供了创建身份验证页面所需的几乎所有功能,让处理登录,注销和密码管理等工作,都能 “开箱即用”。这些相关功能包括了 url 映射器,视图和表单,但它不包括模板 - 我们必须创建自己的模板!</p>
+
+<p>在本节中,我们将展示如何将默认系统,集成到 LocalLibrary 网站并创建模板。我们将它们放在主项目的 URL 当中。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 您不必一定要使用这些代码,但您可能希望这样做,因为它使事情变得更容易。如果更改用户模型(高级主题!),您几乎肯定需要更改表单处理代码。但即便如此,您仍然可以使用先前已经有的视图功能。</p>
+</div>
+
+<div class="note">
+<p><strong>注意: </strong>在这种情况下,我们可以合理地将认证页面(包括URL和模板)放在我们的目录应用程序中。但是,如果我们有多个应用程序,最好将这个共享登录行为分开,并让它在整个站点上可用,这就是我们在这里展示的内容!</p>
+</div>
+
+<h3 id="项目网址">项目网址</h3>
+
+<p>将以下内容,添加到项目 urls.py(<strong>locallibrary/locallibrary/urls.py</strong>)文件的底部:</p>
+
+<pre class="brush: python"># Use include() to add URLS from the catalog application and authentication system
+from django.urls import include
+
+#Add Django site authentication urls (for login, logout, password management)
+urlpatterns += [
+ path('accounts/', include('django.contrib.auth.urls')),
+]
+</pre>
+
+<p>打开 URL <a href="http://127.0.0.1:8000/accounts/">http://127.0.0.1:8000/accounts/</a> (注意前面的斜杠!),Django将显示一个错误,它无法找到此URL,并列出它尝试过的所有URL。从中您可以看到可以使用的URL,例如:</p>
+
+<div class="note">
+<p><strong>注意: </strong>使用上面的方法,添加以下带有方括号中的名称的 URL,可用于反转 URL 映射。您不必实现任何其他内容 - 上面的 url 映射,会自动映射下面提到的URL。</p>
+</div>
+
+<div class="note">
+<pre class="brush: python">accounts/ login/ [name='login']
+accounts/ logout/ [name='logout']
+accounts/ password_change/ [name='password_change']
+accounts/ password_change/done/ [name='password_change_done']
+accounts/ password_reset/ [name='password_reset']
+accounts/ password_reset/done/ [name='password_reset_done']
+accounts/ reset/&lt;uidb64&gt;/&lt;token&gt;/ [name='password_reset_confirm']
+accounts/ reset/done/ [name='password_reset_complete']</pre>
+</div>
+
+<p>现在尝试打开登录 URL(<a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a>)。这将再次失败,但有一个错误告诉您,我们在模板搜索路径上缺少必需的模板(<strong>registration/login.html</strong>)。您将在顶部的黄色部分中,看到以下文字:</p>
+
+<pre class="brush: python">Exception Type: TemplateDoesNotExist
+Exception Value: <strong>registration/login.html</strong></pre>
+
+<p>下一步是在搜索路径上创建注册目录,然后添加 <strong>login.html </strong>文件。</p>
+
+<h3 id="模板目录">模板目录</h3>
+
+<p>我们希望在模板搜索路径中的目录 <strong>/registration/</strong> 某处,找到刚刚添加的 url(以及隐式视图)的关联模板。</p>
+
+<p>对于此站点,我们将 HTML 页面,放在 <strong>templates/registration/ </strong>目录中。此目录应该位于项目的根目录中,即与 <strong>catalog </strong>和 <strong>locallibrary </strong>文件夹相同的目录)。请立即创建这些文件夹。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 您的文件夹结构,现在应如下所示:<br>
+ locallibrary (django project folder)<br>
+    |_catalog<br>
+    |_locallibrary<br>
+    |_templates <strong>(new)</strong><br>
+                 |_registration</p>
+</div>
+
+<p>要使这些目录对模板加载器可见(即将此目录放在模板搜索路径中),请打开项目设置(<strong>/locallibrary/locallibrary/settings.py</strong>),并更新<code>TEMPLATES </code>部分的 “<code>DIRS</code>” 那一行,如下所示。</p>
+
+<pre class="brush: python">TEMPLATES = [
+ {
+ ...
+<strong> 'DIRS': ['./templates',],</strong>
+ 'APP_DIRS': True,
+ ...
+</pre>
+
+<h3 id="登录模板">登录模板</h3>
+
+<div class="warning">
+<p><strong>重要说明</strong>: 本文提供的身份验证模板,是 Django 演示登录模板的基本/略微修改版本。您可能需要自定义它们,以供自己使用!</p>
+</div>
+
+<p>创建一个名为 <strong>/locallibrary/templates/registration/login.html</strong> 的新HTML文件。为它加入以下内容:</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+
+{% if form.errors %}
+&lt;p&gt;Your username and password didn't match. Please try again.&lt;/p&gt;
+{% endif %}
+
+{% if next %}
+    {% if user.is_authenticated %}
+    &lt;p&gt;Your account doesn't have access to this page. To proceed,
+    please login with an account that has access.&lt;/p&gt;
+    {% else %}
+    &lt;p&gt;Please login to see this page.&lt;/p&gt;
+    {% endif %}
+{% endif %}
+
+&lt;form method="post" action="{% url 'login' %}"&gt;
+{% csrf_token %}
+
+&lt;div&gt;
+  &lt;td&gt;\{{ form.username.label_tag }}&lt;/td&gt;
+  &lt;td&gt;\{{ form.username }}&lt;/td&gt;
+&lt;/div&gt;
+&lt;div&gt;
+  &lt;td&gt;\{{ form.password.label_tag }}&lt;/td&gt;
+  &lt;td&gt;\{{ form.password }}&lt;/td&gt;
+&lt;/div&gt;
+
+&lt;div&gt;
+  &lt;input type="submit" value="login" /&gt;
+  &lt;input type="hidden" name="next" value="\{{ next }}" /&gt;
+&lt;/div&gt;
+&lt;/form&gt;
+
+{# Assumes you setup the password_reset view in your URLconf #}
+&lt;p&gt;&lt;a href="{% url 'password_reset' %}"&gt;Lost password?&lt;/a&gt;&lt;/p&gt;
+
+{% endblock %}</pre>
+
+<p>此模板与我们之前看到的模板,有一些相似之处 - 它扩展了我们的基本模板,并覆盖了内容区块 <code>content</code>。其余代码,是相当标准的表单处理代码,我们将在后面的教程中讨论。您现在需要知道的是,这将显示一个表单,您可以在其中输入您的用户名和密码,如果您输入的值无效,则会在页面刷新时,提示您输入正确的值。</p>
+
+<p>保存模板后,回到登录页面(<a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a>),您应该看到如下内容:</p>
+
+<p><img alt="Library login page v1" src="https://mdn.mozillademos.org/files/14101/library_login.png" style="border-style: solid; border-width: 1px; display: block; height: 173px; margin: 0px auto; width: 441px;"></p>
+
+<p>如果您尝试登录,将会成功,并且您将被重定向到另一个页面(默认情况下,这将是 <a href="http://127.0.0.1:8000/accounts/profile/">http://127.0.0.1:8000/accounts/profile/</a>)。这里的问题是,默认情况下,Django希望在登录后,你可能会被带到个人资料页面,这可能是,也可能不是。由于您还没有定义此页面,您将收到另一个错误!</p>
+
+<p>打开项目设置(<strong>/locallibrary/locallibrary/settings.py</strong>),并将下面的文本添加到底部。现在登录时,您应该默认重定向到站点主页。</p>
+
+<pre class="brush: python"># Redirect to home URL after login (Default redirects to /accounts/profile/)
+LOGIN_REDIRECT_URL = '/'
+</pre>
+
+<h3 id="登出模板">登出模板</h3>
+
+<p>如果您打开登出网址(<a href="http://127.0.0.1:8000/accounts/logout/">http://127.0.0.1:8000/accounts/logout/</a>),那么您会看到一些奇怪的行为 - 您所属的用户肯定会被登出,但您将被带到管理员登出页面。这不是您想要的,只是因为该页面上的登录链接,将您带到管理员登录屏幕(并且仅对具有<code>is_staff</code>权限的用户可用)。</p>
+
+<p>创建并打开 /<strong>locallibrary/templates/registration/logged_out.html</strong>。将下面的文字,复制到文档中:</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+&lt;p&gt;Logged out!&lt;/p&gt;
+
+&lt;a href="{% url 'login'%}"&gt;Click here to login again.&lt;/a&gt;
+{% endblock %}</pre>
+
+<p>这个模板非常简单。它只显示一条消息,通知您已登出,并提供一个链接,您可以点击此按钮,返回登录屏幕。如果再次回到登出 URL,您应该看到此页面:</p>
+
+<p><img alt="Library logout page v1" src="https://mdn.mozillademos.org/files/14103/library_logout.png" style="border-style: solid; border-width: 1px; display: block; height: 169px; margin: 0px auto; width: 385px;"></p>
+
+<h3 id="密码重置模板">密码重置模板</h3>
+
+<p>默认密码重置系统,使用电子邮件向用户发送重置链接。您需要创建表单,以获取用户的电子邮件地址,发送电子邮件,允许他们输入新密码,以及记录整个过程的完成时间。</p>
+
+<p>以下模板可作为起点。</p>
+
+<h4 id="密码重置表单">密码重置表单</h4>
+
+<p>这是用于获取用户电子邮件地址的表单(用于发送密码重置电子邮件)。创建 <strong>/locallibrary/templates/registration/password_reset_form.html</strong>,并为其提供以下内容:</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+{% block content %}
+
+&lt;form action="" method="post"&gt;{% csrf_token %}
+ {% if form.email.errors %} \{{ form.email.errors }} {% endif %}
+ &lt;p&gt;\{{ form.email }}&lt;/p&gt;
+ &lt;input type="submit" class="btn btn-default btn-lg" value="Reset password" /&gt;
+&lt;/form&gt;
+
+{% endblock %}
+</pre>
+
+<h4 id="密码重置完成">密码重置完成</h4>
+
+<p>收集您的电子邮件地址后,会显示此表单。创建 <strong>/locallibrary/templates/registration/password_reset_done.html</strong>,并为其提供以下内容:</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+{% block content %}
+&lt;p&gt;We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.&lt;/p&gt;
+{% endblock %}
+</pre>
+
+<h4 id="密码重置电子邮件">密码重置电子邮件</h4>
+
+<p>此模板提供 HTML 电子邮件的文本,其中包含我们将发送给用户的重置链接。创建 <strong>/locallibrary/templates/registration/password_reset_email.html</strong>,并为其提供以下内容:</p>
+
+<pre class="brush: html">Someone asked for password reset for email \{{ email }}. Follow the link below:
+\{{ protocol}}://\{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
+</pre>
+
+<h4 id="密码重置确认">密码重置确认</h4>
+
+<p>点击密码重置电子邮件中的链接后,您可以在此页面输入新密码。创建 <strong>/locallibrary/templates/registration/password_reset_confirm.html</strong>,并为其提供以下内容:</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+
+ {% if validlink %}
+ &lt;p&gt;Please enter (and confirm) your new password.&lt;/p&gt;
+ &lt;form action="" method="post"&gt;
+ &lt;div style="display:none"&gt;
+ &lt;input type="hidden" value="\{{ csrf_token }}" name="csrfmiddlewaretoken"&gt;
+ &lt;/div&gt;
+ &lt;table&gt;
+ &lt;tr&gt;
+ &lt;td&gt;\{{ form.new_password1.errors }}
+ &lt;label for="id_new_password1"&gt;New password:&lt;/label&gt;&lt;/td&gt;
+ &lt;td&gt;\{{ form.new_password1 }}&lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td&gt;\{{ form.new_password2.errors }}
+ &lt;label for="id_new_password2"&gt;Confirm password:&lt;/label&gt;&lt;/td&gt;
+ &lt;td&gt;\{{ form.new_password2 }}&lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td&gt;&lt;/td&gt;
+ &lt;td&gt;&lt;input type="submit" value="Change my password" /&gt;&lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;/table&gt;
+ &lt;/form&gt;
+ {% else %}
+ &lt;h1&gt;Password reset failed&lt;/h1&gt;
+ &lt;p&gt;The password reset link was invalid, possibly because it has already been used. Please request a new password reset.&lt;/p&gt;
+ {% endif %}
+
+{% endblock %}
+</pre>
+
+<h4 id="密码重置完成_2">密码重置完成</h4>
+
+<p>这是最后一个密码重置模板,显示该模板,以在密码重置成功时通知您。创建 <strong>/locallibrary/templates/registration/password_reset_complete.html</strong>,并为其提供以下内容:</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+{% block content %}
+
+&lt;h1&gt;The password has been changed!&lt;/h1&gt;
+&lt;p&gt;&lt;a href="{% url 'login' %}"&gt;log in again?&lt;/a&gt;&lt;/p&gt;
+
+{% endblock %}</pre>
+
+<h3 id="测试新的身份验证页面">测试新的身份验证页面</h3>
+
+<p>现在您已经添加了 URL 配置,并创建了所有模板,现在认证页面应该可以正常工作了!</p>
+
+<p>您可以尝试登录,然后使用以下 URL 登出超级用户帐户,来测试新的身份验证页面:</p>
+
+<ul>
+ <li><a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a></li>
+ <li><a href="http://127.0.0.1:8000/accounts/logout/">http://127.0.0.1:8000/accounts/logout/</a></li>
+</ul>
+
+<p>您将能够从登录页面中的链接,测试密码重置功能。<strong>请注意,Django只会向已存储在其数据库中的地址(用户)发送重置电子邮件!</strong></p>
+
+<div class="note">
+<p><strong>注意</strong>: 密码重置系统,要求您的网站支持电子邮件,这超出了本文的范围,因此该部分<strong>将无法使用</strong>。要测试此功能,请将以下一行放在 settings.py 文件的末尾。这会记录发送到命令行控制台的所有电子邮件(因此您可以从命令行控制台,复制密码重置链接)。</p>
+
+<pre class="brush: python">EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+</pre>
+
+<p>有关更多信息,请参阅<a href="https://docs.djangoproject.com/en/2.0/topics/email/">发送电子邮件</a>(Django文档)。</p>
+</div>
+
+<h2 id="测试已验证身份的用户">测试已验证身份的用户</h2>
+
+<p>本节介绍如何根据用户是否登录,来有选择地控制用户看到的内容。</p>
+
+<h3 id="在模板中测试">在模板中测试</h3>
+
+<p>您可以使用<code>\{{ user }}</code>模板变量,以获取有关模板中,当前登录用户的信息(默认情况下,在我们在骨架中设置项目时,会将其添加到模板上下文中)。</p>
+
+<p>通常,您将首先针对 <code>\{{ user.is_authenticated }} </code>模板变量进行测试,以确定用户是否有资格查看特定内容。为了展示这一点,接下来我们将更新侧边栏,以在用户登出时,显示“登录”链接,如果他们已登录,则显示“登出”链接。</p>
+
+<p>打开基本模板(<strong>/locallibrary/catalog/templates/base_generic.html</strong>),并将以下文本,复制到侧边栏区块<code>sidebar</code>中,紧接在<code>endblock</code>模板标记之前。</p>
+
+<pre class="brush: html"> &lt;ul class="sidebar-nav"&gt;
+
+ ...
+
+ <strong>{% if user.is_authenticated %}</strong>
+ &lt;li&gt;User: <strong>\{{ user.get_username }}</strong>&lt;/li&gt;
+ &lt;li&gt;&lt;a href="{% url 'logout'%}?next=\{{request.path}}"&gt;Logout&lt;/a&gt;&lt;/li&gt;
+ <strong>{% else %}</strong>
+ &lt;li&gt;&lt;a href="{% url 'login'%}?next=\{{request.path}}"&gt;Login&lt;/a&gt;&lt;/li&gt;
+ <strong>{% endif %} </strong>
+ &lt;/ul&gt;</pre>
+
+<p>如您所见,我们使用 <code>if</code>-<code>else</code>-<code>endif</code>模板标签,根据 <code>\{{ user.is_authenticated }}</code> 是否为 true ,来有条件地显示文本。如果用户已通过身份验证,那么我们知道,我们拥有有效用户,因此我们会调用 <strong>\{{ user.get_username }} </strong>,来显示其名称。</p>
+
+<p>我们使用 <code>url</code>模板标记,和相应 URL 配置的名称,创建登录和登出链接 URL。另外请注意,我们如何将 “<code>?next=\{{request.path}}</code>附加到URL的末尾。这样做,是将包含当前页面地址(URL)的URL参数,添加到链接URL的末尾。用户成功登录/登出后,视图将使用此“下一个”值,将用户重定向,回到他们首次单击登录/登出链接的页面。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 试试吧!如果您在主页上,并单击侧栏中的“登录/登出”,在操作完成后,您应该返回到同一页面。</p>
+</div>
+
+<h3 id="在视图中测试">在视图中测试</h3>
+
+<p>如果您正在使用基于函数的视图,则限制访问函数的最简单方法,是将<code>login_required</code>装饰器,应用于您的视图函数,如下所示。如果用户已登录,则您的视图代码将正常执行。</p>
+
+<p>如果用户未登录,则会重定向到项目设置 (<code>settings.LOGIN_URL</code>)中定义的登录URL,并将当前绝对路径,作为URL参数("下一个"<code>next</code>)来传递。如果用户成功登录,则会返回到此页面,但这次会进行身份验证。</p>
+
+<pre class="brush: python">from django.contrib.auth.decorators import login_required
+
+@login_required
+def my_view(request):
+ ...</pre>
+
+<div class="note">
+<p><strong>注意:</strong> 您可以通过<code>request.user.is_authenticated</code>,测试手动执行类似的操作,但装饰器更方便!</p>
+</div>
+
+<p>同样,在基于类别的视图中,限制对登录用户的访问的最简单方法,是从<code>LoginRequiredMixin</code>派生。您需要在主视图类之前的超类列表中,首先声明此 mixin。</p>
+
+<pre class="brush: python">from django.contrib.auth.mixins import LoginRequiredMixin
+
+class MyView(LoginRequiredMixin, View):
+ ...</pre>
+
+<p>这与<code>login_required</code>装饰器,具有完全相同的重定向行为。如果用户未经过身份验证(<code>login_url</code>),还可以指定一个替代位置,以将用户重定向到该位置,并使用URL参数名称,而不是“<code>next</code>”,来插入当前绝对路径(<code>redirect_field_name</code>)。</p>
+
+<pre class="brush: python">class MyView(LoginRequiredMixin, View):
+ login_url = '/login/'
+ redirect_field_name = 'redirect_to'
+</pre>
+
+<p>有关其他详细信息,请查看<a href="https://docs.djangoproject.com/en/2.0/topics/auth/default/#limiting-access-to-logged-in-users">Django</a>文档。</p>
+
+<h2 id="示例_-_列出当前用户的书本">示例 - 列出当前用户的书本</h2>
+
+<p>现在我们知道,如何将页面限制为特定用户,让我们为当前用户借阅的书本,创建一个视图。</p>
+
+<p>不幸的是,我们还没有办法让用户借书!因此,在我们创建图书清单之前,我们首先会扩展<code>BookInstance</code>模型,以支持借阅的概念,并使用Django Admin应用程序,借给测试用户一些书。</p>
+
+<h3 id="模型">模型</h3>
+
+<p>首先,我们必须让用户可以借用书本实例<code>BookInstance</code>(我们已经拥有状态<code>status</code>和还书日期<code>due_back</code>,但这个模型和用户之间,没有任何关联。我们将使用<code>ForeignKey</code>(一对多)字段,来创建一个。我们还需要一个简单的机制,来测试借出的书是否过期。</p>
+
+<p>打开 <strong>catalog/models.py</strong>,然后从 <code>django.contrib.auth.models</code>  导入 <code>User</code>模型(在文件顶部的上一个导入行的正下方添加它,好让后续代码可以使用 <code>User</code>):</p>
+
+<pre class="brush: python">from django.contrib.auth.models import User
+</pre>
+
+<p>接下来将借用者字段<code>borrower</code>,添加到<code>BookInstance</code>模型:</p>
+
+<pre class="brush: python">borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
+</pre>
+
+<p>当我们在这里,让我们添加一个属性,我们可以从模板中调用它,来判断特定的书本实例是否过期。虽然我们可以在模板本身中计算这一点,但使用如下所示的<a href="https://docs.python.org/3/library/functions.html#property">属性</a>会更有效率。将其添加到文件的底部:</p>
+
+<pre class="brush: python">from datetime import date
+
+@property
+def is_overdue(self):
+ if self.due_back and date.today() &gt; self.due_back:
+ return True
+ return False</pre>
+
+<div class="note">
+<p><strong>注意</strong>: 在进行比较之前,我们首先验证<code>due_back</code>是否为空。空的<code>due_back</code>字段,会导致Django抛出错误,而不是显示页面:空值不具有可比性。这不是我们希望用户体验到的东西!</p>
+</div>
+
+<p>现在我们已经更新了模型,我们需要对项目进行新的迁移,然后应用这些迁移:</p>
+
+<pre class="brush: bash">python3 manage.py makemigrations
+python3 manage.py migrate
+</pre>
+
+<h3 id="管理员">管理员</h3>
+
+<p>现在打开 <strong>catalog/admin.py</strong>,并将<code>borrower</code>字段,添加到<code>BookInstanceAdmin</code>类别中的<code>list_display</code>和<code>fieldsets</code>,如下所示。这将使该字段在Admin部分中可见,以便我们可以在需要时将<code>User</code>分配给<code>BookInstance</code>。</p>
+
+<pre class="brush: python">@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+    list_display = ('book', 'status'<strong>, 'borrower'</strong>, 'due_back', 'id')
+    list_filter = ('status', 'due_back')
+
+    fieldsets = (
+        (None, {
+            'fields': ('book','imprint', 'id')
+        }),
+        ('Availability', {
+            'fields': ('status', 'due_back'<strong>,'borrower'</strong>)
+        }),
+    )</pre>
+
+<h3 id="借几本书">借几本书</h3>
+
+<p>现在可以将书本借给特定用户,然后借出一些<code>BookInstance</code>记录。将他们的借用字段<code>borrowed</code>,设置为您的测试用户,将状态<code>status</code>设置为 “On loan”,并在将来和过去设置截止日期。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 我们不会一步一步说明这个流程,因为您已经知道如何使用管理站点!</p>
+</div>
+
+<h3 id="在借书视图">在借书视图</h3>
+
+<p>现在我们将添加一个视图,以获取已经借给当前用户的所有书本列表。我们将使用我们熟悉的、基于类的通用类列表视图,但这次我们还将导入并派生自<code>LoginRequiredMixin</code>,以便只有登录用户才能调用此视图。我们还将选择声明<code>template_name</code>,而不是使用默认值,因为我们最终可能会有几个不同的 BookInstance 记录列表,其中包含不同的视图和模板。</p>
+
+<p>将以下内容添加到 catalog/views.py:</p>
+
+<pre class="brush: python">from django.contrib.auth.mixins import LoginRequiredMixin
+
+class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
+ """
+ Generic class-based view listing books on loan to current user.
+ """
+ model = BookInstance
+ template_name ='catalog/bookinstance_list_borrowed_user.html'
+ paginate_by = 10
+
+ def get_queryset(self):
+ return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')</pre>
+
+<p>为了将查询,限制为当前用户的<code>BookInstance</code>对象,我们重新实现了<code>get_queryset()</code>,如上所示。请注意,“o”是表示借出当中“on loan”的存储代码,我们按<code>due_back</code>日期排序,以便首先显示最旧的项目。</p>
+
+<h3 id="借书的_URL_设置">借书的 URL 设置</h3>
+
+<p>现在打开<strong>/catalog/urls.py</strong>,并添加指向上面视图的<code>path()</code>(您只需将下面的文本复制到文件末尾)。</p>
+
+<pre class="brush: python">urlpatterns += [
+ path('mybooks/', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'),
+]</pre>
+
+<h3 id="借书的模板">借书的模板</h3>
+
+<p>现在,我们需要为此页面添加一个模板。首先,创建模板文件<strong>/catalog/templates/catalog/bookinstance_list_borrowed_user.html</strong>,并为其提供以下内容:</p>
+
+<pre class="brush: python">{% extends "base_generic.html" %}
+
+{% block content %}
+ &lt;h1&gt;Borrowed books&lt;/h1&gt;
+
+ {% if bookinstance_list %}
+ &lt;ul&gt;
+
+ {% for bookinst in bookinstance_list %}
+ &lt;li class="{% if bookinst.is_overdue %}text-danger{% endif %}"&gt;
+ &lt;a href="{% url 'book-detail' bookinst.book.pk %}"&gt;\{{bookinst.book.title}}&lt;/a&gt; (\{{ bookinst.due_back }})
+ &lt;/li&gt;
+ {% endfor %}
+ &lt;/ul&gt;
+
+ {% else %}
+ &lt;p&gt;There are no books borrowed.&lt;/p&gt;
+ {% endif %}
+{% endblock %}</pre>
+
+<p>此模板与我们之前为 <code>Book </code>和 <code>Author</code>对象创建的模板非常相似。这里唯一新的东西,是我们检查在模型中添加的方法(<code>bookinst.is_overdue</code>),并使用它,来更改过期项目的颜色。</p>
+
+<p>当开发服务器运行时,您现在应该能够在浏览器中,查看登录用户的列表,网址为<a href="http://127.0.0.1:8000/catalog/mybooks/">http://127.0.0.1:8000/catalog/mybooks/</a>。在您的用户登录并登出后,尝试此操作(在第二种情况下,您应该被重定向到登录页面)。</p>
+
+<p> </p>
+
+<h3 id="将列表添加到侧栏">将列表添加到侧栏</h3>
+
+<p>最后一步,是将这个新页面的链接,添加到侧边栏中。我们将把它放在我们为登录用户显示其他信息的同一部分。</p>
+
+<p>打开基本模板(<strong>/locallibrary/catalog/templates/base_generic.html</strong>),并将粗体标识的那一行,添加到侧边栏区块,如下所示。</p>
+
+<pre class="brush: python"> &lt;ul class="sidebar-nav"&gt;
+ {% if user.is_authenticated %}
+ &lt;li&gt;User: \{{ user.get_username }}&lt;/li&gt;
+<strong> &lt;li&gt;&lt;a href="{% url 'my-borrowed' %}"&gt;My Borrowed&lt;/a&gt;&lt;/li&gt;</strong>
+ &lt;li&gt;&lt;a href="{% url 'logout'%}?next=\{{request.path}}"&gt;Logout&lt;/a&gt;&lt;/li&gt;
+ {% else %}
+ &lt;li&gt;&lt;a href="{% url 'login'%}?next=\{{request.path}}"&gt;Login&lt;/a&gt;&lt;/li&gt;
+ {% endif %}
+ &lt;/ul&gt;
+</pre>
+
+<h3 id="它看起来是什么样子的?">它看起来是什么样子的?</h3>
+
+<p>当任何用户登录时,他们会在侧栏中看到 My Borrowed 链接,并显示如下所示的书本列表(第一本书没有截止日期,这是我们希望在以后的教程中修复的错误!) 。</p>
+
+<p><img alt="Library - borrowed books by user" src="https://mdn.mozillademos.org/files/14105/library_borrowed_by_user.png" style="border-style: solid; border-width: 1px; display: block; height: 215px; margin: 0px auto; width: 530px;"></p>
+
+<h2 id="权限">权限</h2>
+
+<p>权限与模型相关联,并定义可以由具有权限的用户,在模型实例上执行的操作。默认情况下,Django会自动为所有模型提供添加,更改和删除权限,这允许具有权限的用户,通过管理站点执行相关操作。您可以为模型定义自己的权限,并将其授予特定用户。您还可以更改与同一模型的不同实例关联的权限。</p>
+
+<p>对于视图和模板中的权限测试,非常类似于对身份验证状态的测试(实际上,测试权限也会测试身份验证)。</p>
+
+<h3 id="模型_2">模型</h3>
+
+<p>在模型“<code>class Meta</code>”部分上,使用 <code>permissions</code>字段,完成权限定义。您可以在元组中指定所需的权限,每个权限本身,都在包含权限名称和权限显示值的嵌套元组中被定义。例如,我们可能会定义一个权限,允许用户标记已归还的图书,如下所示:</p>
+
+<pre class="brush: python">class BookInstance(models.Model):
+ ...
+  class Meta:
+  ...
+<strong> permissions = (("can_mark_returned", "Set book as returned"),) </strong> </pre>
+
+<p>然后,我们可以将权限分配给管理站点中的图书管理员“Librarian”分组。打开 <strong>catalog/models.py</strong>,然后添加权限,如上所示。您需要重新运行迁移(调用 <code>python3 manage.py makemigrations</code> 和 <code>python3 manage.py migrate</code>),以适当地更新数据库。</p>
+
+<p> </p>
+
+<h3 id="模板">模板</h3>
+
+<p>当前用户的权限,存在名为 <code>\{{ perms }}</code>的模板变量中。您可以使用关联的Django “app” 中的特定变量名,来检查当前用户是否具有特定权限 - 例如,如果用户具有此权限,则 <code>\{{ perms.catalog.can_mark_returned }}</code>将为True,否则为False。我们通常使用模板标记 <code>{% if %}</code> 测试权限,如下所示:</p>
+
+<pre class="brush: python">{% if perms.catalog.<code>can_mark_returned</code> %}
+ &lt;!-- We can mark a BookInstance as returned. --&gt;
+  &lt;!-- Perhaps add code to link to a "book return" view here. --&gt;
+{% endif %}
+</pre>
+
+<h3 id="视图">视图</h3>
+
+<p>在功能视图中,可以使用 <code>permission_required</code>装饰器,或在基于类别的视图中,使用 <code>PermissionRequiredMixin</code>测试权限。模式和行为与登录身份验证相同,但当然您可能需要添加多个权限。</p>
+
+<p>功能视图装饰器:</p>
+
+<pre class="brush: python">from django.contrib.auth.decorators import permission_required
+
+@permission_required('catalog.<code>can_mark_returned</code>')
+@permission_required('catalog.<code>can_edit</code>')
+def my_view(request):
+ ...</pre>
+
+<p>基于类别视图的权限要求 mixin。</p>
+
+<pre class="brush: python">from django.contrib.auth.mixins import PermissionRequiredMixin
+
+class MyView(PermissionRequiredMixin, View):
+ permission_required = 'catalog.<code>can_mark_returned</code>'
+ # Or multiple permissions
+ permission_required = ('catalog.<code>can_mark_returned</code>', 'catalog.can_edit')
+ # Note that 'catalog.can_edit' is just an example
+ # the catalog application doesn't have such permission!</pre>
+
+<h3 id="示例">示例</h3>
+
+<p>我们不会在这里更新 LocalLibrary;也许在下一个教程中!</p>
+
+<h2 id="挑战自己"><a id="Challenge_yourself" name="Challenge_yourself"></a>挑战自己</h2>
+
+<p>在本文前面,我们向您展示了,如何为当前用户创建一个页面,列出他们借用的书本。现在的挑战,是创建一个只对图书馆员可见的类似页面,它显示所有借用的书本,其中包括每个借用人的名字。</p>
+
+<p>您应该能够遵循与其他视图相同的模式。主要区别在于,您需要将视图限制为仅限图书馆员。您可以根据用户是否是工作人员(函数装饰器:<code>staff_member_required</code>,模板变量:<code>user.is_staff</code>)来执行此操作,但我们建议您改为使用<code>can_mark_returned</code>权限,和 <code>PermissionRequiredMixin</code>,如上一节所述。</p>
+
+<div class="warning">
+<p><strong>重要</strong>: 请记住,不要使用超级用户进行基于权限的测试(即使尚未定义权限,权限检查也会对超级用户返回 true)。而是要创建一个图书管理员用户,并添加所需的功能。</p>
+</div>
+
+<p>完成后,您的页面应该类似于下面的屏幕截图。</p>
+
+<p><img alt="All borrowed books, restricted to librarian" src="https://mdn.mozillademos.org/files/14115/library_borrowed_all.png" style="border-style: solid; border-width: 1px; display: block; height: 283px; margin: 0px auto; width: 500px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="总结">总结</h2>
+
+<p>做的太好了 — 你已经创造了一个网站,图书馆用户可以登入并检视他们拥有的内容,图书管理员(有正确的授权)可以检视所有借出的书本以及借阅者。目前,我们仍然只是查看内容,但是当您想要开始修改和添加数据时,会使用相同的原则和技术。</p>
+
+<p>在我们的下一篇文章,我们将介绍如何使用Django 表单,收集使用者输入,然后开始修改我们储存的一些资料。</p>
+
+<h2 id="也可以参考">也可以参考</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/auth/">Django中的用户授权</a> (Django 文档)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/auth/default//">使用 Django 授权系统(默认) </a>(Django 文档)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/intro/#decorating-class-based-views">介绍从基于类别的视图 &gt; 到使用装饰器的基于类别的视图</a> (Django 文档)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Sessions", "Learn/Server-side/Django/Forms", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="本教程文档">本教程文档</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django 介绍</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">架设 Django 开发环境</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django 教程: The Local Library website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django 教程 2: Creating a skeleton website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django 教程 3: Using models</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django 教程 4: Django admin site</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django 教程 5: Creating our home page</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django 教程 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django 教程 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django 教程 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django 教程 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django 教程 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django 教程 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django 网页应用安全</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django 微博客</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/django/deployment/index.html b/files/zh-cn/learn/server-side/django/deployment/index.html
new file mode 100644
index 0000000000..fe582da51f
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/deployment/index.html
@@ -0,0 +1,675 @@
+---
+title: 'Django 教程 11: 部署 Django 到生产环境'
+slug: learn/Server-side/Django/Deployment
+translation_of: Learn/Server-side/Django/Deployment
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Testing", "Learn/Server-side/Django/web_application_security", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">现<font><font>在,您已经创建(并测试)了一个令人敬畏的</font></font><a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website" style='color: rgb(63, 135, 166); margin: 0px; padding: 0px; border: 0px; text-decoration: none; font-family: "Open Sans", arial, x-locale-body, sans-serif; font-size: 20px; font-style: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 255, 255);'><font><font>LocalLibrary</font></font></a><font><font>网站,如果您希望将其安装在公共Web服务器上,以便图书馆工作人员和成员可以通过Internet访问它。</font><font>本文概述了如何找到主机来部署您的网站,以及您需要做什么才能让您的网站准备好生产</font></font>。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row"><strong style='background-color: #ffe8d4; color: #333333; display: inline !important; float: none; font-family: "Open Sans",arial,x-locale-body,sans-serif; font-size: medium; font-style: normal; font-weight: 700; letter-spacing: normal; text-align: left; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal;'>先决条件</strong>:</th>
+ <td>完成所有先前的教程,包括:
+ <p><a href="https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Django/Testing">在线教学 10:测试 Django 的 Web 应用</a></p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><strong style='background-color: #ffe8d4; color: #333333; display: inline !important; float: none; font-family: "Open Sans",arial,x-locale-body,sans-serif; font-size: medium; font-style: normal; font-weight: 700; letter-spacing: normal; text-align: left; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal;'>目的</strong>:</th>
+ <td>了解您可以在何处以及如何将Django应用程序部署到生产环境。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概述">概述</h2>
+
+<p><span style='background-color: #ffffff; color: #333333; display: inline !important; float: none; font-family: "Open Sans",arial,x-locale-body,sans-serif; font-size: medium; font-style: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal;'>一旦您的网站完成(或完成“足够”开始公开测试),您将需要将其托管在比您的个人开发计算机更公开和可访问的地方。</span></p>
+
+<p style='font-style: normal; margin: 0px 0px 24px; padding: 0px; border: 0px; max-width: 42rem; color: rgb(51, 51, 51); font-family: "Open Sans", arial, x-locale-body, sans-serif; font-size: medium; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-style: initial;'><font><font>到目前为止,您一直在开发环境中工作,使用Django开发Web服务器将您的站点共享到本地浏览器/网络,并使用暴露调试和其他私人信息的(不安全)开发设置运行您的网站。</font><font>在您可以从外部托管网站之前,您首先必须:</font></font></p>
+
+<ul style='font-style: normal; margin: 0px 0px 24px; padding: 0px 0px 0px 40px; border: 0px; max-width: 42rem; color: rgb(51, 51, 51); font-family: "Open Sans", arial, x-locale-body, sans-serif; font-size: medium; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-style: initial;'>
+</ul>
+
+<ul>
+ <li>对你的项目设置(project settings)做一定的修改</li>
+ <li>选择一个用来托管Django app的环境</li>
+ <li>选择一个用来托管所有静态文件的环境</li>
+ <li>设置一个产品级的设施来为你的网站服务</li>
+</ul>
+
+<p style='font-style: normal; margin: 0px 0px 24px; padding: 0px; border: 0px; max-width: 42rem; color: rgb(51, 51, 51); font-family: "Open Sans", arial, x-locale-body, sans-serif; font-size: medium; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-style: initial;'><font><font>本教程为您选择托管站点提供了一些指导,简要概述了为了让您的Django应用程序可用于生产需要做什么以及如何将LocalLibrary网站安装到</font></font><a class="external external-icon" href="https://www.heroku.com/" rel="noopener" style="font-style: normal !important; color: rgb(63, 135, 166); margin: 0px; padding: 0px; border: 0px; text-decoration: none; white-space: pre-line;"><font><font>Heroku</font></font></a><font><font>云托管上服务。</font></font></p>
+
+<h2 id="什么是生产环境">什么是生产环境?</h2>
+
+<p>生产环境是一个由服务器电脑提供的环境,你将在这里运行你的网站,为外部使用提供服务。生产环境包括:</p>
+
+<ul>
+ <li>网站运行所需要的电脑硬件</li>
+ <li>操作系统 (例如 Linux, Windows).</li>
+ <li>编程语言运行库和框架库,在其上编写您的网站。</li>
+ <li>用于提供页面和其他内容的Web服务器(例如Nginx,Apache)。</li>
+ <li>在 Django 网站和 Web 服务器之间,传递“动态”请求的应用程序服务器。</li>
+ <li>您的网站所依赖的数据库。</li>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>: 根据您的生产配置方式,您可能还有反向代理,负载均衡器等。</p>
+</div>
+
+<p>服务器计算机可以位于您的场所,并通过快速链接连接到 Internet,但使用托管“在云中”的计算机更为常见。这实际上意味着,您的代码在托管公司的数据中心的某台远程计算机(或可能是“虚拟”计算机)上运行。远程服务器通常会以特定价格,提供一些保证级别的计算资源(例如CPU,RAM,存储器等)和互联网连接。</p>
+
+<p>这种可远程访问的计算/网络硬件,称为基础架构即服务(IaaS)。许多IaaS供应商,提供预安装特定操作系统的选项,您必须在其上安装生产环境的其他组件。其他供应商允许您选择功能更全面的环境,可能包括完整的 Django ,和 Web 服务器设置。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 预构建环境可以使您的网站设置变得非常简单,因为它们会减少配置,但可用选项可能会限制您使用不熟悉的服务器(或其他组件),并且可能基于较旧版本的操作系统。通常最好自己安装组件,以便获得所需的组件,当您需要升级系统的某些部分时,您就知道从哪里开始!</p>
+</div>
+
+<p>其他托管服务提供商,支持 Django 作为平台即服务(PaaS)产品的一部分。在这种托管中,您不必担心大多数生产环境(Web 服务器,应用程序服务器,负载平衡器),因为主机平台会为您处理这些(以及为了扩展您的应用程序,而需要做的大部分工作)。这使得部署非常简单,因为您只需要专注于 Web 应用程序,而不是所有其他服务器的基础结构。</p>
+
+<p>相对于 PaaS,一些开发人员会选择 IaaS 所提供的更高灵活性,而其他开发人员,则欣赏 PaaS 降低的维护开销,和更轻松地扩展。当您开始使用时,在 PaaS 系统上设置您的网站,要容易得多,因此我们将在本教程中这么做。</p>
+
+<div class="note">
+<p><strong>提示:</strong> 如果您选择一个 Python/Django 友好的托管服务提供商,他们应该提供有关如何使用不同配置的网络服务器,应用服务器,反向代理等设置 Django 网站的说明(如果您选择 PaaS,这就没有关系了)。例如,<a href="https://www.digitalocean.com/community/tutorials?q=django">Digital Ocean Django 社区文档 </a>中的各种配置,有许多手把手指南。</p>
+</div>
+
+<h2 id="选择托管服务提供商">选择托管服务提供商</h2>
+
+<p>已知有超过100个托管服务提供商,积极支持或与 Django 合作(您可以在 <a href="http://djangofriendly.com/hosts/">Djangofriendly hosts </a>主机上,找到相当广泛的列表)。这些供应商提供不同类型的环境(IaaS,PaaS),以及不同价格、不同级别的计算和网络资源。</p>
+
+<p>选择主机时需要考虑的一些事项:</p>
+
+<ul>
+ <li>您的网站可能有多忙,以及满足该需求,所需的数据和计算资源的成本。</li>
+ <li>水平扩展(添加更多机器)和垂直扩展(升级到更强大的机器)的支持级别,以及这样做的成本。</li>
+ <li>供应商的数据中心位于何处,因此访问可能是最快的。</li>
+ <li>主机的历史正常运行时间,和停机时间的表现。</li>
+ <li>用于管理站点的工具 - 易于使用且安全(例如 SFTP 相比于 FTP)。</li>
+ <li>用于监控服务器的内置框架。</li>
+ <li>已知限制。有些主机会故意阻止某些服务(例如电子邮件)。其他在某些价格层中,仅提供一定时数的“实时时间”,或者仅提供少量存储空间。</li>
+ <li>额外的好处。一些提供商将提供免费域名和 SSL 证书支持,否则您将不得不为此支付费用。</li>
+ <li>您所依赖的“免费”等级,是否会随着时间的推移而过期,以及迁移到更昂贵等级的成本,是否意味着,您最好一开始就使用其他服务!</li>
+</ul>
+
+<p>当你刚开始时,好消息是,有很多网站提供了 “免费” 的 “评估”、“开发者” 或 “爱好者” 计算环境。这些始终是资源相当受限/有限的环境,您需要注意,它们可能会在广告期限后过期。然而,它们非常适合在真实环境中,测试低流量站点,并且可以在您的站点变得更加繁忙时,付费取得更多资源,并轻松迁移。此类别中的热门选择包括<a href="https://www.heroku.com/">Heroku</a>, <a href="https://www.pythonanywhere.com/">Python Anywhere</a>, <a href="http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/billing-free-tier.html">Amazon Web Services</a>, <a href="https://azure.microsoft.com/en-us/pricing/details/app-service/">Microsoft Azure</a> 等。</p>
+
+<p>许多提供商还有“基本”层,可提供更多有用的计算能力和更少的限制。<a href="https://www.digitalocean.com/">Digital Ocean</a> 和 <a href="https://www.pythonanywhere.com/">Python Anywhere</a> ,就是流行的托管服务提供商的例子,提供相对便宜的基本计算等级(每月 5 美元到 10 美元不等)。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 请记住,价格不是唯一的选择标准。如果您的网站成功,可能会发现,可扩展性是最重要的考虑因素。</p>
+</div>
+
+<h2 id="让您的网站准备好发布">让您的网站准备好发布</h2>
+
+<p>使用 django-admin 和 manage.py 工具创建的 Django 骨架网站,是为了使开发更容易而配置的。出于安全性或性能原因,许多 Django 项目设置(在<strong>settings.py</strong>中指定),在生产应该是不同的。</p>
+
+<div class="note">
+<p><strong>提示:</strong> 通常有一个单独的 <strong>settings.py</strong> 文件用于生产环境,并从单独的文件或环境变量,导入敏感设置。即使其他源代码在公共存储库中可用,也应保护此文件。</p>
+</div>
+
+<p>您必须检查的关键设置是:</p>
+
+<ul>
+ <li><code>DEBUG</code>. 这应该在生产环境中设置为 <code>False</code>(<code>DEBUG = False</code>)。这将停止显示敏感/机密调试跟踪和变量信息。</li>
+ <li><code>SECRET_KEY</code>. 这是用于CRSF保护等的大随机值。重要的是,生产中使用的密钥,不应在源代码管理中、或在生产服务器外部可访问。 Django文档表明,可能最好从环境变量加载,或从仅供服务的文件中读取。
+ <pre class="notranslate"># Read SECRET_KEY from an environment variable
+import os
+SECRET_KEY = os.environ['SECRET_KEY']
+
+#OR
+
+#Read secret key from a file
+with open('/etc/secret_key.txt') as f:
+ SECRET_KEY = f.read().strip()</pre>
+ </li>
+</ul>
+
+<p>让我们更改 LocalLibrary 应用程序,以便我们从环境变量中,读取<code>SECRET_KEY</code> 和 <code>DEBUG</code>变量(如果已定义),否则使用配置文件中的默认值。</p>
+
+<p>打开 <strong>/locallibrary/settings.py</strong>,禁用原始的<code>SECRET_KEY</code>配置,并加入如下以<strong>粗体</strong>显示的几行。在开发过程中,不会为密钥指定环境变量,因此将使用默认值(在此处使用的密钥,或密钥“泄漏”无关紧要,因为您不会在生产环境中使用它)。</p>
+
+<pre class="brush: python notranslate"># SECURITY WARNING: keep the secret key used in production secret!
+# SECRET_KEY = 'cg#p$g+j9tax!#a3cup@1$8obt2_+&amp;k3q+pmu)5%asj6yjpkag'
+<strong>import os</strong>
+<strong>SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'cg#p$g+j9tax!#a3cup@1$8obt2_+&amp;k3q+pmu)5%asj6yjpkag')</strong>
+</pre>
+
+<p>然后注释掉现有的<code>DEBUG</code>设置,并如下所示,添加新的一行。</p>
+
+<pre class="brush: python notranslate"># SECURITY WARNING: don't run with debug turned on in production!
+# DEBUG = True
+<strong>DEBUG = bool( os.environ.get('DJANGO_DEBUG', True) )</strong>
+</pre>
+
+<p>默认情况下,<code>DEBUG</code>的值为<code>True</code>,但如果<code>DJANGO_DEBUG</code>环境变量的值,设置为空字符串,则为<code>False</code>,例如,<code>DJANGO_DEBUG=''</code>。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 如果我们可以直接将<code>DJANGO_DEBUG</code>环境变量设置为<code>True</code>或<code>False</code>,而不是分别使用“any string”或“empty string”,那将更直观。不幸的是,环境变量值存储为 Python 字符串,计算结果为 <code>False</code> 的唯一字符串,是空字符串(例如<code>bool('')==False</code>)。</p>
+</div>
+
+<p><a href="https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/">部署清单(Django文档)</a>中,提供了您可能要更改的完整设置清单。您还可以使用下面的终端命令,列出其中的一些:</p>
+
+<pre class="brush: python notranslate">python3 manage.py check --deploy
+</pre>
+
+<h2 id="示例:在Heroku上安装LocalLibrary">示例:在Heroku上安装LocalLibrary</h2>
+
+<p>本节提供了如何在 <a href="http://heroku.com">Heroku PaaS cloud </a>云上安装 LocalLibrary 的实际演示。</p>
+
+<h3 id="为何选择Heroku?">为何选择Heroku?</h3>
+
+<p>Heroku 是运行时间最长,且最受欢迎的基于云的 PaaS 服务之一。它最初只支持 Ruby 应用程序,但现在可用于托管来自许多编程环境的应用程序,包括 Django!</p>
+
+<p>我们选择使用Heroku有以下几个原因:</p>
+
+<ul>
+ <li>Heroku 有一个真正免费的免费套餐<a href="https://www.heroku.com/pricing">free tier</a> (尽管有一些限制)。</li>
+ <li>作为 PaaS,Heroku 为我们提供了大量的 Web 基础架构。这使得入门更加容易,因为您不必担心 Heroku 为我们提供的服务器,负载平衡器,反向代理或任何其他 Web 基础结构。</li>
+ <li>虽然它确实有一些限制,但这些不会影响本教程的应用程序。例如:
+ <ul>
+ <li>Heroku只提供短期储存,因此用户上传的文件无法安全地存储在 Heroku 本身。</li>
+ <li>如果半小时内没有请求,免费套餐将使不活动的网络应用程序进入睡眠。然后,该网站可能需要几秒钟才能被唤醒。</li>
+ <li>免费套餐将您网站运行的时间,限制为每月一定的小时数(不包括网站“睡着”的时间)。这对于低使用/演示站点来说很好,但如果需要100%的正常运行时间则不适用。</li>
+ <li>在 <a href="https://devcenter.heroku.com/articles/limits">Limits</a> (Heroku 文档) 中列出了其他限制。</li>
+ </ul>
+ </li>
+ <li>大多数情况下它只是能工作,如果你最终喜欢它,扩展你的应用程序非常容易。</li>
+</ul>
+
+<p>虽然 Heroku 非常适合用于此演示,但它可能并不适合您的真实网站。 Heroku可以轻松设置和扩展,但代价是灵活性较低,而且一旦退​​出免费套餐,可能会花费更多。</p>
+
+<h3 id="Heroku是如何工作的?">Heroku是如何工作的?</h3>
+
+<p>Heroku 在一个或多个 “<a href="https://devcenter.heroku.com/articles/dynos">Dynos</a>” 中,运行Django网站,这是一个独立的虚拟化 Unix 容器,提供运行应用程序所需的环境。<a href="https://devcenter.heroku.com/articles/dynos">Dynos </a>是完全隔离的,并且有一个短暂的文件系统(一个短暂的文件系统,每次dyno 重新启动时,都会清理/清空)。 Dynos 默认共享的唯一内容,是应用程序配置变量。 Heroku 内部使用负载均衡器,将 Web 流量分配给所有 “web” dynos。由于他们之间没有任何共享,Heroku可以通过添加更多 dynos ,来水平扩展应用程序(当然,您可能还需要扩展数据库,以接受其他连接)。</p>
+
+<p>由于文件系统是暂时的,因此无法直接安装应用程序所需的服务(例如数据库,队列,缓存系统,存储,电子邮件服务等)。取代的是,Heroku Web 应用程序,使用 Heroku 或第三方作为独立“附加组件”提供的支持服务。一旦连接到 Web 应用程序,dynos 就会使用应用程序配置变量中包含的信息,来访问服务。</p>
+
+<p>为了执行您的应用程序,Heroku 需要能够设置适当的环境,和依赖关系,并了解它是如何启动的。对于 Django 应用程序而言,我们在一些文本文件中提供此信息:</p>
+
+<ul>
+ <li><strong>runtime.txt</strong>:<strong> </strong>要使用的编程语言和版本。</li>
+ <li><strong>requirements.txt</strong>: Python组件依赖项,包括Django。</li>
+ <li><strong>Procfile</strong>: 启动 Web 应用程序要执行的进程列表。对于Django,这通常是 Gunicorn Web 应用程序服务器(带有 <code>.wsgi </code>脚本)。</li>
+ <li><strong>wsgi.py</strong>: 在 Heroku 环境中,调用我们的Django 应用程序的 <a href="http://wsgi.readthedocs.io/en/latest/what.html">WSGI </a>配置。</li>
+</ul>
+
+<p>开发人员使用特殊的客户端应用程序/终端与 Heroku 交互,这很像Unix bash 脚本。这允许您上传存在 git 储存库中的代码,检查正在运行的进程,查看日志,设置配置变量等等!</p>
+
+<p>为了让我们的应用程序在Heroku上工作,我们需要将我们的 Django Web 应用程序,放入git储存库,添加上面的文件,集成数据库附加组件,并进行更改,以正确处理静态文件。</p>
+
+<p>完成所有操作后,我们可以设置 Heroku 帐户,获取 Heroku 客户端,并使用它来安装我们的网站。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 以下说明反映了在撰写本书时,如何使用 Heroku。如果 Heroku 显着改变了他们的操作过程,您可能希望检查他们的设置文档:<a href="https://devcenter.heroku.com/articles/getting-started-with-python#introduction">在Heroku上开始使用 Django</a>。</p>
+</div>
+
+<p>就是您一开始所需的所有概述(请参阅 <a href="https://devcenter.heroku.com/articles/how-heroku-works">Heroku 如何工作</a>,以获取更全面的指南)。</p>
+
+<h3 id="在_Github_中创建应用程序储存库">在 Github 中创建应用程序储存库</h3>
+
+<p>Heroku 与 <strong>git</strong> 源代码版本控制系统紧密集成,使用它来上传/同步您对实时系统所做的任何更改。它通过添加一个名为 heroku 的新的heroku “远程” 储存库,来指向 Heroku 云上的源储存库。在开发期间,您使用 git 在“主”储存库中储存更改。如果要部署站点,请将更改同步到 Heroku 储存库。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 如果您习惯于遵循良好的软件开发实践,那么您可能已经在使用 git,或其他一些 SCM 系统。如果您已有 git 储存库,则可以跳过此步骤。</p>
+</div>
+
+<p>有很多方法可以使用 git,但最简单的方法之一,是首先在 <a href="https://github.com/">Github</a> 上建立一个帐户,在那里创建储存库,然后将它同步到本地:</p>
+
+<ol>
+ <li>访问 <a href="https://github.com/">https://github.com/</a> 并创建一个帐户。</li>
+ <li>登录后,点击顶部工具栏中的 + 链接,然后选择新建储存库 <strong>New repository</strong>。</li>
+ <li>填写此表单上的所有字段。虽然这些不是强制性的,但强烈建议使用它们。
+ <ul>
+ <li>输入新的储存库名称(例如 django_local_library)和描述(例如 “用 Django编写的本地图书馆网站”)。</li>
+ <li>在Add .gitignore 选择列表中,选择 <strong>Python</strong>。</li>
+ <li>在添加许可证选择列表中,选择您想要的许可证。</li>
+ <li>选中使用自述文件初始化此储存库(<strong>Initialize this repository with a README)</strong>。</li>
+ </ul>
+ </li>
+ <li>点击 <strong>Create repository</strong>.</li>
+ <li>点击新仓库页面上的绿色 “克隆或下载”<strong> </strong>(<strong>Clone or download</strong>)按钮 。</li>
+ <li>从显示的对话框中的文本字段中复制URL值(它应该类似于: <strong>https://github.com/<em>&lt;your_git_user_id&gt;</em>/django_local_library.git</strong>)。</li>
+</ol>
+
+<p>现在创建了储存库(“repo”),我们将要在本地计算机上克隆它:</p>
+
+<ol>
+ <li>为您的本地计算机安装 git(您可以在<a href="https://git-scm.com/downloads">此处</a>找到不同平台的版本)。</li>
+ <li>打开命令提示符/终端,并使用您在上面复制的 URL 克隆储存库:
+ <pre class="brush: bash notranslate">git clone https://github.com/<strong><em>&lt;your_git_user_id&gt;</em></strong>/django_local_library.git
+</pre>
+ 这将在当前目录下方创建储存库。</li>
+ <li>切换目录,到新的仓库。
+ <pre class="brush: bash notranslate">cd django_local_library</pre>
+ </li>
+</ol>
+
+<p>最后一步是复制你的应用程序,然后使用 git ,将文件添加到你的仓库:</p>
+
+<ol>
+ <li>将您的 Django 应用程序,复制到此文件夹(与 <strong>manage.py</strong> 级别相同的、和以下级别的所有文件,而<strong>不是</strong>包含 locallibrary 文件夹的文件)。</li>
+ <li>打开<strong>.gitignore</strong>文件,将以下几行复制到其底部,然后保存(此文件用于标识默认情况下,不应上传到 git 的文件)。
+ <pre class="notranslate"># Text backup files
+*.bak
+
+#Database
+*.sqlite3</pre>
+ </li>
+ <li>打开命令提示符/终端,并使用<code>add</code>命令,将所有文件添加到 git。
+ <pre class="brush: bash notranslate">git add -A
+</pre>
+ </li>
+ <li>使用 status 命令,检查要添加的所有文件是否正确(您希望包含源文件,而不是二进制文件,临时文件等)。它应该看起来有点像下面的列表。
+ <pre class="notranslate">&gt; git status
+On branch master
+Your branch is up-to-date with 'origin/master'.
+Changes to be committed:
+  (use "git reset HEAD &lt;file&gt;..." to unstage)
+
+        modified:   .gitignore
+        new file:   catalog/__init__.py
+ ...
+        new file:   catalog/migrations/0001_initial.py
+ ...
+        new file:   templates/registration/password_reset_form.html</pre>
+ </li>
+ <li>如果您满意,请将文件提交到本地储存库:
+ <pre class="brush: bash notranslate">git commit -m "First version of application moved into github"</pre>
+ </li>
+ <li>然后使用以下内容,将本地储存库同步到 Github 网站:
+ <pre class="notranslate">git push origin master</pre>
+ </li>
+</ol>
+
+<p>完成此操作后,您应该可以返回创建储存库的 Github上的页面,刷新页面,并看到您的整个应用程序已经上传。使用此添加/提交/推送循环,您可以在文件更改时,继续更新储存库。</p>
+
+<div class="note">
+<p><strong>提示:</strong> 这是备份您的“vanilla”项目的一个好时机 - 虽然我们将在以下部分中,进行的一些更改,可能对有些人在任何平台(或开发)上的部署有用,对其他人可能没有用。</p>
+
+<p>执行此操作的最佳方法,是使用 git 来管理您的修订。使用 git,您不仅可以回到特定的旧版本,而且可以在生产变更的单独“分支”中进行维护,并选择在生产和开发分支之间移动的任何更改。<a href="https://help.github.com/articles/good-resources-for-learning-git-and-github/">学习 Git</a> 非常值得,但超出了本主题的范围。</p>
+
+<p>最简单的方法是将文件复制到另一个位置。使用最符合您对 git 了解的方法!</p>
+</div>
+
+<h3 id="更新_Heroku_的应用程序">更新 Heroku 的应用程序</h3>
+
+<p>本节介绍了您需要对 LocalLibrary 应用程序进行的更改,以使其在Heroku上运行。虽然 Heroku 的<a href="https://devcenter.heroku.com/articles/getting-started-with-python#introduction">在 Heroku 使用 Django 入门教程</a>,假设您将使用 Heroku 客户端,来运行您的本地开发环境,但我们的更改,与现有的 Django 开发服务器,以及我们已经学习的工作方式兼容。</p>
+
+<h4 id="Procfile">Procfile</h4>
+
+<p>在 GitHub 储存库的根目录中,创建文件<code>Procfile</code>(无扩展名),以声明应用程序的进程类型和入口点。将以下文本复制到其中:</p>
+
+<pre class="notranslate">web: gunicorn locallibrary.wsgi --log-file -</pre>
+
+<p>“<code>web:</code>”告诉 Heroku ,这是一个 web dyno,可以发送 HTTP 流量。在这个 dyno 中启动的进程,是 gunicorn,这是 Heruko 推荐的一种流行的 Web 应用程序服务器。我们使用模块 <code>locallibrary.wsgi</code>(使用我们的应用程序框架创建:<strong>/locallibrary/wsgi.py </strong>)中的配置信息启动 Gunicorn。</p>
+
+<h4 id="Gunicorn">Gunicorn</h4>
+
+<p><a href="http://gunicorn.org/">Gunicorn</a> 是推荐的 HTTP 服务器,用于 Heroku 上的 Django(如上面的 Procfile 中所述)。它是一个用于 WSGI 应用程序的纯 Python HTTP 服务器,可以在一个 dyno 中,运行多个 Python 并发进程(有关更多信息,请参阅<a href="https://devcenter.heroku.com/articles/python-gunicorn">使用 Gunicorn 部署 Python 应用程序</a>)。</p>
+
+<p>虽然在开发期间,我们不需要 Gunicorn 为我们的 LocalLibrary 应用程序提供服务,但我们将安装它,以便它成为我们在远程服务器上设置 Heroku 的 <a href="#requirements">requirements</a> 的一部分。</p>
+
+<p>使用 pip(我们在<a href="/zh-CN/docs/Learn/Server-side/Django/development_environment">设置开发环境</a>时安装)在命令行上,将 Gunicorn 安装到本地:</p>
+
+<pre class="brush: bash notranslate">pip3 install gunicorn
+</pre>
+
+<h4 id="数据库配置">数据库配置</h4>
+
+<p>我们不能在 Heroku 上使用默认的 SQLite 数据库,因为它是基于文件的,并且每次应用程序重新启动时,都会从暂时的文件系统中删除它(通常每天一次,每次应用程序或其配置变量被更改时)。</p>
+
+<p>处理这种情况的 Heroku 机制,是使用<a href="https://elements.heroku.com/addons#data-stores">数据库加载项</a>,并使用来自加载项设置的环境<a href="https://devcenter.heroku.com/articles/config-vars">配置变量</a>的信息,来配置 Web 应用程序。有很多数据库选项,但我们将使用 Heroku postgres 数据库的<a href="https://devcenter.heroku.com/articles/heroku-postgres-plans#plan-tiers">爱好者等级</a>,因为它是免费的,被 Django 所支持,并在使用免费的爱好者 dyno 计划等级时,会自动添加到新的 Heroku 应用程序。</p>
+
+<p>使用名为<code>DATABASE_URL</code>的配置变量,将数据库连接信息提供给 Web dyno。Heroku 建议开发人员使用 <a href="https://warehouse.python.org/project/dj-database-url/">dj-database-url </a>套件包,以解析<code>DATABASE_URL</code>环境变量,并自动将其转换为 Django 所需的配置格式,而不是将此信息硬编码到 Django 中。除了安装 dj-database-url 套件包之外,我们还需要安装<a href="http://initd.org/psycopg/">psycopg2</a>,因为 Django 需要它与 Postgres 数据库进行交互。</p>
+
+<h5 id="dj-database-url_Django_database_configuration_from_environment_variable">dj-database-url (Django database configuration from environment variable)</h5>
+
+<p>在本地安装 dj-database-url,使其成为我们在远程服务器上设置 Heroku 的 <a href="#requirements">requirements</a> 的一部分:</p>
+
+<pre class="notranslate">$ pip3 install dj-database-url
+</pre>
+
+<h5 id="settings.py">settings.py</h5>
+
+<p>打开<strong>/locallibrary/settings.py</strong>,并将以下配置复制到文件的底部:</p>
+
+<pre class="notranslate"># Heroku: Update database configuration from $DATABASE_URL.
+import dj_database_url
+db_from_env = dj_database_url.config(conn_max_age=500)
+DATABASES['default'].update(db_from_env)</pre>
+
+<div class="note">
+<p><strong>注意:</strong></p>
+
+<ul>
+ <li>我们仍然会在开发期间使用SQLite,因为我们的开发计算机上不会设置<code>DATABASE_URL</code>环境变量。</li>
+ <li><code>conn_max_age=500</code>的值使连接持久,这比在每个请求周期重新创建连接更有效。但是,这是可选的,如果需要可以删除。</li>
+</ul>
+</div>
+
+<h5 id="psycopg2_Python_Postgres_database_support">psycopg2 (Python Postgres database support)</h5>
+
+<p>Django 需要 psycopg2 来处理 Postgres 数据库,你需要将它添加到<a href="#requirements">requirements.txt </a>中,以便 Heroku 在远程服务器上进行设置(如下面的 requirements 部分所述)。</p>
+
+<p>Django 默认会在本地使用我们的 SQLite 数据库,因为我们的本地环境中,没有设置<code>DATABASE_URL</code>环境变量。如果您想完全切换到Postgres ,并使用我们的 Heroku 免费等级数据库,进行开发和生产,那么您可以这么做。例如,要在基于 Linux 的系统上,本地安装psycopg2 及其依赖项,您将使用以下 bash / terminal 命令:</p>
+
+<pre class="brush: bash notranslate"><code>sudo apt-get install python-pip python-dev libpq-dev postgresql postgresql-contrib</code>
+pip3 install psycopg2
+</pre>
+
+<p>有关其他平台的安装说明,请访问 <a href="http://initd.org/psycopg/docs/install.html">psycopg2 </a>网站。</p>
+
+<p>但是,您不需要这样做 - 您不需要在本地计算机上激活 PostGreSQL,只要将其作为要求(requirement)提供给 Heroku,请参阅<code>requirements.txt</code>(见下文)。</p>
+
+<h4 id="在生产环境中提供静态文件">在生产环境中提供静态文件</h4>
+
+<p>在开发过程中,我们使用 Django 和 Django 开发 Web 服务器,来提供静态文件(CSS,JavaScript 等)。在生产环境中,我们通常提供来自内容传送网络(CDN)或 Web 服务器的静态文件。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 通过 Django/web 应用程序提供静态文件是低效的,因为请求必须通过不必要的附加代码(Django),而不是由 Web 服务器或完全独立的 CDN 直接处理。虽然这对于开发期间的本地使用无关紧要,但如果我们在生产环境中使用相同的方法,则会对性能产生重大影响。</p>
+</div>
+
+<p>为了便于将静态文件与 Django Web 应用程序分开托管,Django 提供了 collectstatic 工具,来收集这些文件以进行部署(有一个设置变量,用于定义在运行 collectstatic 时,应该收集文件的位置)。 Django 模板是指相对于设置变量(<code>STATIC_URL</code>)的静态文件的托管位置,因此如果将静态文件移动到另一个主机/服务器,则可以更改此位置。</p>
+
+<p>相关的设置变量是:</p>
+
+<ul>
+ <li><code>STATIC_URL</code>:  这是将提供静态文件的基本 URL 位置,例如,在CDN上。这用于在我们的基本模板中访问的静态模板变量(请参阅 <a href="/zh-CN/docs/Learn/Server-side/Django/Home_page">Django 教程 5:创建我们的主页</a>)。</li>
+ <li><code>STATIC_ROOT</code>: 这是 Django 的 “collectstatic” 工具将收集模板中引用的任何静态文件的目录的绝对路径。收集完成后,可以将这些文件,作为一个组上载到托管文件的任何位置。</li>
+ <li><code>STATICFILES_DIRS</code>: 这列出了 Django 的 collectstatic 工具应该搜索静态文件的其他目录。</li>
+</ul>
+
+<h5 id="settings.py_2">settings.py</h5>
+
+<p>打开<strong>/locallibrary/settings.py</strong>,并将以下配置,复制到文件的底部。 <code>BASE_DIR </code>应该已经在您的文件中定义了(<code>STATIC_URL</code>可能已经在文件创建时已经定义。虽然它不会造成任何伤害,但您也可以删除重复的先前引用)。</p>
+
+<pre class="notranslate"># Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.0/howto/static-files/
+
+# The absolute path to the directory where collectstatic will collect static files for deployment.
+STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
+
+# The URL to use when referring to static files (where they will be served from)
+STATIC_URL = '/static/'
+</pre>
+
+<p>我们实际上,将使用名为 <a href="https://warehouse.python.org/project/whitenoise/">WhiteNoise</a> 的库来提供文件,我们将在下一节中安装和配置。</p>
+
+<p>有关更多信息,请参阅 <a href="https://devcenter.heroku.com/articles/django-assets">Django和静态资产</a>(Heroku 文档)。<br>
+ <br>
+ Whitenoise</p>
+
+<p>有许多方法可以在生产环境中提供静态文件(我们在前面的部分中看到了相关的 Django 设置)。 Heroku 建议在生产环境中使用 <a href="https://warehouse.python.org/project/whitenoise/">WhiteNoise</a> 项目,直接从 Gunicorn 提供静态资产。</p>
+
+<div class="note">
+<p><strong>注意: </strong>Heroku 会在上传您的应用程序后,自动调用collectstatic 并准备静态文件,以供 WhiteNoise 使用。查看 <a href="https://warehouse.python.org/project/whitenoise/">WhiteNoise</a> 文档,了解其工作原理以及实现,为什么是提供这些文件的相对有效方法。</p>
+</div>
+
+<p>设置 WhiteNoise 以便在项目中使用的步骤如下:</p>
+
+<h5 id="WhiteNoise">WhiteNoise</h5>
+
+<p>使用以下命令在本地安装 whitenoise:</p>
+
+<pre class="notranslate">$ pip3 install whitenoise
+</pre>
+
+<h5 id="settings.py_3">settings.py</h5>
+
+<p>要将 WhiteNoise 安装到您的 Django 应用程序中,请打开<strong>/locallibrary/settings.py</strong>,找到<code>MIDDLEWARE</code>设置,并在<code>SecurityMiddleware</code>正下方的列表顶部附近,添加<code>WhiteNoiseMiddleware</code>:</p>
+
+<pre class="notranslate">MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+ <strong>'whitenoise.middleware.WhiteNoiseMiddleware',</strong>
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+</pre>
+
+<p>这是可选的,您可以在提供静态文件时,减小它们的大小(这样更有效)。只需将以下内容添加到<strong>/locallibrary/settings.py</strong>的底部:</p>
+
+<pre class="notranslate"># Simplified static file serving.
+# https://warehouse.python.org/project/whitenoise/
+STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
+</pre>
+
+<h4 id="Requirements">Requirements</h4>
+
+<p>Web 应用程序的 Python requirements ,必须放在储存库根目录中的文件 <strong>requirements.txt</strong> 中。然后 Heroku 将在重建您的环境时,自动安装它们。您可以在命令行上,使用 pip 创建此文件(在 repo 根目录中,运行以下命令):</p>
+
+<pre class="brush: bash notranslate">pip3 freeze &gt; requirements.txt</pre>
+
+<p>安装上面所有不同的依赖项后,您的 <strong>requirements.txt</strong> 文件,应至少列出这些项目(尽管版本号可能不同)。请删除下面未列出的任何其他依赖项,除非您已为此应用程序明确添加它们。</p>
+
+<pre class="notranslate">dj-database-url==0.4.1
+Django==2.0
+gunicorn==19.6.0
+<strong>psycopg2==2.6.2</strong>
+whitenoise==3.2.2
+</pre>
+
+<div class="note">
+<p><strong>注意</strong>:确保存在类似上面的 <strong>psycopg2 </strong>的那一行!即使你没有在本地安装它,你仍然应该将它添加到<strong>requirements.txt</strong>。</p>
+</div>
+
+<h4 id="Runtime">Runtime</h4>
+
+<p><strong>runtime.txt </strong>文件(如果已定义)告诉 Heroku 使用哪种编程语言。在 repo 的根目录中,创建文件并添加以下文本:</p>
+
+<pre class="notranslate">python-3.6.4</pre>
+
+<div class="note">
+<p><strong>注意:</strong> Heroku 只支持少量的 <a href="https://devcenter.heroku.com/articles/python-support#supported-python-runtimes">Python 运行库</a>(在编写本文时,包括上面的那个)。 Heroku 将使用受支持的运行库,而不管此文件中指定的值。</p>
+</div>
+
+<h4 id="将更改保存到_Github_并重新测试">将更改保存到 Github 并重新测试</h4>
+
+<p>接下来,我们将所有更改保存到 Github。在终端(我们的存储库中的 whist)中,输入以下命令:</p>
+
+<pre class="brush: python notranslate">git add -A
+git commit -m "Added files and changes required for deployment to heroku"
+git push origin master</pre>
+
+<p>在我们继续之前,让我们在本地,再次测试网站,并确保它不受上述任何更改的影响。像往常一样运行开发 Web 服务器,然后检查站点,是否仍然按预期在浏览器上运行。</p>
+
+<pre class="brush: bash notranslate">python3 manage.py runserver</pre>
+
+<p>我们现在应该准备开始在 Heroku 上部署 LocalLibrary。</p>
+
+<h3 id="获取_Heroku_帐户">获取 Heroku 帐户</h3>
+
+<p>要开始使用Heroku,您首先需要创建一个帐户:</p>
+
+<ul>
+ <li>访问 <a href="https://www.heroku.com/">www.heroku.com</a> ,并单击免费注册按钮(<strong>SIGN UP FOR FREE)</strong>。</li>
+ <li>输入您的详细信息,然后按 <strong>CREATE FREE ACCOUNT</strong>。系统会要求您,检查帐户中是否有注册电子邮件。</li>
+ <li>单击注册电子邮件中的帐户激活链接。您将在网络浏览器上收回您的帐户。</li>
+ <li>输入您的密码,然后单击 <strong>SET PASSWORD AND LOGIN</strong>。</li>
+ <li>然后,您将登录并进入 Heroku 仪表板: <a href="https://dashboard.heroku.com/apps">https://dashboard.heroku.com/apps</a>.</li>
+</ul>
+
+<h3 id="安装客户端">安装客户端</h3>
+
+<p>按照 <a href="https://devcenter.heroku.com/articles/getting-started-with-python#set-up">Heroku 上的说明</a>,下载并安装 Heroku 客户端。</p>
+
+<p>安装客户端后,您将能够运行命令。例如,要获得客户端的帮助:</p>
+
+<pre class="brush: bash notranslate">heroku help
+</pre>
+
+<h3 id="创建并上传网站">创建并上传网站</h3>
+
+<p>要创建应用程序,我们在储存库的根目录中,运行“create”命令。这将在我们的本地 git 环境中,创建一个名为 heroku 的 git remote(“指向远程储存库的指标”)。</p>
+
+<pre class="brush: bash notranslate">heroku create</pre>
+
+<div class="note">
+<p><strong>注意:</strong> 如果您愿意,可以通过在“创建”之后指定值来命名远程。如果你不这样做,你会得到一个随机的名字。该名称用于默认URL。</p>
+</div>
+
+<p>然后我们可以将应用程序,推送到 Heroku 储存库,如下所示。这将上传应用程序,将其打包到 dyno 中,运行 collectstatic,然后启动该站点。</p>
+
+<pre class="brush: bash notranslate">git push heroku master</pre>
+
+<p>如果我们很幸运,该应用程序现在在网站上“运行”,但它将无法正常工作,因为我们尚未设置数据库表,以供我们的应用程序使用。为此,我们需要使用 heroku run命令,并启动 “<a href="https://devcenter.heroku.com/articles/deploying-python#one-off-dynos">one off dyno</a>” 来执行迁移操作。在终端中输入以下命令:</p>
+
+<pre class="brush: bash notranslate">heroku run python manage.py migrate</pre>
+
+<p>我们还需要能够添加书本和作者,所以我们再次使用一次性dyno,创建我们的管理超级用户:</p>
+
+<pre class="brush: bash notranslate">heroku run python manage.py createsuperuser</pre>
+
+<p>完成后,我们可以查看该网站。它应该有用,虽然它还没有任何书本。要打开浏览器访问新网站,请使用以下命令:</p>
+
+<pre class="brush: bash notranslate">heroku open</pre>
+
+<p>在管理站点中创建一些书本,并检查该站点是否按预期运行。</p>
+
+<h3 id="管理附加组件(插件)">管理附加组件(插件)</h3>
+
+<p>您可以使用 <code>heroku addons</code>命令,查看应用程序的附加组件。这将列出所有附加组件,以及它们的价格等级和状态。</p>
+
+<pre class="brush: bash notranslate">&gt;heroku addons
+
+Add-on Plan Price State
+───────────────────────────────────────── ───────── ───── ───────
+heroku-postgresql (postgresql-flat-26536) hobby-dev free created
+ └─ as DATABASE</pre>
+
+<p>在这里,我们看到我们只有一个附加组件,即 postgres SQL数据库。这是免费的,并且是在我们创建应用时,自动创建的。您可以使用以下命令,更详细地打开网页,以检查数据库附加组件(或任何其他附加组件):</p>
+
+<pre class="brush: bash notranslate">heroku addons:open heroku-postgresql
+</pre>
+
+<p>其他命令允许您创建,销毁,升级和降级附加组件(使用类似的语法打开)。有关更多信息,请参阅<a href="https://devcenter.heroku.com/articles/managing-add-ons">管理附加组件</a>(Heroku 文档)。</p>
+
+<h3 id="设定配置变量">设定配置变量</h3>
+
+<p>您可以使用 <code>heroku config</code>命令,检查站点的配置变量。下面你可以看到,我们只有一个变量,<code>DATABASE_URL</code>用于配置我们的数据库。</p>
+
+<pre class="brush: bash notranslate">&gt;heroku config
+
+=== locallibrary Config Vars
+DATABASE_URL: postgres://uzfnbcyxidzgrl:j2jkUFDF6OGGqxkgg7Hk3ilbZI@ec2-54-243-201-144.compute-1.amazonaws.com:5432/dbftm4qgh3kda3</pre>
+
+<p>如果您回想起来<strong>准备发布网站</strong>的部分,我们必须为<code>DJANGO_SECRET_KEY</code> 和 <code>DJANGO_DEBUG</code>设置环境变量。我们现在就这样做。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 密钥需要真正的保密!生成新密钥的一种方法,是创建一个新的 Django 项目(<code>django-admin startproject someprojectname</code>),然后从 <strong>settings.py </strong>中,获取为您生成的密钥。</p>
+</div>
+
+<p>我们使用 <code>config:set</code>命令,设置<code>DJANGO_SECRET_KEY</code>(如下所示)。记得使用自己的密钥!</p>
+
+<pre class="brush: bash notranslate">&gt;heroku config:set DJANGO_SECRET_KEY=eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh&amp;=
+
+Setting DJANGO_SECRET_KEY and restarting locallibrary... done, v7
+DJANGO_SECRET_KEY: eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh
+</pre>
+
+<p>我们以同样的方式设置<code>DJANGO_DEBUG</code>:</p>
+
+<pre class="brush: bash notranslate">&gt;heroku config:set <code>DJANGO_DEBUG=''
+
+Setting DJANGO_DEBUG and restarting locallibrary... done, v8</code></pre>
+
+<p>如果您现在访问该站点,您将收到“错误请求”(Bad request)错误,因为如果您有 <code>DEBUG=False</code>(作为安全措施),则需要<a href="https://docs.djangoproject.com/en/2.0/ref/settings/#allowed-hosts">ALLOWED_HOSTS</a> 设置。打开 <strong>/locallibrary/settings.py</strong>,并更改<code>ALLOWED_HOSTS</code>设置,以包含您的基本应用程序 URL(例如'locallibrary1234.herokuapp.com'),以及您通常在本地开发服务器上使用的 URL。</p>
+
+<pre class="brush: python notranslate">ALLOWED_HOSTS = ['&lt;your app URL without the https:// prefix&gt;.herokuapp.com','127.0.0.1']
+# For example:
+# ALLOWED_HOSTS = ['fathomless-scrubland-30645.herokuapp.com','127.0.0.1']
+</pre>
+
+<p>然后保存您的设置,并将它们提交到您的 Github 仓库和 Heroku:</p>
+
+<pre class="brush: bash notranslate">git add -A
+git commit -m 'Update ALLOWED_HOSTS with site and development server URL'
+git push origin master
+git push heroku master</pre>
+
+<div class="note">
+<p>完成 Heroku 的站点更新后,输入一个不存在的 URL(例如,<strong>/catalog/doesnotexist/</strong>)。以前这会显示一个详细的调试页面,但现在,您应该只看到一个简单的“未找到”页面。</p>
+</div>
+
+<h3 id="除错调试">除错调试</h3>
+
+<p>Heroku客户端提供了一些调试工具:</p>
+
+<pre class="brush: bash notranslate">heroku logs # Show current logs
+heroku logs --tail # Show current logs and keep updating with any new results
+heroku config:set DEBUG_COLLECTSTATIC=1 # Add additional logging for collectstatic (this tool is run automatically during a build)
+heroku ps #Display dyno status
+</pre>
+
+<p>如果您需要比这些更多的信息,您将需要开始研究 <a href="https://docs.djangoproject.com/en/2.0/topics/logging/">Django Logging</a>。</p>
+
+<h2 id="总结"><strong><span style='font-family: x-locale-heading-primary,zillaslab,Palatino,"Palatino Linotype",x-locale-heading-secondary,serif; font-size: 2.5rem;'>总结</span></strong></h2>
+
+<p>本教程讲述如何在生产环境中配置Django应用,也是本系列Django教程的结尾。我们希望你觉得教程有用。你可以在Github上取得一个完全可工作版本的<a href="https://github.com/mdn/django-locallibrary-tutorial">源码(用力点击此处)</a>。</p>
+
+<p>下一步是阅读我们此前的一些文章,然后完成评估任务。</p>
+
+<h2 id="也可以参考">也可以参考</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/howto/deployment/">Deploying Django</a> (Django 文档)
+
+ <ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/">Deployment checklist</a> (Django 文档)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/howto/static-files/deployment/">Deploying static files</a> (Django 文档)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/">How to deploy with WSGI</a> (Django 文档)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/modwsgi/">How to use Django with Apache and mod_wsgi</a> (Django 文档)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/gunicorn/">How to use Django with Gunicorn</a> (Django 文档)</li>
+ </ul>
+ </li>
+ <li>Heroku
+ <ul>
+ <li><a href="https://devcenter.heroku.com/articles/django-app-configuration">Configuring Django apps for Heroku</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/getting-started-with-python#introduction">Getting Started on Heroku with Django</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/django-assets">Django and Static Assets</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/python-concurrency-and-database-connections">Concurrency and Database Connections in Django</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/how-heroku-works">How Heroku works</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/dynos">Dynos and the Dyno Manager</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/config-vars">Configuration and Config Vars</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/limits">Limits</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/python-gunicorn">Deploying Python applications with Gunicorn</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/articles/deploying-python">Deploying Python and Django apps on Heroku</a> (Heroku 文档)</li>
+ <li><a href="https://devcenter.heroku.com/search?q=django">Other Heroku Django docs</a></li>
+ </ul>
+ </li>
+ <li>Digital Ocean
+ <ul>
+ <li><a href="https://www.digitalocean.com/community/tutorials/how-to-serve-django-applications-with-uwsgi-and-nginx-on-ubuntu-16-04">How To Serve Django Applications with uWSGI and Nginx on Ubuntu 16.04</a></li>
+ <li><a href="https://www.digitalocean.com/community/tutorials?q=django">Other Digital Ocean Django community docs</a></li>
+ </ul>
+ </li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Testing", "Learn/Server-side/Django/web_application_security", "Learn/Server-side/Django")}}</p>
+
+
+
+<h2 id="本教程文章">本教程文章</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/django/django_assessment_blog/index.html b/files/zh-cn/learn/server-side/django/django_assessment_blog/index.html
new file mode 100644
index 0000000000..a3ca28e160
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/django_assessment_blog/index.html
@@ -0,0 +1,313 @@
+---
+title: 评估:DIY Django 微博客
+slug: learn/Server-side/Django/django_assessment_blog
+translation_of: Learn/Server-side/Django/django_assessment_blog
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenu("Learn/Server-side/Django/web_application_security", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">在这个评估中,您将使用您在 <a href="/en-US/docs/Learn/Server-side/Django">Django Web Framework (Python)</a> 模块中获得的知识,来创建一个非常基本的博客。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>在尝试此评估之前,您应该已经完成​​了本模块中的所有文章。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>测试对Django基础知识的理解,包括URL配置,模型,视图,表单和模板。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="項目概要">項目概要</h2>
+
+<p>下面列出了需要显示的页面、URL和其它要求:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">页面</th>
+ <th scope="col">URL</th>
+ <th scope="col">要求</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>主页</td>
+ <td><code>/</code> and <code>/blog/</code></td>
+ <td>描述站点的索引页面。</td>
+ </tr>
+ <tr>
+ <td>所有博客文章列表</td>
+ <td><code>/blog/blogs/</code></td>
+ <td>
+ <p>所有博客文章列表:</p>
+
+ <ul>
+ <li>可通过侧边栏链接访问所有用户。</li>
+ <li>列表的排序按发布日期(从最新到最旧)。</li>
+ <li>列表分页为每 5 篇文章 1 个分页。</li>
+ <li>列表项显示博客标题,发布日期和作者。</li>
+ <li>博客帖子名称,链接到博客详细信息页面。</li>
+ <li>博主(作者姓名)链接到博客作者详细信息页面。</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>博客作者(博主)详细信息页面</td>
+ <td><code>/blog/blogger/<em>&lt;author-id&gt;</em></code></td>
+ <td>
+ <p>指定作者的信息(按ID)和他们的博客文章列表:</p>
+
+ <ul>
+ <li>可以从博客文章等的作者链接访问所有用户</li>
+ <li>包含有关博主/作者的一些简要经历信息。</li>
+ <li>按发布日期排序的列表(从最新到最旧)。</li>
+ <li>没有分页。</li>
+ <li>列表项仅显示博客帖子名称和发布日期。</li>
+ <li>博客帖子名称,链接到博客详细信息页面。</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>博客帖子详细信息页面</td>
+ <td><code>/blog/<em>&lt;blog-id&gt;</em></code></td>
+ <td>
+ <p>博客帖子详情。</p>
+
+ <ul>
+ <li>可从博客帖子列表访问所有用户。</li>
+ <li>页面包含博客文章:姓名,作者,发布日期和内容。</li>
+ <li>博客文章的评论应显示在底部。</li>
+ <li>评论应按顺序排序:从最旧到最近。</li>
+ <li>包含为登录用户添加注释的链接(请参阅注释表单页面)</li>
+ <li>博客帖子和评论,只需显示纯文本。不需要支持任何类型的HTML标记(例如链接,图像,粗体/斜体等)。</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>所有博主的名单</td>
+ <td><code>/blog/bloggers/</code></td>
+ <td>
+ <p>系统上的博主列表:</p>
+
+ <ul>
+ <li>可从站点侧栏访问所有用户</li>
+ <li>博主名称链接到博客作者详细信息页面。</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>评论表单页面</td>
+ <td><code>/blog/<em>&lt;blog-id&gt;</em>/create</code></td>
+ <td>
+ <p>为博客帖子创建评论:</p>
+
+ <ul>
+ <li>只能从博客帖子详细信息页面底部的链接,访问登录用户(仅限)。</li>
+ <li>显示表单以及用于输入注释的描述(发布日期和博客不可编辑)。</li>
+ <li>发布评论后,该页面将重定向回相关的博客帖子页面。</li>
+ <li>用户无法编辑或删除其帖子。</li>
+ <li>注销用户将被引导至登录页面进行登录,然后才能添加评论。登录后,他们将被重定向,回到他们想要评论的博客页面。</li>
+ <li>评论页面应包含被评论的博客帖子的名称/链接。</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>用户认证页面</td>
+ <td><code>/accounts/<em>&lt;standard urls&gt;</em></code></td>
+ <td>
+ <p>用于登录,注销和设置密码的标准Django身份验证页面:</p>
+
+ <p> </p>
+
+ <ul>
+ <li>应该可以通过侧边栏链接,访问登录/退出页面。</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>管理站点</td>
+ <td><code>/admin/<em>&lt;standard urls&gt;</em></code></td>
+ <td>
+ <p>应启用管理站点,以允许创建/编辑/删除博客帖子、博客作者、和博客评论(这是博客创建新博客帖子的机制):</p>
+
+ <p> </p>
+
+ <ul>
+ <li>管理站点的博客帖子记录,应显示内联的相关评论列表(在每篇博客文章下方)。</li>
+ <li>管理站点中的注释名称,是通过将注释说明,截断为75个字符来创建的。</li>
+ <li>其他类型的记录,可以使用基本注册。</li>
+ </ul>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>此外,您应该编写一些基本测试来验证:</p>
+
+<ul>
+ <li>所有模型字段都具有正确的标签和长度。</li>
+ <li>所有模型都具有预期的对象名称(例如<code> __str__()</code> 返回预期值)。</li>
+ <li>模型具有单个博客和评论记录的预期URL(例如,<code>get_absolute_url()</code>返回预期的URL)。</li>
+ <li>BlogListView(所有博客页面)可在预期位置访问(例如 /blog/blogs)</li>
+ <li>BlogListView(所有博客页面)可通过预期的命名网址访问(例如 'blogs')</li>
+ <li>BlogListView(所有博客页面)使用预期的模板(例如默认模板)</li>
+ <li>BlogListView 以 5 个记录为 1 个分页(至少在第一页上)</li>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>: 当然还有许多其他测试可以运行。请谨慎使用,但我们希望您至少进行上述测试。</p>
+</div>
+
+<p>以下部分,显示了实现上述要求的站点的屏幕截图。</p>
+
+<h2 id="截图">截图</h2>
+
+<p>以下屏幕截图,提供了完成的程序应输出的示例。</p>
+
+<h3 id="所有博客文章列表">所有博客文章列表</h3>
+
+<p>这将显示所有博客帖子的列表(可从侧栏中的“所有博客” All blogs 链接访问)。注意事项:</p>
+
+<ul>
+ <li>侧栏还列出了登录用户。</li>
+ <li>个人博客帖子和博主可以作为页面中的链接访问。</li>
+ <li>启用分页(以 5 个为一组)</li>
+ <li>次序是从最新到最旧。</li>
+</ul>
+
+<p><img alt="List of all blogs" src="https://mdn.mozillademos.org/files/14319/diyblog_allblogs.png" style="border-style: solid; border-width: 1px; display: block; height: 363px; margin: 0px auto; width: 986px;"></p>
+
+<h3 id="所有博主的列表">所有博主的列表</h3>
+
+<p>这提供了到所有博客的链接,如同来自侧栏中的“所有博客” All bloggers 链接。在这种情况下,我们可以从侧边栏看到,并没有用户登录。</p>
+
+<p><img alt="List of all bloggers" src="https://mdn.mozillademos.org/files/14321/diyblog_blog_allbloggers.png" style="border-style: solid; border-width: 1px; display: block; height: 256px; margin: 0px auto; width: 493px;"></p>
+
+<h3 id="博客详情页面">博客详情页面</h3>
+
+<p>这显示了指定博客的详细信息页面。</p>
+
+<p><img alt="Blog detail with add comment link" src="https://mdn.mozillademos.org/files/14323/diyblog_blog_detail_add_comment.png" style="border-style: solid; border-width: 1px; display: block; height: 640px; margin: 0px auto; width: 986px;"></p>
+
+<p>请注意,注释具有日期和时间,并且从最旧到最新排序(与博客次序相反)。最后,我们有一个链接,用于访问表单,以添加新评论。如果用户未登录,我们会看到登录的建议。</p>
+
+<p><img alt="Comment link when not logged in" src="https://mdn.mozillademos.org/files/14325/diyblog_blog_detail_not_logged_in.png" style="border-style: solid; border-width: 1px; display: block; height: 129px; margin: 0px auto; width: 646px;"></p>
+
+<h3 id="添加评论表单">添加评论表单</h3>
+
+<p>这是添加评论的表单。请注意,我们已登录。如果成功,我们应该返回相关的博客帖子页面。</p>
+
+<p><img alt="Add comment form" src="https://mdn.mozillademos.org/files/14329/diyblog_comment_form.png" style="border-style: solid; border-width: 1px; display: block; height: 385px; margin: 0px auto; width: 778px;"></p>
+
+<h3 id="作者简介">作者简介</h3>
+
+<p>这会显示博主的个人信息及其博客帖子列表。</p>
+
+<p><img alt="Blogger detail page" src="https://mdn.mozillademos.org/files/14327/diyblog_blogger_detail.png" style="border-style: solid; border-width: 1px; display: block; height: 379px; margin: 0px auto; width: 982px;"></p>
+
+<h2 id="完成的步骤">完成的步骤</h2>
+
+<p>以下部分,描述了您需要执行的操作。</p>
+
+<ol>
+ <li>为站点创建骨架项目和Web应用程序(如<a href="/zh-CN/docs/Learn/Server-side/Django/skeleton_website">Django教程 2:创建骨架网站</a>中所述)。您可以使用 'diyblog' 作为项目名称,使用 'blog' 作为应用程序名称。</li>
+ <li>为博客帖子,评论和所需的任何其他对象创建模型。在考虑您的设计时,请记住:
+ <ul>
+ <li>每个评论只有一个博客,但博客可能有很多评论。</li>
+ <li>博客帖子和评论,必须按发布日期排序。</li>
+ <li>并非每个用户都必须是博客作者,尽管任何用户都可能是评论者。</li>
+ <li>博客作者还必须包含个人信息。</li>
+ </ul>
+ </li>
+ <li>为新模型运行迁移,并创建超级用户。</li>
+ <li>使用管理站点,创建一些示例博客帖子,和博客评论。</li>
+ <li>为博客帖子、和博客列表页面,创建视图、模板、和 URL 配置。</li>
+ <li>为博客帖子、和博客详细信息页面,创建视图、模板、和 URL 配置。</li>
+ <li>创建一个页面,其中包含用于添加新评论的表单(请记住,这仅适用于已登录的用户!)</li>
+</ol>
+
+<h2 id="提示和技巧">提示和技巧</h2>
+
+<p>该项目与 <a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> 教程非常相似。您将能够使用几乎所有相同的方法,包含设置框架,用户登录/注销行为,对静态文件,视图,URL,表单,基本模板和管理站点配置的支持。</p>
+
+<p>一些一般提示:</p>
+
+<ol>
+ <li>索引页面可以实现为基本功能视图和模板(就像locallibrary一样)。</li>
+ <li>T可以使用<a href="/zh-CN/docs/Learn/Server-side/Django/Generic_views">通用列表和详细信息视图</a>,以创建博客帖子和博主的列表视图,以及博客帖子的详细信息视图。</li>
+ <li>可以使用通用列表的博客列表视图,并对指定作者匹配的博客对象进行过滤,来创建特定作者的博客帖子列表。
+ <ul>
+ <li>您将必须实现<code>get_queryset(self)</code>来进行过滤(很像我们的图书馆类<code>LoanedBooksAllListView</code>),并从URL获取作者信息。</li>
+ <li>您还需要将作者的名称,传递给上下文中的页面。要在基于类的视图中执行此操作,您需要实现<code>get_context_data()</code>(在下面讨论)。</li>
+ </ul>
+ </li>
+ <li>可以使用基于函数的视图(以及关联的模型和表单),或使用通用<code>CreateView</code>,以创建添加注释表单。如果您使用<code>CreateView</code>(推荐),那么:
+ <ul>
+ <li>您还需要将博客文章的名称,传递到上下文中的评论页面(实现<code>get_context_data()</code> ,如下所述)。</li>
+ <li>表单应仅显示用户输入的注释“description”(日期和相关的博客文章,不应该是可编辑的)。由于它们本身不在表单中,因此您的代码,需要在<code> form_valid()</code> 函数中,设置注释的作者,以便将其保存到模型中(<a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user">如此处所述</a> -  Django文档)。在同一个功能中,我们设置了相关的博客。可能的实现如下所示(<code>pk</code>是从URL / URL配置传入的博客ID)。
+ <pre class="brush: python"> def form_valid(self, form):
+ """
+ Add author and associated blog to form data before setting it as valid (so it is saved to model)
+ """
+ #Add logged-in user as author of comment
+ form.instance.author = self.request.user
+ #Associate comment with blog based on passed id
+ form.instance.blog=get_object_or_404(Blog, pk = self.kwargs['pk'])
+ # Call super-class form validation behaviour
+ return super(BlogCommentCreate, self).form_valid(form)
+</pre>
+ </li>
+ <li>在表单验证后,您需要提供成功的 URL,以进行重新定向;这应该是原来的博客。为此,您需要覆盖 <code>get_success_url()</code>,并为原来的博客 “反转” URL 。您可以使用<code>self.kwargs</code>属性,获取所需的博客ID,如上面的 <code>form_valid()</code> 方法所示。</li>
+ </ul>
+ </li>
+</ol>
+
+<p>我们简要地讨论了在<a href="/zh-CN/docs/Learn/Server-side/Django/Generic_views#Overriding_methods_in_class-based_views">Django教程 6:通用列表和详细信息视图</a>主题中,在基于类的视图中,将上下文传递给模板。要执行此操作,您需要覆盖<code>get_context_data()</code>(首先,获取现有上下文,使用要传递给模板的任何其他变量,更新它,然后返回更新的上下文)。例如,下面的代码片段,显示了如何根据<code>BlogAuthor</code> id,将 blogger 对象添加到上下文中。</p>
+
+<pre class="brush: python">class SomeView(generic.ListView):
+ ...
+
+ def get_context_data(self, **kwargs):
+        # Call the base implementation first to get a context
+        context = super(SomeView, self).get_context_data(**kwargs)
+        # Get the blogger object from the "pk" URL parameter and add it to the context
+        context['blogger'] = get_object_or_404(BlogAuthor, pk = self.kwargs['pk'])
+        return context
+</pre>
+
+<h2 id="评估">评估</h2>
+
+<p>这个任务的评估,可以在<a href="https://github.com/mdn/django-diy-blog/blob/master/MarkingGuide.md">Github</a>上找到。此评估主要基于您的应用程序,满足上面列出要求的程度,尽管评估的某些部分,会检查您的代码是否使用了适当的模型,并且您至少编写了一些测试代码。完成后,您可以查看我们<a href="https://github.com/mdn/django-diy-blog">完成的示例</a>,该示例项目的表现是 “满分”。</p>
+
+<p>完成本单元后,表示您还完成了所有 MDN 用于学习《基本 Django 服务器端网站编程》的内容!我们希望您喜欢这个模块,并感觉您已经掌握了基础知识!</p>
+
+<p>{{PreviousMenu("Learn/Server-side/Django/web_application_security", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="本系列教程">本系列教程</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django 介绍</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">架设 Django 开发环境</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django 教程: 本地图书馆网站</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django 教程 2: 创建骨架网站</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django 教程 3: 使用模型</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django 教程 4: Django 管理站点</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django 教程 5: 创建主页</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django 教程 6: 通用列表与详细视图</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django 教程 7: 会话框架</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django 教程 8: 用户认证与授权</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django 教程 9: 使用表单</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django 教程 10: 测试 Django 网页应用</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django 教程 11: 部署 Django 到生产环境</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django 网页应用安全</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django 微博客</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/django/forms/index.html b/files/zh-cn/learn/server-side/django/forms/index.html
new file mode 100644
index 0000000000..ab99a520a3
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/forms/index.html
@@ -0,0 +1,671 @@
+---
+title: 'Django 教程 9: 使用表单'
+slug: learn/Server-side/Django/Forms
+translation_of: Learn/Server-side/Django/Forms
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">在<font><font>本教程中,我们将向您展示如何在Django中使用HTML表单,特别是编写表单以创建,更新和删除模型实例的最简单方法。</font><font>作为本演示的一部分,我们将扩展</font></font><a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website" style='color: rgb(63, 135, 166); margin: 0px; padding: 0px; border: 0px; text-decoration: none; font-family: "Open Sans", arial, x-locale-body, sans-serif; font-size: 20px; font-style: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 255, 255);'><font><font>LocalLibrary</font></font></a><font><font>网站,以便图书馆员可以使用我们自己的表单(而不是使用管理员应用程序)更新图书,创建,更新和删除作者</font></font>。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前置条件:</th>
+ <td>完成所有先前的教程主题,包含 <a href="/zh-CN/docs/Learn/Server-side/Django/authentication_and_sessions">Django 教程 8: 使用者授权与许可</a>。</td>
+ </tr>
+ <tr>
+ <th scope="row">目標:</th>
+ <td>了解如何撰写表单,向使用者取得资料,并更新资料库。了解通用类别表单编辑视图,如何大量地简化用于单一模型的新表单制作。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概览">概览</h2>
+
+<p>一张 <a href="/zh-CN/docs/Web/Guide/HTML/Forms">HTML 表单</a> ,是由一个或多个栏位/widget在一个网页上组成的,以用于向使用者收集资料,并提交至伺服器。表单是一个弹性的机制,用于收集使用者输入,有合适的 widgets 可输入许多不同型态的资料,包含文字框、复选框、单选按钮、日期选取组件等等。若是允许我们用 <code>POST</code> 方式传送资料,并附加 CSRF 跨站要求伪造保护,表单也是与伺服器分享资料的一种相对安全的方式。</p>
+
+<p>在这个教程目前为止,我们还没有创造任何表单,但我们已经在 Django 管理站点遇到这些表单了— 例如以下的撷图展示了一张表单,用于编辑我们的一个 <a href="/zh-CN/docs/Learn/Server-side/Django/Models">Book书本</a>模型,包含一些选择列表以及文字编辑框。</p>
+
+<p><img alt="Admin Site - Book Add" src="https://mdn.mozillademos.org/files/13979/admin_book_add.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<p>表单的使用可以很复杂!开发者需要为表单撰写 HTML 语法,在服务端验证输入的资料并经过充分的安全处理(并且可能在浏览器端也需要),回到表单呈现错误信息,告知使用者任何无效的栏位,当成功提交时处理资料,在最后用某些方式回应使用者表单提交成功的信息。经由提供一个框架,让你程序化定义表单以及其中的栏位,Django 表单接手处理了以上这些步骤的大量工作,比如使用这些物件,产生表单的 HTML 源码,并处理大量的验证、使用者互动的工作。</p>
+
+<p>在本教程中,我们将展示一些方法,用以创造并使用表单,特别是,当你创造用以操作资料模型的表单,通用编辑表单视图如何显著降低你的工作量。在此过程中,我们将通过添加表单,来扩展我们的 LocalLibrary 应用程序,以允许图书馆员更新图书馆书本,我们将创建页面来创建,编辑和删除书本和作者(复制上面显示的表格的基本版本,以便编辑书本)。</p>
+
+<h2 id="HTML_表单">HTML 表单</h2>
+
+<p>首先简要概述<a href="/zh-CN/docs/Learn/HTML/Forms">HTML表单</a>。考虑一个简单的HTML表单,其中包含一个文本字段,用于输入某些“团队”的名称及其相关标签:</p>
+
+<p><img alt="Simple name field example in HTML form" src="https://mdn.mozillademos.org/files/14117/form_example_name_field.png" style="border-style: solid; border-width: 1px; display: block; height: 44px; margin: 0px auto; width: 399px;"></p>
+
+<p>表单在HTML中定义为<code>&lt;form&gt;...&lt;/form&gt;</code> 标记内的元素集合,包含至少一个<code>type="submit"</code>的<code>input</code> 输入元素。</p>
+
+<pre class="brush: html">&lt;form action="/team_name_url/" method="post"&gt;
+    &lt;label for="team_name"&gt;Enter name: &lt;/label&gt;
+    &lt;input id="team_name" type="text" name="name_field" value="Default name for team."&gt;
+    &lt;input type="submit" value="OK"&gt;
+&lt;/form&gt;</pre>
+
+<p>虽然在这里,我们只有一个文本字段,用于输入团队名称,但表单可能包含任意数量的其他输入元素,及其相关标签。字段的<code>type</code> 属性,定义将显示哪种窗口小部件。该字段的名称<code>name</code> 和 <code>id</code>,用于标识 JavaScript / CSS / HTML中的字段,而<code>value</code> 定义字段首次显示时的初始值。匹配团队标签使用<code style="font-style: normal; font-weight: normal;">label</code> 标签指定(请参阅上面的“输入名称” Enter name),其中<code style="font-style: normal; font-weight: normal;">for</code> 字段包含相关<code style="font-style: normal; font-weight: normal;">input</code>输入的<code style="font-style: normal; font-weight: normal;">id</code> 值。</p>
+
+<p>提交输入<code>submit</code> 将显示为一个按钮(默认情况下),用户可以按下该按钮,将表单中所有其他输入元素中的数据,上传到服务器(在本例中,只有<code>team_name</code>)。表单属性定义用于发送数据的 HTTP <code>method</code> 方法,和服务器上数据的目标(<code>action</code>):       </p>
+
+<ul>
+ <li><code>action</code>: 提交表单时,要发送数据以进行处理的资源 /URL。如果未设置(或设置为空字符串),则表单将提交回当前页面 URL。</li>
+ <li><code>method</code>: 用于发送数据的HTTP方法:post 或 get。
+ <ul>
+ <li>如果数据将导致服务器数据库的更改,则应始终使用<code>POST</code> 方法,因为这可以更加抵抗跨站点伪造请求攻击。</li>
+ <li><code>GET</code> 方法,只应用于不更改用户数据的表单(例如搜索表单)。当您希望能够为URL添加书签、或共享时,建议使用此选项。</li>
+ </ul>
+ </li>
+</ul>
+
+<p>服务器的角色,首先是呈现初始表单状态 - 包含空白字段或预先填充初始值。在用户按下提交按钮之后,服务器将从Web浏览器,接收具有值的表单数据,并且必须验证该信息。如果表单包含无效数据,则服务器应再次显示表单,这次使用用户输入的数据在“有效”字段中,并使用消息来描述无效字段的问题。一旦服务器获得具有所有有效表单数据的请求,它就可以执行适当的操作(例如,保存数据,返回搜索结果,上载文件等),然后通知用户。</p>
+
+<p>可以想象,创建HTML,验证返回的数据,根据需要重新显示输入的数据,和错误报告,以及对有效数据执行所需的操作,都需要花费很多精力才能“正确”。通过删除一些繁重的重复代码,Django 使这变得更容易!</p>
+
+<h2 id="Django_表单处理流程">Django 表单处理流程</h2>
+
+<p>Django 的表单处理,使用了我们在之前的教程中,学到的所有相同技术(用于显示有关模型的信息):视图获取请求,执行所需的任何操作,包括从模型中读取数据,然后生成并返回HTML页面(从模板中,我们传递一个包含要显示的数据的上下文。使事情变得更复杂的是,服务器还需要能够处理用户提供的数据,并在出现任何错误时,重新显示页面。</p>
+
+<p>下面显示了 Django 如何处理表单请求的流程图,从对包含表单的页面的请求开始(以绿色显示)。</p>
+
+<p><img alt="Updated form handling process doc." src="https://mdn.mozillademos.org/files/14205/Form%20Handling%20-%20Standard.png" style="display: block; height: 569px; margin: 0px auto; width: 800px;"></p>
+
+<p>基于上图,Django 表单处理的主要内容是:</p>
+
+<p> </p>
+
+<ol>
+ <li>在用户第一次请求时,显示默认表单。
+ <ul>
+ <li>表单可能包含空白字段(例如,如果您正在创建新记录),或者可能预先填充了初始值(例如,如果您要更改记录,或者具有有用的默认初始值)。</li>
+ <li>此时表单被称为未绑定,因为它与任何用户输入的数据无关(尽管它可能具有初始值)。</li>
+ </ul>
+ </li>
+ <li>从提交请求接收数据,并将其绑定到表单。
+ <ul>
+ <li>将数据绑定到表单,意味着当我们需要重新显示表单时,用户输入的数据和任何错误都可取用。 </li>
+ </ul>
+ </li>
+ <li>清理并验证数据。
+ <ul>
+ <li>清理数据会对输入执行清理(例如,删除可能用于向服务器发送恶意内容的无效字符)并将其转换为一致的 Python 类型。</li>
+ <li>验证检查值是否适合该字段(例如,在正确的日期范围内,不是太短或太长等)</li>
+ </ul>
+ </li>
+ <li>如果任何数据无效,请重新显示表单,这次使用任何用户填充的值,和问题字段的错误消息。</li>
+ <li>如果所有数据都有效,请执行必要的操作(例如保存数据,发送表单和发送电子邮件,返回搜索结果,上传文件等)</li>
+ <li>完成所有操作后,将用户重定向到另一个页面。</li>
+</ol>
+
+<p>Django 提供了许多工具和方法,来帮助您完成上述任务。最基本的是 <code>Form</code> 类,它简化了表单 HTML 和数据清理/验证的生成。在下一节中,我们将描述表单如何使用页面的实际示例,来允许图书馆员更新书本籍。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 在我们讨论 Django 更“高级”的表单框架类时,了解 <code>Form</code> 的使用方式,将对您有所帮助。</p>
+</div>
+
+<h2 id="续借表单_-_使用表单和功能视图">续借表单 - 使用表单和功能视图</h2>
+
+<p>接下来,我们将添加一个页面,以允许图书馆员,为被借用的书本办理续借。为此,我们将创建一个允许用户输入日期值的表单。我们将从当前日期(正常借用期)起 3 周内,为该字段设定初始值,并添加一些验证,以确保图书管理员无法输入过去的日期、或未来的日期。输入有效日期后,我们会将其写入当前记录的 <code>BookInstance.due_back </code>字段。</p>
+
+<p>该示例将使用基于函数的视图和<code>Form</code> 类。以下部分,说明了表单的工作方式,以及您需要对正在进行的 LocalLibrary 项目所做的更改。</p>
+
+<h3 id="表单">表单</h3>
+
+<p><code>Form</code> 类是 Django 表单处理系统的核心。它指定表单中的字段、其布局、显示窗口小部件、标签、初始值、有效值,以及(一旦验证)与无效字段关联的错误消息。该类还提供了使用预定义格式(表,列表等)在模板中呈现自身的方法,或者用于获取任何元素的值(启用细粒度手动呈现)的方法。</p>
+
+<h4 id="声明表单">声明表单</h4>
+
+<p><code>Form</code> 的声明语法,与声明<code>Model</code>非常相似,并且共享相同的字段类型(以及一些类似的参数)。这是有道理的,因为在这两种情况下,我们都需要确保每个字段处理正确类型的数据,受限于有效数据,并具有显示/文档的描述。</p>
+
+<p>要创建表单,我们导入表单库,从<code>Form</code> 类派生,并声明表单的字段。我们的图书馆图书续借表单的一个非常基本的表单类如下所示:</p>
+
+<pre class="brush: python">from django import forms
+
+class RenewBookForm(forms.Form):
+    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
+</pre>
+
+<h4 id="表单字段">表单字段</h4>
+
+<p>在这种情况下,我们有一个 <code><a href="https://docs.djangoproject.com/zh-hans/2.0/ref/forms/fields//#datefield">DateField</a></code> 用于输入续借日期,该日期将使用空白值在 HTML 中呈现,默认标签为“续借日期:”,以及一些有用的用法文本:“输入从现在到 4 周之间的日期(默认为 3)周)。”  由于没有指定其他可选参数,该字段将使用 <a href="https://docs.djangoproject.com/zh-hans/2.0/ref/forms/fields/#django.forms.DateField.input_formats">input_formats </a>接受日期:YYYY-MM-DD(2016-11-06)、MM/DD/YYYY(02/26/2016)、MM/DD/YY( 10/25/16),并且将使用默认<a href="https://docs.djangoproject.com/zh-hans/2.0/ref/forms/fields/#widget">小部件</a>呈现:<a href="https://docs.djangoproject.com/zh-hans/2.0/ref/forms/widgets/#django.forms.DateInput">DateInput</a>。</p>
+
+<p>还有许多其他类型的表单字段,您可以从它们与等效模型字段类的相似性中大致认识到:</p>
+
+<p><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#booleanfield"><code>BooleanField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#charfield"><code>CharField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#choicefield"><code>ChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#typedchoicefield"><code>TypedChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#datefield"><code>DateField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#datetimefield"><code>DateTimeField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#decimalfield"><code>DecimalField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#durationfield"><code>DurationField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#emailfield"><code>EmailField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#filefield"><code>FileField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#filepathfield"><code>FilePathField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#floatfield"><code>FloatField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#imagefield"><code>ImageField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#integerfield"><code>IntegerField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#genericipaddressfield"><code>GenericIPAddressField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#multiplechoicefield"><code>MultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#typedmultiplechoicefield"><code>TypedMultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#nullbooleanfield"><code>NullBooleanField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#regexfield"><code>RegexField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#slugfield"><code>SlugField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#timefield"><code>TimeField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#urlfield"><code>URLField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#uuidfield"><code>UUIDField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#combofield"><code>ComboField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#multivaluefield"><code>MultiValueField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#splitdatetimefield"><code>SplitDateTimeField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#modelmultiplechoicefield"><code>ModelMultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#modelchoicefield"><code>ModelChoiceField</code></a>​​​​.</p>
+
+<p>下面列出了大多数字段共有的参数(这些参数具有合理的默认值):</p>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#required">required</a>: 如果为<code>True</code>,则该字段不能留空或给出<code>None</code>值。默认情况下需要字段,因此您可以设置<code>required=False</code>以允许表单中的空白值。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#label">label</a>: 在 HTML 中呈现字段时使用的标签。如果未指定<a href="https://docs.djangoproject.com/zh-hans/2.0/ref/forms/fields/#label">label</a>,则 Django 将通过大写第一个字母、并用空格替换下划线(例如续订日期)的方式,从字段名称创建一个。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#label-suffix">label_suffix</a>: 默认情况下,标签后面会显示冒号(例如续借日期:)。此参数允许您指定包含其他字符的不同后缀。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#initial">initial</a>: 显示表单时,字段的初始值。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#widget">widget</a>: 要使用的显示小部件。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#help-text">help_text</a> (如上例所示):可以在表单中显示的附加文本,用于说明如何使用该字段。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#error-messages">error_messages</a>: 字段的错误消息列表。如果需要,您可以使用自己的消息,覆盖这些消息。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#validators">validators</a>: 验证时将在字段上调用的函数列表。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#localize">localize</a>: 启用表单数据输入的本地化(有关详细信息,请参阅链接)。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/#disabled">disabled</a>: 如果为<code>True</code>,该字段会被显示,但无法编辑其值。默认值为<code>False</code>。</li>
+</ul>
+
+<h4 id="验证">验证</h4>
+
+<p>Django 提供了许多可以验证数据的地方。验证单个字段的最简单方法,是覆盖要检查的字段的方法<code>clean_<strong>&lt;fieldname&gt;</strong>()</code> 。因此,例如,我们可以通过实现<code>clean_<strong>renewal_date</strong>()  </code>,验证输入的<code>renewal_date</code> 值是从现在到 4 周之间,如下所示。</p>
+
+<pre class="brush: python">from django import forms
+
+<strong>from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext_lazy as _
+import datetime #for checking renewal date range.
+</strong>
+class RenewBookForm(forms.Form):
+    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
+
+<strong>    def clean_renewal_date(self):
+        data = self.cleaned_data['renewal_date']
+
+        #Check date is not in past.
+        if data &lt; datetime.date.today():
+            raise ValidationError(_('Invalid date - renewal in past'))
+
+        #Check date is in range librarian allowed to change (+4 weeks).
+        if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
+            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
+
+        # Remember to always return the cleaned data.
+        return data</strong></pre>
+
+<p>有两件重要的事情需要注意。首先,我们使用<code>self.cleaned_data['renewal_date']</code> 获取数据,并且无论是否在函数末尾更改数据,我们都会返回此数据。此步骤使用默认验证器,将数据“清理”、并清除可能不安全的输入,并转换为数据的正确标准类型(在本例中为Python <code>datetime.datetime</code>对象)。</p>
+
+<p>第二点是,如果某个值超出了我们的范围,我们会引发<code>ValidationError</code>,指定在输入无效值时,我们要在表单中显示的错误文本。上面的例子,也将这个文本包含在 <a href="https://docs.djangoproject.com/zh-hans/2.0/topics/i18n/translation/">Django 的翻译函数</a><code>ugettext_lazy()</code>中(导入为 <code>_()</code>),如果你想在稍后翻译你的网站,这是一个很好的做法。</p>
+
+<div class="note">
+<p><strong>注意:</strong>  在<a href="https://docs.djangoproject.com/zh-hans/2.0/ref/forms/validation/">表单和字段验证</a>(Django docs)中验证表单还有其他很多方法和示例。例如,如果您有多个相互依赖的字段,则可以覆盖<a href="https://docs.djangoproject.com/en/2.0/ref/forms/api/#django.forms.Form.clean">Form.clean()</a> 函数并再次引发<code>ValidationError</code>。</p>
+</div>
+
+<p>这就是我们在这个例子中,对表单所需要了解的全部内容!</p>
+
+<h4 id="复制表单">复制表单</h4>
+
+<p>创建并打开文件 <strong>locallibrary/catalog/forms.py</strong>,并将前一个块中的整个代码清单,复制到其中。</p>
+
+<h3 id="URL_配置">URL 配置</h3>
+
+<p>在创建视图之前,让我们为续借页面添加 URL 配置。将以下配置,复制到<strong>locallibrary/catalog/urls.py </strong>的底部。</p>
+
+<pre class="brush: python">urlpatterns += [
+ path('book/&lt;uuid:pk&gt;/renew/', views.renew_book_librarian, name='renew-book-librarian'),
+]</pre>
+
+<p>URL 配置会将格式为 <strong>/catalog/book/<em>&lt;bookinstance id&gt;</em>/renew/</strong>的URL,重定向到 <strong>views.py </strong>中,名为<code>renew_book_librarian()</code> 的函数,并将<code>BookInstance</code> id作为名为 <code>pk</code>的参数发送。只有 <code>pk</code>是正确格式化的 <code>uuid</code>,该模式才会匹配。</p>
+
+<div class="note">
+<p><strong>注意</strong>:  我们可以将捕获的 URL 数据,命名为“<code>pk</code>”,因为我们可以完全控制视图函数(我们不使用需要具有特定名称的参数的通用详细视图类)。然而,<code>pk</code>,“主键” primary key 的缩写,是一个合理的惯例!</p>
+</div>
+
+<h3 id="视图">视图</h3>
+
+<p>正如上面的 Django 表单处理过程中,所讨论的那样,视图必须在首次调用时呈现默认表单,然后在数据无效时,重新呈现它,并显示错误消息,或者数据有效时,处理数据,并重定向到新页面。为了执行这些不同的操作,视图必须能够知道,它是第一次被调用以呈现默认表单,还是后续处理以验证数据。</p>
+
+<p>对于使用<code>POST</code> 请求向服务器提交信息的表单,最常见的模式,是视图针对<code>POST</code> 请求类型进行测试(<code>if request.method == 'POST':</code>)以识别表单验证请求和<code>GET</code> (使用一个<code>else</code> 条件)来识别初始表单创建请求。如果要使用<code>GET</code> 请求提交数据,则识别这是第一个、还是后续视图调用的典型方法,是读取表单数据(例如,读取表单中的隐藏值)。</p>
+
+<p>书本续借过程将写入我们的数据库,因此按照惯例,我们使用 <code>POST</code> 请求方法。下面的代码片段,显示了这种函数视图的(非常标准)模式。</p>
+
+<pre class="brush: python">from django.shortcuts import get_object_or_404
+from django.http import HttpResponseRedirect
+from django.urls import reverse
+import datetime
+
+from .forms import RenewBookForm
+
+def renew_book_librarian(request, pk):
+    book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a POST request then process the Form data
+<strong>    if request.method == 'POST':</strong>
+
+        # Create a form instance and populate it with data from the request (binding):
+        form = RenewBookForm(request.POST)
+
+        # Check if the form is valid:
+        <strong>if form.is_valid():</strong>
+            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+            book_inst.due_back = form.cleaned_data['renewal_date']
+            book_inst.save()
+
+            # redirect to a new URL:
+            return HttpResponseRedirect(reverse('all-borrowed') )
+
+    # If this is a GET (or any other method) create the default form.
+<strong>    else:</strong>
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
+        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<p>首先,我们导入我们的表单(<code>RenewBookForm</code>)和视图函数中使用的许多其他有用的对象/方法:</p>
+
+<ul>
+ <li><code><a href="https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#get-object-or-404">get_object_or_404()</a></code>:  根据模型的主键值,从模型返回指定的对象,如果记录不存在,则引发<code>Http404</code> 异常(未找到)。</li>
+ <li><code><a href="https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpResponseRedirect">HttpResponseRedirect</a></code>:  这将创建指向指定URL的重定向(HTTP状态代码 302)。</li>
+ <li><code><a href="https://docs.djangoproject.com/en/2.0/ref/urlresolvers/#django.urls.reverse">reverse()</a></code>:  这将从 URL 配置名称和一组参数生成 URL。它是我们在模板中使用的 <code>url</code> 标记的 Python 等价物。</li>
+ <li><code><a href="https://docs.python.org/3/library/datetime.html">datetime</a></code>:  用于操作日期和时间的 Python 库。</li>
+</ul>
+
+<p>在视图中,我们首先使用 <code>get_object_or_404()</code>中的 <code>pk</code> 参数,来获取当前的 <code>BookInstance</code> (如果这不存在,视图将立即退出,页面将显示“未找到”错误)。如果这不是 <code>POST</code> 请求(由 <code>else</code> 子句处理),那么我们创建默认表单,传递 <code>renewal_date</code> 字段的<code>initial</code> 初始值(如下面的<strong>粗体</strong>所示,这是从当前日期起的 3 周)。</p>
+
+<pre class="brush: python"> book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a GET (or any other method) create the default form
+    <strong>else:</strong>
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(<strong>weeks=3</strong>)
+        <strong>form = RenewBookForm(initial={'</strong>renewal_date<strong>': </strong>proposed_renewal_date<strong>,})</strong>
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<p>创建表单后,我们调用 <code>render()</code> 来创建HTML页面,指定模板和包含表单的上下文。在这种情况下,上下文还包含我们的 <code>BookInstance</code>,我们将在模板中使用它,来提供有关我们正在续借的书本信息。</p>
+
+<p>但是,如果这是一个<code>POST</code> 请求,那么我们创建表单对象,并使用请求中的数据填充它。此过程称为“绑定”,并且允许我们验证表单。然后我们检查表单是否有效,它运行所有字段上的所有验证代码 - 包括用于检查我们的日期字段,实际上是有效日期的通用代码,以及用于检查日期的特定表单的<code>clean_renewal_date()</code>函数在合适的范围内。</p>
+
+<pre class="brush: python">    book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a POST request then process the Form data
+    if request.method == 'POST':
+
+        # Create a form instance and populate it with data from the request (binding):
+<strong>        form = RenewBookForm(request.POST)</strong>
+
+        # Check if the form is valid:
+        if form.is_valid():
+            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+            book_inst.due_back = form.cleaned_data['renewal_date']
+            book_inst.save()
+
+            # redirect to a new URL:
+            return HttpResponseRedirect(reverse('all-borrowed') )
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<p>如果表单无效,我们再次调用<code>render()</code> ,但这次在上下文中传递的表单值将包含错误消息。</p>
+
+<p>如果表单有效,那么我们可以开始使用数据,通过 <code>form.cleaned_data</code>属性访问它(例如 <code>data = form.cleaned_data['renewal_date']</code>)。这里我们只将数据保存到关联的<code>BookInstance</code> 对象的<code>due_back</code> 值中。</p>
+
+<div class="warning">
+<p><strong>重要</strong>:  虽然您也可以通过请求直接访问表单数据(例如<code>request.POST['renewal_date']</code> 或 <code>request.GET['renewal_date']</code>(如果使用 GET 请求),但不建议这样做。清理后的数据是无害的、验证过的、并转换为 Python 友好类型。</p>
+</div>
+
+<p>视图的表单处理部分的最后一步,是重定向到另一个页面,通常是“成功”页面。在这种情况下,我们使用 <code>HttpResponseRedirect</code> 和 <code>reverse()</code> ,重定向到名为'<code>all-borrowed</code>'的视图(这是在 <a href="/zh-CN/docs/learn/Server-side/Django/Authentication#Challenge_yourself">Django 教程第 8 部分中创建的 “挑战”:用户身份验证和权限</a>)。如果您没有创建该页面,请考虑重定向到URL'/'处的主页。</p>
+
+<p>这就是表单处理本身所需的一切,但我们仍然需要将视图,限制为图书馆员可以访问。我们应该在 <code>BookInstance</code> (“<code>can_renew</code>”)中创建一个新的权限,但为了简单起见,我们只需使用<code>@permission_required</code>函数装饰器,和我们现有的 <code>can_mark_returned</code> 权限。</p>
+
+<p>因此,最终视图如下所示。请将其复制到 <strong>locallibrary/catalog/views.py </strong>的底部。</p>
+
+<pre><strong>from django.contrib.auth.decorators import permission_required</strong>
+
+from django.shortcuts import get_object_or_404
+from django.http import HttpResponseRedirect
+from django.urls import reverse
+import datetime
+
+from .forms import RenewBookForm
+
+<strong>@permission_required('catalog.<code>can_mark_returned</code>')</strong>
+def renew_book_librarian(request, pk):
+    """
+    View function for renewing a specific BookInstance by librarian
+    """
+    book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a POST request then process the Form data
+    if request.method == 'POST':
+
+        # Create a form instance and populate it with data from the request (binding):
+        form = RenewBookForm(request.POST)
+
+        # Check if the form is valid:
+        if form.is_valid():
+            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+            book_inst.due_back = form.cleaned_data['renewal_date']
+            book_inst.save()
+
+            # redirect to a new URL:
+            return HttpResponseRedirect(reverse('all-borrowed') )
+
+    # If this is a GET (or any other method) create the default form.
+    else:
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
+        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
+</pre>
+
+<h3 id="模板">模板</h3>
+
+<p>创建视图中引用的模板(<strong>/catalog/templates/catalog/book_renew_librarian.html</strong>),并将下面的代码,复制到其中:</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+{% block content %}
+
+ &lt;h1&gt;Renew: \{{bookinst.book.title}}&lt;/h1&gt;
+ &lt;p&gt;Borrower: \{{bookinst.borrower}}&lt;/p&gt;
+ &lt;p{% if bookinst.is_overdue %} class="text-danger"{% endif %}&gt;Due date: \{{bookinst.due_back}}&lt;/p&gt;
+
+<strong> &lt;form action="" method="post"&gt;
+ {% csrf_token %}
+ &lt;table&gt;
+ \{{ form }}
+  &lt;/table&gt;
+ &lt;input type="submit" value="Submit" /&gt;
+ &lt;/form&gt;</strong>
+
+{% endblock %}</pre>
+
+<p>这里大部分内容,和以前的教程都是完全类似的。我们扩展基本模板,然后重新定义内容块。我们能够引用 <code>\{{bookinst}}</code>(及其变量),因为它被传递到 <code>render()</code>函数中的上下文对象中,我们使用这些来列出书名,借阅者和原始截止日期。</p>
+
+<p>表单代码相对简单。首先,我们声明表单标签,指定表单的提交位置(<code>action</code>)和提交数据的方法(在本例中为 “HTTP POST”) - 如果您回想一下页面顶部的 HTML 表单概述,如图所示的空<code>action</code> ,意味着表单数据将被发布回页面的当前 URL(这是我们想要的!)。在标签内部,我们定义了<code>submit</code> 提交输入,用户可以按这个输入来提交数据。在表单标签内添加的<code>{% csrf_token %}</code> ,是 Django 跨站点伪造保护的一部分。</p>
+
+<div class="note">
+<p><strong>注意:</strong>  将<code>{% csrf_token %}</code> 添加到您创建的每个使用 <code>POST</code> 提交数据的 Django 模板中。这将减少恶意用户劫持表单的可能性。</p>
+</div>
+
+<p>剩下的就是 <code>\{{form}}</code>模板变量,我们将其传递给上下文字典中的模板。也许不出所料,当如图所示使用时,它提供了所有表单字段的默认呈现,包括它们的标签、小部件、和帮助文本 - 呈现如下所示:</p>
+
+<pre class="brush: html">&lt;tr&gt;
+  &lt;th&gt;&lt;label for="id_renewal_date"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt;
+  &lt;td&gt;
+  &lt;input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required /&gt;
+  &lt;br /&gt;
+  &lt;span class="helptext"&gt;Enter date between now and 4 weeks (default 3 weeks).&lt;/span&gt;
+  &lt;/td&gt;
+&lt;/tr&gt;
+</pre>
+
+<div class="note">
+<p><strong>注意:</strong>   它可能并不明显,因为我们只有一个字段,但默认情况下,每个字段都在其自己的表格行中定义(这就是变量在上面的<code>table </code>表格标记内部的原因)。如果您引用模板变量<code>\{{ form.as_table }}</code>,会提供相同的渲染。</p>
+</div>
+
+<p>如果您输入无效日期,您还会获得页面中呈现的错误列表(下面以<strong>粗体</strong>显示)。</p>
+
+<pre class="brush: html">&lt;tr&gt;
+  &lt;th&gt;&lt;label for="id_renewal_date"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt;
+  &lt;td&gt;
+<strong>  &lt;ul class="errorlist"&gt;
+  &lt;li&gt;Invalid date - renewal in past&lt;/li&gt;
+  &lt;/ul&gt;</strong>
+  &lt;input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required /&gt;
+  &lt;br /&gt;
+  &lt;span class="helptext"&gt;Enter date between now and 4 weeks (default 3 weeks).&lt;/span&gt;
+ &lt;/td&gt;
+&lt;/tr&gt;</pre>
+
+<h4 id="使用表单模板变量的其他方法">使用表单模板变量的其他方法</h4>
+
+<p>如上所示使用<code>\{{form}}</code>,每个字段都呈现为表格行。您还可以将每个字段呈现为列表项(使用<code>\{{form.as_ul}}</code> )或作为段落(使用<code>\{{form.as_p}}</code>)。</p>
+
+<p>更酷的是,您可以通过使用点表示法,索引其属性,来完全控制表单每个部分的呈现。例如,我们可以为<code>renewal_date</code> 字段访问许多单独的项目:</p>
+
+<ul>
+ <li><code>\{{form.renewal_date}}:</code> 整个领域。</li>
+ <li><code>\{{form.renewal_date.errors}}</code>: 错误列表。</li>
+ <li><code>\{{form.renewal_date.id_for_label}}</code>: 标签的 id 。</li>
+ <li><code>\{{form.renewal_date.help_text}}</code>: 字段帮助文本。</li>
+ <li> 其他等等!</li>
+</ul>
+
+<p>有关如何在模板中,手动呈现表单,并动态循环模板字段的更多示例,请参阅<a href="https://docs.djangoproject.com/zh-hans/2.0/topics/forms/#rendering-fields-manually">使用表单&gt;手动呈现字段</a>(Django文档)。</p>
+
+<h3 id="测试页面">测试页面</h3>
+
+<p>如果您接受了<a href="https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django/Authentication#Challenge_yourself">Django 教程第 8 部分中的 “挑战”:用户身份验证和权限</a>,您将获得图书馆中借出的所有书本的列表,这只有图书馆工作人员才能看到。我们可以使用下面的模板代码,为每个项目旁边的续借页面,添加链接。</p>
+
+<pre class="brush: html">{% if perms.catalog.can_mark_returned %}- &lt;a href="{% url 'renew-book-librarian' bookinst.id %}"&gt;Renew&lt;/a&gt; {% endif %}</pre>
+
+<div class="note">
+<p><strong>注意</strong>:  请记住,您的测试登录需要具有“<code>catalog.can_mark_returned</code>”权限,才能访问续借书本页面(可能使用您的超级用户帐户)。</p>
+</div>
+
+<p>您也可以手动构建这样的测试URL  -  <a href="http://127.0.0.1:8000/catalog/book/&lt;bookinstance id>/renew/">http://127.0.0.1:8000/catalog/book/<em>&lt;bookinstance_id&gt;</em>/renew/</a> (可以通过导航到图书馆中的书本详细信息页面,获取有效的 bookinstance id,并复制<code>id</code> 字段)。</p>
+
+<h3 id="它看起来是什么样子?">它看起来是什么样子?</h3>
+
+<p>如果您成功,默认表单将如下所示:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14209/forms_example_renew_default.png" style="border-style: solid; border-width: 1px; display: block; height: 292px; margin: 0px auto; width: 680px;"></p>
+
+<p>输入无效值的表单将如下所示:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14211/forms_example_renew_invalid.png" style="border-style: solid; border-width: 1px; display: block; height: 290px; margin: 0px auto; width: 658px;"></p>
+
+<p>所有包含续借链接的图书清单如下所示:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14207/forms_example_renew_allbooks.png" style="border-style: solid; border-width: 1px; display: block; height: 256px; margin: 0px auto; width: 613px;"></p>
+
+<h2 id="模型表单">模型表单</h2>
+
+<p>使用上述方法创建<code>Form</code> 类非常灵活,允许您创建任何类型的表单页面,并将其与任何单一模型、或多个模型相关联。</p>
+
+<p>但是,如果您只需要一个表单,来映射单个模型的字段,那么您的模型,将已经定义了表单中所需的大部分信息:字段、标签、帮助文本等。而不是在表单中重新创建模型定义,使用 <a href="https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/">ModelForm </a>帮助程序类从模型创建表单更容易。然后,可以在视图中使用此<code>ModelForm</code> ,其方式与普通<code>Form</code>完全相同。</p>
+
+<p>包含与原始<code>RenewBookForm</code> 相同的字段的基本 <code>ModelForm</code> 如下所示。创建表单所需要做的,就是添加带有相关模型(<code>BookInstance</code>)的<code>class Meta</code>、和要包含在表单中的模型字段列表(您可以使用 <code>fields = '__all__'</code>,以包含所有字段,或者您可以使用 <code>exclude</code> (而不是字段),指定不包含在模型中的字段)。</p>
+
+<pre class="brush: python">from django.forms import ModelForm
+from .models import BookInstance
+
+class RenewBookModelForm(ModelForm):
+<strong> class Meta:
+ model = BookInstance
+ fields = ['due_back',]</strong>
+</pre>
+
+<div class="note">
+<p><strong>注意</strong>:  这可能看起来不像使用<code>Form</code> 那么简单(在这种情况下不是这样,因为我们只有一个字段)。但是,如果你有很多字段,它可以显着减少代码量!</p>
+</div>
+
+<p>其余信息来自模型字段的定义(例如标签、小部件、帮助文本、错误消息)。如果这些不太正确,那么我们可以在<code> Meta</code>类中覆盖它们,指定包含要更改的字段、及其新值的字典。例如,在这种形式中,我们可能需要 “更新日期” <em>Renewal date </em>字段的标签(而不是基于字段名称的默认值:截止日期 <em>Due date</em>),并且我们还希望我们的帮助文本,特定于此用例。下面的<code>Meta</code> 显示了如何覆盖这些字段,如果默认值不够,您可以类似地方式设置<code>widgets</code> 窗口小部件和<code>error_messages</code> 。</p>
+
+<pre class="brush: python">class Meta:
+ model = BookInstance
+ fields = ['due_back',]
+<strong> labels = { 'due_back': _('Renewal date'), }
+ help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), } </strong>
+</pre>
+
+<p>要添加验证,您可以使用与普通表单相同的方法 - 定义名为 <code>clean_<em>field_name</em>()</code>的函数,并为无效值引发<code>ValidationError</code> 异常。与我们原始形式的唯一区别,是模型字段名为<code>due_back</code> 而不是“<code>renewal_date</code>”。</p>
+
+<pre class="brush: python">from django.forms import ModelForm
+from .models import BookInstance
+
+class RenewBookModelForm(ModelForm):
+<strong>    def clean_due_back(self):
+       data = self.cleaned_data['due_back']
+
+  #Check date is not in past.
+       if data &lt; datetime.date.today():
+           raise ValidationError(_('Invalid date - renewal in past'))
+
+       #Check date is in range librarian allowed to change (+4 weeks)
+       if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
+           raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
+
+       # Remember to always return the cleaned data.
+       return data
+</strong>
+ class Meta:
+ model = BookInstance
+ fields = ['due_back',]
+ labels = { 'due_back': _('Renewal date'), }
+ help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), }
+</pre>
+
+<p>下面的 <code>RenewBookModelForm</code> 类现在在功能上等同于我们原来的 <code>RenewBookForm</code>。您可以在当前使用<code>RenewBookForm </code>的任何地方导入和使用它。</p>
+
+<h2 id="通用编辑视图">通用编辑视图</h2>
+
+<p>我们在上面的函数视图示例中,使用的表单处理算法,表示表单编辑视图中非常常见的模式。 Django 通过创建基于模型创建、编辑和删除视图的<a href="https://docs.djangoproject.com/zh-hans/2.0/ref/class-based-views/generic-editing/">通用编辑视图</a>,为您抽象出大部分“样板”。这些不仅处理“视图”行为,而且它们会自动从模型中为您创建表单类(<code>ModelForm</code>)。</p>
+
+<div class="note">
+<p><strong>注意:  </strong>除了这里描述的编辑视图之外,还有一个 <a href="https://docs.djangoproject.com/zh-hans/2.0/ref/class-based-views/generic-editing/#formview">FormView </a>类,它位于我们的函数视图,和其他通用视图之间的 “灵活性” 与 “编码工作” 之间。使用 <code>FormView</code> ,您仍然需要创建表单,但不必实现所有标准表单处理模式。相反,您只需提供一个函数的实现,一旦知道提交有效,就会调用该函数。</p>
+</div>
+
+<p>在本节中,我们将使用通用编辑视图,来创建页面,以添加从我们的库中创建、编辑和删除<code>Author</code> 作者记录的功能 - 有效地提供管理站点一部分的基本重新实现(这可能很有用,如果您需要比管理站点能提供的、更加灵活的管理功能)。</p>
+
+<h3 id="视图_2">视图</h3>
+
+<p>打开视图文件(<strong>locallibrary/catalog/views.py</strong>),并将以下代码块,附加到其底部:</p>
+
+<pre class="brush: python">from django.views.generic.edit import CreateView, UpdateView, DeleteView
+from django.urls import reverse_lazy
+from .models import Author
+
+class AuthorCreate(CreateView):
+ model = Author
+ fields = '__all__'
+ initial={'date_of_death':'05/01/2018',}
+
+class AuthorUpdate(UpdateView):
+ model = Author
+ fields = ['first_name','last_name','date_of_birth','date_of_death']
+
+class AuthorDelete(DeleteView):
+ model = Author
+ success_url = reverse_lazy('authors')</pre>
+
+<p>如您所见,要创建视图,您需要从<code>CreateView</code>, <code>UpdateView</code>, 和 <code>DeleteView</code>(分别)派生,然后定义关联的模型。</p>
+
+<p>对于 “创建” 和 “更新” 的情况,您还需要指定要在表单中显示的字段(使用与<code>ModelForm</code>相同的语法)。在这种情况下,我们将说明两者的语法,如何显示 “所有” 字段,以及如何单独列出它们。您还可以使用 field_name / value对的字典,为每个字段指定初始值(此处我们为了演示目的,而任意设置死亡日期 - 您可能希望删除它!)。默认情况下,这些视图会在成功时,重定向到显示新创建/编辑的模型项的页面,在我们的示例中,这将是我们在上一个教程中,创建的作者详细信息视图。您可以通过显式声明参数<code>success_url</code>  ,指定备用重定向位置(与<code>AuthorDelete</code> 类一样)。</p>
+
+<p><code>AuthorDelete</code> 类不需要显示任何字段,因此不需要指定这些字段。但是你需要指定<code>success_url</code>,因为 Django 没有明显的默认值。在这种情况下,我们使用<code><a href="https://docs.djangoproject.com/en/2.0/ref/urlresolvers/#reverse-lazy">reverse_lazy()</a></code>函数,在删除作者后,重定向到我们的作者列表 -  <code>reverse_lazy()</code>是一个延迟执行的<code>reverse()</code>版本,在这里使用,是因为我们提供了一个基于类的 URL 查看属性。</p>
+
+<h3 id="模板_2">模板</h3>
+
+<p>“创建” 和 “更新” 视图默认使用相同的模板,它将以您的模型命名:<em>model_name</em><strong>_form.html</strong>(您可以使用视图中的<code>template_name_suffix</code> 字段,将后缀更改为<strong>_form</strong> 以外的其他内容,例如,<code>template_name_suffix = '_other_suffix'</code>)</p>
+
+<p>创建模板文件 <strong>locallibrary/catalog/templates/catalog/author_form.html</strong>,并复制到下面的文本中。</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+
+&lt;form action="" method="post"&gt;
+ {% csrf_token %}
+ &lt;table&gt;
+ \{{ form.as_table }}
+ &lt;/table&gt;
+ &lt;input type="submit" value="Submit" /&gt;
+
+&lt;/form&gt;
+{% endblock %}</pre>
+
+<p>这与我们之前的表单类似,并使用表单呈现字段。另请注意我们如何声明<code>{% csrf_token %}</code>,以确保我们的表单能够抵抗 CSRF 攻击。</p>
+
+<p>“删除”视图需要查找以 <em>model_name</em><strong>_confirm_delete.html</strong> 格式命名的模板(同样,您可以在视图中,使用<code>template_name_suffix</code> 更改后缀)。创建模板文件 <strong>locallibrary/catalog/templates/catalog/author_confirm_delete</strong><strong>.html</strong> ,并复制到下面的文本中。</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+
+&lt;h1&gt;Delete Author&lt;/h1&gt;
+
+&lt;p&gt;Are you sure you want to delete the author: \{{ author }}?&lt;/p&gt;
+
+&lt;form action="" method="POST"&gt;
+ {% csrf_token %}
+ &lt;input type="submit" action="" value="Yes, delete." /&gt;
+&lt;/form&gt;
+
+{% endblock %}
+</pre>
+
+<h3 id="URL配置">URL配置</h3>
+
+<p>打开 URL 配置文件(<strong>locallibrary/catalog/urls.py</strong>),并将以下配置,添加到文件的底部:</p>
+
+<pre class="brush: python">urlpatterns += [
+    path('author/create/', views.AuthorCreate.as_view(), name='author_create'),
+    path('author/&lt;int:pk&gt;/update/', views.AuthorUpdate.as_view(), name='author_update'),
+    path('author/&lt;int:pk&gt;/delete/', views.AuthorDelete.as_view(), name='author_delete'),
+]</pre>
+
+<p>这里没有什么特别的新东西!您可以看到视图是类,因此必须通过<code>.as_view()</code>调用,并且您应该能够识别每种情况下的 URL 模式。我们必须使用 <code>pk</code> 作为捕获的主键值的名称,因为这是视图类所期望的参数名称。</p>
+
+<p>作者的创建,更新和删除页面,现在已准备好进行测试(在这种情况下,我们不会将它们连接到站点侧栏,尽管如果您愿意,也可以这样做)。</p>
+
+<div class="note">
+<p><strong>注意</strong>:   敏锐的用户会注意到,我们没有采取任何措施,来防止未经授权的用户访问这些页面!我们将其作为练习留给您(提示:您可以使用<code>PermissionRequiredMixin</code> ,并创建新权限,或重用我们的<code>can_mark_returned</code>权限)。</p>
+</div>
+
+<h3 id="测试页面_2">测试页面</h3>
+
+<p>首先,使用具有访问作者编辑页面权限的帐户(由您决定),登录该站点。</p>
+
+<p>然后导航到作者创建页面: <a href="http://127.0.0.1:8000/catalog/author/create/">http://127.0.0.1:8000/catalog/author/create/</a>,它应该如下面的截图。</p>
+
+<p><img alt="Form Example: Create Author" src="https://mdn.mozillademos.org/files/14223/forms_example_create_author.png" style="border-style: solid; border-width: 1px; display: block; height: 184px; margin: 0px auto; width: 645px;"></p>
+
+<p>输入字段的值,然后按“提交” <strong>Submit</strong>  ,保存作者记录。现在,您应该进入新作者的详细视图,其 URL 为 http://127.0.0.1:8000/catalog/author/10。</p>
+
+<p>您可以通过将 /update/ ,附加到详细视图 URL 的末尾,来测试编辑记录(例如http://127.0.0.1:8000/catalog/author/10/update/) - 我们不显示截图,因为它看起来就像“创建”页面!</p>
+
+<p>最后,我们可以删除页面,方法是将删除,附加到作者详细信息视图URL的末尾(例如http://127.0.0.1:8000/catalog/author/10/delete/)。 Django应该显示如下所示的删除页面。按 "是,删除" <strong>(Yes, delete)</strong>。删除记录,并将其带到所有作者的列表中。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14221/forms_example_delete_author.png" style="border-style: solid; border-width: 1px; display: block; height: 194px; margin: 0px auto; width: 561px;"></p>
+
+<p> </p>
+
+<h2 id="挑战自己">挑战自己</h2>
+
+<p>创建一些表单,来创建、编辑和删除书本记录<code>Book</code>。您可以使用与作者<code>Authors</code>完全相同的结构。如果您的 <strong>book_form.html</strong> 模板只是<strong> author_form.html</strong> 模板的复制重命名版本,则新的“创建图书”页面,将如下所示:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14225/forms_example_create_book.png" style="border-style: solid; border-width: 1px; display: block; height: 521px; margin: 0px auto; width: 595px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="总结">总结</h2>
+
+<p>创建和处理表单可能是一个复杂的过程! Django通过提供声明、呈现和验证表单的编程机制,使其变得更加容易。此外,Django提供了通用的表单编辑视图,几乎可以完成所有工作,以定义可以创建,编辑和删除与单个模型实例关联的记录的页面。</p>
+
+<p>表单可以完成更多工作(请参阅下面的“请参阅”列表),但您现在应该了解,如何将基本表单和表单处理代码,添加到您自己的网站。</p>
+
+<h2 id="也可以参考">也可以参考</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/forms/">Working with forms</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/intro/tutorial04/#write-a-simple-form">Writing your first Django app, part 4 &gt; Writing a simple form</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/api/">The Forms API</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/fields/">Form fields</a> (Django docs) </li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/forms/validation/">Form and field validation</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/">Form handling with class-based views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/">Creating forms from models</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/class-based-views/generic-editing/">Generic editing views</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="本系列教程">本系列教程</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/django/generic_views/index.html b/files/zh-cn/learn/server-side/django/generic_views/index.html
new file mode 100644
index 0000000000..c8eeefc366
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/generic_views/index.html
@@ -0,0 +1,630 @@
+---
+title: 'Django 教程 6: 通用列表和详细信息视图'
+slug: learn/Server-side/Django/Generic_views
+translation_of: Learn/Server-side/Django/Generic_views
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">本教程扩充了 <a href="/zh-CN/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> 网站,为书本与作者增加列表与细节页面。此处我们将学到通用类别视图,并演示如何降低你必须为一般使用案例撰写的程式码数量。我们也会更加深入URL处理细节,演示如何实施基本模式匹配。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>完成所有先前的教程主题,包含<a href="/zh-CN/docs/Learn/Server-side/Django/Home_page">Django 教程 5: 创建主页。</a></td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>了解如何使用、在何处使用通用类别视图,以及如何从URLs取出模式,如何传送资料到视图。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概览">概览</h2>
+
+<p>本教程中,通过为书本和作者添加列表和详细信息页面,我们将完成第一个版本的<a href="/zh-CN/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> 网站(或者更准确地说,我们将向您展示如何实现书页,并让您自己创建作者页面!)</p>
+
+<p>该过程类似于创建索引页面,我们在上一个教程中展示了该页面。我们仍然需要创建URL地图,视图和模板。主要区别在于,对于详细信息页面,我们还有一个额外的挑战,即从URL中的模式中提取信息,并将其传递给视图。对于这些页面,我们将演示一种完全不同的视图类型:基于类别的通用列表和详细视图。这些可以显着减少所需的视图代码量,使其更易于编写和维护。</p>
+
+<p>本教程的最后一部分,将演示在使用基于类别的通用列表视图时,如何对数据进行分页。</p>
+
+<h2 id="书本清单页面">书本清单页面</h2>
+
+<p>书本清单页面,将显示页面中所有可用图书记录的列表,使用url: <code>catalog/books/</code>进行访问。该页面将显示每条记录的标题和作者,标题是指向相关图书详细信息页面的超链接。该页面将具有与站点中,所有其他页面相同的结构和导航,因此,我们可以扩展在上一个教程中创建的基本模板(<strong>base_generic.html</strong>)。</p>
+
+<h3 id="URL_映射">URL 映射</h3>
+
+<p>打开<strong>/catalog/urls.py</strong> ,并复制到下面粗体显示的行中。就像索引页面的方式,这个<code>path()</code>函数,定义了一个与 URL 匹配的模式('<strong>books/</strong>'),如果URL匹配,将调用视图函数(<code>views.BookListView.as_view()</code>)和一个对应这个特定映射的名称。</p>
+
+<pre class="brush: python">urlpatterns = [
+ path('', views.index, name='index'),
+<strong>  </strong>path<strong>('books/', views.BookListView.as_view(), name='books'),</strong>
+]</pre>
+
+<p>正如前一个教程中所讨论的,URL 必须已经先匹配了<code>/catalog</code>,因此实际上将为 URL 调用的视图是:<code>/catalog/books/</code>。</p>
+
+<p>视图函数具有与以前不同的格式 - 这是因为该视图,实际上将以类别来实现。我们将继承现有的泛型视图函数,该函数已经完成了我们希望此视图函数执行的大部分工作,而不是从头开始编写自己的函数。对于基于Django类的视图,我们通过调用类方法<code>as_view()</code>,来访问适当的视图函数。这样做可以创建类的实例,并确保为传入的 HTTP 请求调用正确的处理程序方法。</p>
+
+<h3 id="视图_(基于类别)">视图 (基于类别)</h3>
+
+<p>我们可以很容易地,将书本列表视图编写为常规函数(就像我们之前的索引视图一样),它将查询数据库中的所有书本,然后调用<code>render()</code>,将列表传递给指定的模板。然而,我们用另一种方法取代,我们将使用基于类的通用列表视图(<code>ListView</code>) - 一个继承自现有视图的类。因为通用视图,已经实现了我们需要的大部分功能,并且遵循 Django 最佳实践,我们将能够创建更强大的列表视图,代码更少,重复次数更少,最终维护更少。</p>
+
+<p>打开<strong> catalog/views.py</strong>,并将以下代码复制到文件的底部:</p>
+
+<pre class="brush: python">from django.views import generic
+
+class BookListView(generic.ListView):
+ model = Book</pre>
+
+<p>就是这样!通用视图将查询数据库,以获取指定模型(<code>Book</code>)的所有记录,然后呈现位于<strong>/locallibrary/catalog/templates/catalog/book_list.html</strong> 的模板(我们将在下面创建)。在模板中,您可以使用名为<code>object_list</code> 或 <code>book_list</code>的模板变量(即通常为“<code><em>the_model_name</em>_list</code>”),以访问书本列表。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 模板位置的这个尴尬路径不是印刷错误 - 通用视图在应用程序的<code>/<em>application_name</em>/templates/</code>目录中<code>(/catalog/templates/</code>),查找模板<code>/<em>application_name</em>/<em>the_model_name</em>_list.html</code>(在本例中为<code>catalog/book_list.html</code>)。</p>
+</div>
+
+<p>您可以添加属性,以更改上面的默认行为。例如,如果需要使用同一模型的多个视图,则可以指定另一个模板文件,或者如果<code>book_list</code>对于特定模板用例不直观,则可能需要使用不同的模板变量名称。可能最有用的变更,是更改/过滤返回的结果子集 - 因此,您可能会列出其他用户阅读的前5本书,而不是列出所有书本。</p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+ context_object_name = 'my_book_list' # your own name for the list as a template variable
+ queryset = Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
+ template_name = 'books/my_arbitrary_template_name_list.html' # Specify your own template name/location</pre>
+
+<h4 id="覆盖基于类别的视图中的方法">覆盖基于类别的视图中的方法</h4>
+
+<p>虽然我们不需要在这里执行此操作,但您也可以覆盖某些类别方法。</p>
+
+<p>例如,我们可以覆盖<code>get_queryset()</code>方法,来更改返回的记录列表。这比仅仅设置<code>queryset</code>属性更灵活,就像我们在前面的代码片段中所做的那样(尽管在这种情况下没有真正的好处):</p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+
+    def get_queryset(self):
+        return Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
+</pre>
+
+<p>我们还可以覆盖<code>get_context_data()</code> ,以将其他上下文变量传递给模板(例如,默认情况下传递书本列表)。下面的片段,显示了如何将一个名为“<code>some_data</code>”的变量添加到上下文中(然后它将作为一个模板变量,而被提供)。</p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+
+    def get_context_data(self, **kwargs):
+        # Call the base implementation first to get the context
+        context = super(BookListView, self).get_context_data(**kwargs)
+        # Create any data and add it to the context
+        context['some_data'] = 'This is just some data'
+        return context</pre>
+
+<p>这样做时,遵循上面使用的模式非常重要:</p>
+
+<ul>
+ <li>首先从我们的超类别中,获取现有的上下文。</li>
+ <li>然后添加新的上下文信息。</li>
+ <li>然后返回新的(更新的)上下文。</li>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>: 查看<a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/">内置的基于类的通用视图</a>(Django文档),了解更多可以执行的操作示例。</p>
+</div>
+
+<h3 id="创建列表视图模板">创建列表视图模板</h3>
+
+<p>创建 HTML 文件 <strong>/locallibrary/catalog/templates/catalog/book_list.html</strong>,并复制到下面的文本中。如上所述,这是基于类的通用列表视图,所期望的默认模板文件(对于名为<code>catalog</code>的应用程序中,名为<code>Book</code>的模型)。</p>
+
+<p>通用视图的模板就像任何其他模板一样(当然,传递给模板的上下文/信息可能不同)。与我们的索引模板一样,我们在第一行扩展基本模板,然后替换名为<code>content</code>的区块。</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+    &lt;h1&gt;Book List&lt;/h1&gt;
+
+    <strong>{% if book_list %}</strong>
+    &lt;ul&gt;
+
+      {% for book in book_list %}
+      &lt;li&gt;
+        &lt;a href="\{{ book.get_absolute_url }}"&gt;\{{ book.title }}&lt;/a&gt; (\{{book.author}})
+      &lt;/li&gt;
+      {% endfor %}
+
+    &lt;/ul&gt;
+    <strong>{% else %}</strong>
+      &lt;p&gt;There are no books in the library.&lt;/p&gt;
+    <strong>{% endif %} </strong>
+{% endblock %}</pre>
+
+<p>视图默认将上下文(书本列表)作为 <code>object_list </code>和 <code>book_list</code> 的别名传递;任何一个都会奏效。</p>
+
+<h4 id="条件执行">条件执行</h4>
+
+<p>我们使用 <code><a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#if">if</a></code>, <code>else</code> 和 <code>endif</code>模板标签,来检查 <code>book_list</code>是否已定义且不为空。如果 <code>book_list</code>为空,则 <code>else</code>子句显示文本,说明没有要列出的书本。如果 <code>book_list</code>不为空,那么我们遍历书本列表。</p>
+
+<pre class="brush: html"><strong>{% if book_list %}</strong>
+ &lt;!-- code here to list the books --&gt;
+<strong>{% else %}</strong>
+ &lt;p&gt;There are no books in the library.&lt;/p&gt;
+<strong>{% endif %}</strong>
+</pre>
+
+<p>上述条件仅检查一种情况,但您可以使用 <code>elif </code>模板标记(例如<code>{% elif var2 %}</code> )测试其他条件。有关条件运算符的更多信息,请参阅:<a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#if">if</a>, <a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#ifequal-and-ifnotequal">ifequal/ifnotequal</a>,以及<a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins">内置模板标记和过滤器</a>(Django Docs)中的 <a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#ifchanged">ifchanged</a> 。</p>
+
+<h4 id="For_循环回圈">For 循环/回圈</h4>
+
+<p>模板使用<a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#for">for</a> 和 <code>endfor</code>模板标签,以循环遍历书本列表,如下所示。每次迭代都会使用当前列表项的信息,填充书本模板变量<code>book</code>。</p>
+
+<pre class="brush: html">{% for <strong>book</strong> in book_list %}
+ &lt;li&gt; &lt;!-- code here get information from each <strong>book</strong> item --&gt; &lt;/li&gt;
+{% endfor %}
+</pre>
+
+<p>虽然这里没有使用,但在循环中,Django 还会创建其他可用于跟踪迭代的变量。例如,您可以测试<code>forloop.last</code> 变量,以运行最后一次循环当中的条件处理代码。</p>
+
+<h4 id="访问变量">访问变量</h4>
+
+<p>循环内的代码,为每本书创建一个列表项,显示作者和标题(作为尚未创建的详细视图的链接)。</p>
+
+<pre class="brush: html">&lt;a href="\{{ book.get_absolute_url }}"&gt;\{{ book.title }}&lt;/a&gt; (\{{book.author}})
+</pre>
+
+<p>我们使用“点符号”(例如 <code>book.title</code> 和 <code>book.author</code>)访问相关书本记录的字段,其中书本项目<code>book</code>后面的文本是字段名称(如同在模型中定义的)。</p>
+
+<p>我们还可以在模板中,调用模型中的函数 - 在这里,我们调用<code>Book.get_absolute_url()</code>,来获取可用于显示关联详细记录的URL。这项工作提供的函数没有任何参数(没有办法传递参数!)</p>
+
+<div class="note">
+<p><strong>注意</strong>: 在模板中调用函数时,我们必须要小心“副作用”。在这里我们只需要显示一个URL,但是一个函数几乎可以做任何事情 - 我们不想仅仅通过渲染模板,而删除了我们的数据库(例如)!</p>
+</div>
+
+<h4 id="更新基本模板">更新基本模板</h4>
+
+<p>打开基本模板(<strong>/locallibrary/catalog/templates/base_generic.html</strong>)并将 <strong>{% url 'books' %} </strong>插入所有书本 <strong>All books </strong>的 URL 链接,如下所示。这将启用所有页面中的链接(由于我们已经创建了 “books” 的 url 映射器,我们可以成功地将其设置到位)。</p>
+
+<pre class="brush: python">&lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+<strong>&lt;li&gt;&lt;a href="{% url 'books' %}"&gt;All books&lt;/a&gt;&lt;/li&gt;</strong>
+&lt;li&gt;&lt;a href=""&gt;All authors&lt;/a&gt;&lt;/li&gt;</pre>
+
+<h3 id="它看起来是什么样子?">它看起来是什么样子?</h3>
+
+<p>您将无法构建书本清单,因为我们仍然缺少依赖项 - 书本详细信息页面的URL地图,这是创建单个书本的超链接所必需的。我们将在下一节之后,说明列表和详细视图的部分。</p>
+
+<h2 id="书本详细信息页面">书本详细信息页面</h2>
+
+<p>书本详细信息页面,将显示有关特定书本的信息,使用 URL <code>catalog/book/<em>&lt;id&gt;</em></code>(其中 <code><em>&lt;id&gt; </em></code>是书本的主键)进行访问。除了<code>Book</code>模型中的字段(作者,摘要,ISBN,语言和种类)之外,我们还将列出可用副本(<code>BookInstances</code>)的详细信息,包括状态,预期返回日期,印记和 id。这将使我们的读者,不仅可以了解该书,还可以确认是否/何时可用。</p>
+
+<h3 id="URL_映射_2">URL 映射</h3>
+
+<p>打开 <strong>/catalog/urls.py</strong> ,并添加下面粗体显示的 <strong>“book-detail”</strong> URL 映射器。这个<code> path()</code> 函数定义了一个模式,关联到基于通用类的详细信息视图和名称。</p>
+
+<pre class="brush: python">urlpatterns = [
+ path('', views.index, name='index'),
+    path('books/', views.BookListView.as_view(), name='books'),
+<strong>  path('book/&lt;int:pk&gt;', views.BookDetailView.as_view(), name='book-detail'),</strong>
+]</pre>
+
+<p>对于书本详细信息路径,URL 模式使用特殊语法,来捕获我们想要查看的书本的特定 id。语法非常简单:尖括号定义要捕获的URL部分,包含视图可用于访问捕获数据的变量的名称。例如,&lt;<strong>something</strong>&gt; 将捕获标记的模式,并将值作为变量 “something” ,传递给视图。您可以选择在变量名称前,加上一个定义数据类型的<a href="https://docs.djangoproject.com/en/2.0/topics/http/urls/#path-converters">转换器规范</a>(int,str,slug,uuid,path)。</p>
+
+<p>在这里,我们使用 <code>'&lt;int:pk&gt;' </code>来捕获 book id,它必须是一个整数,并将其作为名为 <code>pk </code>的参数(主键的缩写)传递给视图。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 如前所述,我们匹配的URL实际上是 <code>catalog/book/&lt;digits&gt;</code>(因为我们在应用程序 <strong>catalog </strong>中,假定使用<code>/catalog/</code>)。</p>
+</div>
+
+<div class="warning">
+<p><strong>要点</strong>: 基于类的通用详细信息视图,需要传递一个名为 <strong>pk </strong>的参数。如果您正在编写自己的函数视图,则可以使用您喜欢的任何参数名称,或者,确实也可以,在未命名的参数中传递信息。</p>
+</div>
+
+<h4 id="高级路径匹配正则表达式入门">高级路径匹配/正则表达式入门</h4>
+
+<div class="note">
+<p><strong>注意</strong>: 完成教程并不需要此部分说明!我们提供它,是因为了解此可选的部分,未来可能对您使用 Django 有帮助。</p>
+</div>
+
+<p><code>path()</code>提供的模式匹配非常简单,对于您只想捕获任何字符串或整数的(非常常见的)情况非常有用。如果需要更精细的过滤(例如,仅过滤具有一定数量字符的字符串),则可以使用 <a href="https://docs.djangoproject.com/en/2.0/ref/urls/#django.urls.re_path">re_path()</a> 方法。</p>
+
+<p>此方法与</p>
+
+<p><code>path()</code>的使用一样,除了它允许您使用<a href="https://docs.python.org/3/library/re.html">正则表达式</a>,以指定模式。例如,上面的路径可以编写为如下所示:</p>
+
+<pre class="brush: python"><strong>re_path(r'^book/(?P&lt;pk&gt;\d+)$', views.BookDetailView.as_view(), name='book-detail'),</strong>
+</pre>
+
+<p>正则表达式是一种非常强大的模式映射工具。坦率地说,对于初学者来说,他们是非常不直观和可怕的。下面是一个非常短的入门!</p>
+
+<p>首先要知道的是,正则表达式通常应该使用原始字符串文字语法声明(即它们如图所示:<strong>r'&lt;你的正则表达式文本放在这里&gt;'</strong>)。</p>
+
+<p>声明模式匹配需要知道的语法,主要部分是:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">符号</th>
+ <th scope="col">含义</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>^</td>
+ <td>匹配文本的开头</td>
+ </tr>
+ <tr>
+ <td>$</td>
+ <td>匹配文本的结尾</td>
+ </tr>
+ <tr>
+ <td>\d</td>
+ <td>匹配一个位数的数字(0,1,2,... 9)</td>
+ </tr>
+ <tr>
+ <td>\w</td>
+ <td>
+ <p>匹配单词字符,例如字母,数字或下划线字符(_)中的任何大写或小写字符</p>
+ </td>
+ </tr>
+ <tr>
+ <td>+</td>
+ <td>匹配前面一个或多个字符。例如,要匹配一个或多个位数的数字,您将使用<code>\d+</code>。要匹配一个或多个“ a” 字符,您可以使用 <code>a+</code></td>
+ </tr>
+ <tr>
+ <td>*</td>
+ <td>匹配前面字符的零个或多个。例如,要匹配没有内容或单词,您可以使用<code>\w*</code></td>
+ </tr>
+ <tr>
+ <td>( )</td>
+ <td>捕获括号内部模式的一部分。任何捕获的值,都将作为未命名参数,传递给视图(如果捕获了多个模式,则将按照声明捕获的顺序,提供相关参数)。</td>
+ </tr>
+ <tr>
+ <td>(?P&lt;<em>name</em>&gt;...)</td>
+ <td>捕获模式(由...表示)作为命名变量(在本例中为“name”)。捕获的值,将传递给具有指定名称的视图。因此,您的视图,必须声明具有相同名称的参数!</td>
+ </tr>
+ <tr>
+ <td>[  ]</td>
+ <td>匹配集合中的一个字符。例如,[abc] 将匹配 'a' 或 'b' 或 'c'。 [-\w] 将匹配 ' - ' 字符,或任何单词字符。</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>大多数其他字符可以按字面意思理解!</p>
+
+<p>让我们考虑一些模式的真实例子:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">模式</th>
+ <th scope="col">描述</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><strong>r'^book/(?P&lt;pk&gt;\d+)$'</strong></td>
+ <td>
+ <p>这是我们的 url 映射器中使用的 RE。它匹配一个字符串,该字符串在行(<strong>^book/</strong>)的开头具有<code>book/</code>,然后有一个或多个数字(<code>\d+</code>),然后结束(在行标记结束之前,没有非数字字符)。</p>
+
+ <p>它还捕获所有数字(<strong>?P&lt;pk&gt;\d+</strong>),并将它们传递给名为 'pk' 的参数中的视图。<strong>捕获的值始终作为字符串传递</strong>!</p>
+
+ <p>例如,这将匹配 <code>book/1234</code>,并向视图发送变量 <code>pk='1234'</code>。</p>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>r'^book/(\d+)$'</strong></td>
+ <td>这与前面的例子匹配相同的URL。捕获的信息,将作为未命名的参数,发送到视图。</td>
+ </tr>
+ <tr>
+ <td><strong>r'^book/(?P&lt;stub&gt;[-\w]+)$'</strong></td>
+ <td>
+ <p>这匹配一个字符串,该字符串在行(<strong>^book/</strong>)的开头具有<code>book/</code>,然后有一个或多个字符,可以是 ' - ' 或单词字符((<strong>[-\w]+</strong>),然后结束。它还捕获这组字符,并将它们传递给名为 “stub” 的参数中的视图。</p>
+
+ <p>这是 “stub” 的一种相当典型的模式。存根stub 是用于数据的、 URL 友好的、基于单词的主键。如果您希望本书网址提供更多信息,则可以使用 stub。例如 <code>/catalog/book/the-secret-garden</code> ,而不是<code>/catalog/book/33</code>。</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>您可以在一个匹配中捕获多个模式,从而在 URL 中,编码许多不同的信息。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 作为一项挑战,请考虑如何对网址进行编码,以列出特定年份,月份,日期的所有图书,以及可用于匹配它的规则表达式 RE。</p>
+</div>
+
+<h4 id="在_URL_地图中传递其他选项">在 URL 地图中传递其他选项</h4>
+
+<p>我们在这里没有使用、但您可能觉得有价值的一个功能是,您可以向视图声明并传递<a href="https://docs.djangoproject.com/en/2.0/topics/http/urls/#views-extra-options">其他选项</a>。这些选项被声明为一个字典,您将其作为第三个未命名参数,传递给  <code>path()</code>函数。</p>
+
+<p>如果要对多个资源,使用相同的视图,并在每种情况下,传递数据以配置其行为,则此方法非常有用(下面我们在每种情况下提供不同的模板)。</p>
+
+<pre class="brush: python">path('url/', views.my_reused_view, <strong>{'my_template_name': 'some_path'}</strong>, name='aurl'),
+path('anotherurl/', views.my_reused_view, <strong>{'my_template_name': 'another_path'}</strong>, name='anotherurl'),
+</pre>
+
+<div class="note">
+<p><strong>注意:</strong> 额外选项和命名捕获的模式,二者都作为命名参数传递给视图。如果对捕获的模式和额外选项使用<strong>相同的名称</strong>,则仅将捕获的模式值发送到视图(将删除附加选项中指定的值)。</p>
+</div>
+
+<h3 id="视图_(基于类别)_2">视图 (基于类别)</h3>
+
+<p>打开 <strong>catalog / views.py</strong>,并将以下代码复制到文件的底部:</p>
+
+<pre class="brush: python">class BookDetailView(generic.DetailView):
+    model = Book</pre>
+
+<p>就是这样!您现在需要做的就是创建一个名为 <strong>/locallibrary/catalog/templates/catalog/book_detail.html </strong>的模板,该视图将向此模板,传递 URL 映射器提取的特定 <code>Book</code> 记录的数据库信息。在模板中,您可以使用名为  <code>object</code> 或 <code>book</code>的模板变量(即通常为 “<code><em>the_model_name</em></code>”),以访问书本列表。</p>
+
+<p>如果需要,可以更改使用的模板,以及用于在模板中,引用该书本的上下文对象的名称。您还可以覆盖方法,例如,向上下文添加其他信息。</p>
+
+<h4 id="如果记录不存在会怎样?">如果记录不存在会怎样?</h4>
+
+<p>如果请求的记录不存在,那么基于类的通用详细信息视图,将自动为您引发 <code>Http404 </code>异常 - 在生产环境中,这将自动显示适当的 “未找到资源” 页面,您可以根据需要自定义该页面。</p>
+
+<p>为了让您了解其工作原理,下面的代码片段,演示了如何在<strong>不使用</strong>基于类的详细信息视图的情况下,将基于类的视图实现为函数。</p>
+
+<pre class="brush: python">def book_detail_view(request,pk):
+ try:
+ book_id=Book.objects.get(pk=pk)
+ except Book.DoesNotExist:
+ raise Http404("Book does not exist")
+
+  #book_id=get_object_or_404(Book, pk=pk)
+
+ return render(
+ request,
+ 'catalog/book_detail.html',
+ context={'book':book_id,}
+ )
+</pre>
+
+<p>视图首先尝试从模型中,获取特定的书本记录。如果失败,则视图应引发 <code>Http404</code>异常,以指示该书本 “未找到”。然后,最后一步是使用模板名称,和上下文参数<code>context</code>中的书本数据(作为字典)调用<code>render()</code>。</p>
+
+<div class="note">
+<p><strong>注意</strong>: <code>get_object_or_404()</code>(如上所示)是一个方便的快捷方式,用于在未找到记录时,引发 <code>Http404 </code>异常。</p>
+</div>
+
+<h3 id="创建详细信息视图模板">创建详细信息视图模板</h3>
+
+<p>创建 HTML 文件 <strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>,并为其提供以下内容。如上所述,这是基于类的通用详细信息视图,所期望的默认模板文件名(对于名为 <code>catalog </code>的应用程序中名为 <code>Book </code>的模型)。</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+ &lt;h1&gt;Title: \{{ book.title }}&lt;/h1&gt;
+
+ &lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href=""&gt;\{{ book.author }}&lt;/a&gt;&lt;/p&gt; &lt;!-- author detail link not yet defined --&gt;
+ &lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; \{{ book.summary }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;ISBN:&lt;/strong&gt; \{{ book.isbn }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;Language:&lt;/strong&gt; \{{ book.language }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;Genre:&lt;/strong&gt; {% for genre in book.genre.all %} \{{ genre }}{% if not forloop.last %}, {% endif %}{% endfor %}&lt;/p&gt;
+
+ &lt;div style="margin-left:20px;margin-top:20px"&gt;
+ &lt;h4&gt;Copies&lt;/h4&gt;
+
+ {% for copy in book.bookinstance_set.all %}
+ &lt;hr&gt;
+ &lt;p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'm' %}text-danger{% else %}text-warning{% endif %}"&gt;\{{ copy.get_status_display }}&lt;/p&gt;
+ {% if copy.status != 'a' %}&lt;p&gt;&lt;strong&gt;Due to be returned:&lt;/strong&gt; \{{copy.due_back}}&lt;/p&gt;{% endif %}
+ &lt;p&gt;&lt;strong&gt;Imprint:&lt;/strong&gt; \{{copy.imprint}}&lt;/p&gt;
+ &lt;p class="text-muted"&gt;&lt;strong&gt;Id:&lt;/strong&gt; \{{copy.id}}&lt;/p&gt;
+ {% endfor %}
+ &lt;/div&gt;
+{% endblock %}</pre>
+
+<ul>
+</ul>
+
+<div class="note">
+<p><strong>注意: </strong>上面模板中的作者链接,有一个空 URL,因为我们尚未创建作者详细信息页面。一旦创建了,您应该像这样更新URL:</p>
+
+<pre>&lt;a href="<strong>{% url 'author-detail' book.author.pk %}</strong>"&gt;\{{ book.author }}&lt;/a&gt;
+</pre>
+</div>
+
+<p>虽然有点大,但此模板中的几乎所有内容,都已在前面描述过:</p>
+
+<ul>
+ <li>我们扩展基本模板,并覆盖 “内容”区块 content。</li>
+ <li>我们使用条件处理,来确定是否显示特定内容。</li>
+ <li>我们使用 <code>for </code>循环遍历对象列表。</li>
+ <li>我们使用 "点表示法" 访问上下文字段(因为我们使用了详细的通用视图,上下文被命名为<code>book</code>;我们也可以使用 “<code>object</code>”)。</li>
+</ul>
+
+<p>我们以前没见过的一件有趣的事情是函数<code>book.bookinstance_set.all()</code>。此方法由 Django “自动” 构造,以便返回与特定<code> Book</code> 相关联的 <code>BookInstance</code>记录集合。</p>
+
+<pre class="brush: python">{% for copy in book.bookinstance_set.all %}
+&lt;!-- code to iterate across each copy/instance of a book --&gt;
+{% endfor %}</pre>
+
+<p>需要此方法,是因为您仅在关系的 “一” 侧声明 <code>ForeignKey</code>(一对多)字段。由于您没有做任何事情,来声明其他(“多”)模型中的关系,因此它没有任何字段,来获取相关记录集。为了解决这个问题,Django构造了一个适当命名的 “反向查找” 函数,您可以使用它。函数的名称,是通过对声明<code> ForeignKey</code> 的模型名称,转化为小写来构造的,然后是<code>_set</code>(即,在 <code>Book </code>中创建的函数是 <code>bookinstance_set()</code>)。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 这里我们使用<code>all()</code>来获取所有记录(默认值)。虽然您可以使用<code>filter()</code>方法获取代码中的记录子集,但您无法直接在模板中执行此操作,因为您无法指定函数的参数。</p>
+
+<p>还要注意,如果您没有定义顺序(在基于类的视图或模型上),您还会看到开发服务器中的错误,如下所示:</p>
+
+<pre>[29/May/2017 18:37:53] "GET /catalog/books/?page=1 HTTP/1.1" 200 1637
+/foo/local_library/venv/lib/python3.5/site-packages/django/views/generic/list.py:99: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: &lt;QuerySet [&lt;Author: Ortiz, David&gt;, &lt;Author: H. McRaven, William&gt;, &lt;Author: Leigh, Melinda&gt;]&gt;
+ allow_empty_first_page=allow_empty_first_page, **kwargs)
+</pre>
+
+<p>发生这种情况,是因为 <a href="https://docs.djangoproject.com/en/2.0/topics/pagination/#paginator-objects">paginator object </a>对象希望在下划线数据库上看到一些 ORDER BY。没有它,它无法确定,返回的注册表实际上是否为正确顺序!</p>
+
+<p>本教程还没有说明到 <strong>Pagination</strong>(还没,但很快),但由于你不能使用<code>sort_by()</code> 并传递一个参数(与上面描述的<code>filter()</code> 相同),你将不得不在下面三个选择当中,进行挑选:   </p>
+
+<ol>
+ <li>在模型的<code>class Meta</code>声明中,添加排序<code>ordering</code>。</li>
+ <li>Add a <code>queryset</code> attribute in your custom class-based view, specifying a <code>order_by()</code>.在自定义基于类的视图中添加queryset属性,指定order_by()。</li>
+ <li>Adding a <code>get_queryset</code> method to your custom class-based view and also specify the <code>order_by()</code>.将get_queryset方法添加到基于类的自定义视图中,并指定order_by()。</li>
+</ol>
+
+<p>如果您决定使用<code>class Meta </code>作为作者模型<code>Author</code>(可能不像定制基于类的视图那样灵活,但很容易),您最终会得到这样的结果:</p>
+
+<pre>class Author(models.Model):
+ first_name = models.CharField(max_length=100)
+ last_name = models.CharField(max_length=100)
+ date_of_birth = models.DateField(null=True, blank=True)
+ date_of_death = models.DateField('Died', null=True, blank=True)
+
+ def get_absolute_url(self):
+ return reverse('author-detail', args=[str(self.id)])
+
+ def __str__(self):
+ return '%s, %s' % (self.last_name, self.first_name)
+
+<strong> class Meta:
+ ordering = ['last_name']</strong></pre>
+
+<p>当然,该字段不需要是<code>last_name</code>:它可以是任何其他字段。</p>
+
+<p>最后,但并非最不重要的是,您应该按照实际上在数据库上具有索引(唯一或非唯一)的属性/栏位进行排序,以避免性能问题。当然,如果这么少量的书本(和用户!),这里就没有必要(我们可能会让自己提前做太多事情),但是对于未来的项目来说,这是需要考虑的事情。</p>
+</div>
+
+<h2 id="它看起来是什么样子?_2">它看起来是什么样子?</h2>
+
+<p>此时,我们应该创建了显示书本列表,和书本详细信息页面所需的所有内容。运行服务器(<code>python3 manage.py runserver</code>),并打开浏览器到 <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>。</p>
+
+<div class="warning">
+<p><strong>警告:</strong> <span id="result_box" lang="zh-CN"><span>请还不要点击任何作者、或作者详细信息链接 - 您将在挑战练习中,创建这些链接!</span></span></p>
+</div>
+
+<p>单击所有书籍链接<strong> All books</strong> ,以显示书籍列表。</p>
+
+<p><img alt="Book List Page" src="https://mdn.mozillademos.org/files/14049/book_list_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 216px; margin: 0px auto; width: 823px;"></p>
+
+<p>然后点击指向您的某本图书的链接。如果一切设置正确,您应该看到类似下面的屏幕截图。</p>
+
+<p><img alt="Book Detail Page" src="https://mdn.mozillademos.org/files/14051/book_detail_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 783px; margin: 0px auto; width: 926px;"></p>
+
+<h2 id="分页">分页</h2>
+
+<p>如果您刚刚获得了一些记录,我们的图书清单页面看起来会很好。但是,当您进入数十或数百条记录的页面时,页面将逐渐花费更长时间加载(并且有太多内容无法合理浏览)。此问题的解决方案,是为列表视图添加分页,减少每页上显示的项目数。</p>
+
+<p>Django 在分页方面,拥有出色的内置支持。更好的是,它内置于基于类的通用列表视图中,因此您无需执行太多操作即可启用它!</p>
+
+<h3 id="视图">视图</h3>
+
+<p>打开 <strong>catalog/views.py</strong>,然后添加下面粗体显示的<code>paginate_by </code>行。</p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+ <strong>paginate_by = 10</strong></pre>
+
+<p>通过添加这行,只要您有超过10条记录,视图就会开始对它发送到模板的数据,进行分页。使用 GET 参数访问不同的页面 - 要访问第2页,您将使用URL:<code>/catalog/books/<strong>?page=2</strong></code>。</p>
+
+<h3 id="模板">模板</h3>
+
+<p>现在数据已经分页,我们需要添加对模板的支持,以滚动结果集合。因为我们可能希望在所有列表视图中,都执行此操作,所以我们将以可添加到基本模板的方式,执行此操作。</p>
+
+<p>打开 <strong>/locallibrary/catalog/templates/<em>base_generic.html</em></strong>,并复制贴士以下内容区块下面的分页区块(以粗体突出显示)。代码首先检查当前页面上,是否启用了分页。如果是,则它会根据需要,添加下一个和上一个链接(以及当前页码)。</p>
+
+<pre class="brush: python">{% block content %}{% endblock %}
+
+<strong>{% block pagination %}
+ {% if is_paginated %}
+ &lt;div class="pagination"&gt;
+ &lt;span class="page-links"&gt;
+ {% if page_obj.has_previous %}
+ &lt;a href="\{{ request.path }}?page=\{{ page_obj.previous_page_number }}"&gt;previous&lt;/a&gt;
+ {% endif %}
+ &lt;span class="page-current"&gt;
+ Page \{{ page_obj.number }} of \{{ page_obj.paginator.num_pages }}.
+ &lt;/span&gt;
+ {% if page_obj.has_next %}
+ &lt;a href="\{{ request.path }}?page=\{{ page_obj.next_page_number }}"&gt;next&lt;/a&gt;
+ {% endif %}
+ &lt;/span&gt;
+ &lt;/div&gt;
+ {% endif %}
+{% endblock %} </strong></pre>
+
+<p><code>page_obj </code>是一个 <a href="https://docs.djangoproject.com/en/2.0/topics/pagination/#paginator-objects">Paginator</a> 对象,如果在当前页面上使用分页,它将存在。 它允许您获取有关当前页面,之前页面,有多少页面等的所有信息。</p>
+
+<p>我们使用 <code>\{{ request.path }}</code>,来获取用于创建分页链接的当前页面URL。 这很有用,因为它独立于我们正在分页的对象。</p>
+
+<p>就是这样!</p>
+
+<h3 id="它看起来是什么样子的?">它看起来是什么样子的?</h3>
+
+<p>下面的屏幕截图,显示了分页的样子 - 如果您没有在数据库中输入超过10个标题,那么您可以通过降低 <strong>catalog/views.py </strong>文件中 <code>paginate_by </code>行指定的数量,来更轻松地测试它。 为了得到以下结果,我们将其更改为 <code>paginate_by = 2</code>。</p>
+
+<p>分页链接显示在底部,根据您所在的页面,显示下一个/上一个链接。</p>
+
+<p><img alt="Book List Page - paginated" src="https://mdn.mozillademos.org/files/14057/book_list_paginated.png" style="border-style: solid; border-width: 1px; display: block; height: 216px; margin: 0px auto; width: 924px;"></p>
+
+<h2 id="挑战自己">挑战自己</h2>
+
+<p>本文中的挑战,是创建完成项目所需的作者详细信息视图,和列表视图。这些应在以下URL中提供:</p>
+
+<ul>
+ <li><code>catalog/authors/</code> — 所有作者的名单。</li>
+ <li><code>catalog/author/<em>&lt;id&gt;</em></code><em> </em>— 特定作者的详细视图,并具有名为<em><code>&lt;id&gt;</code></em>的主键字段</li>
+</ul>
+
+<p>URL 映射器和视图所需的代码,应与我们上面创建的<code>Book</code>列表和详细视图几乎完全相同。模板将有所不同,但会分享类似的行为。</p>
+
+<p> </p>
+
+<div class="note">
+<p><strong>注意</strong>:</p>
+
+<ul>
+ <li>为作者列表页面,创建URL映射器之后,还需要更新基本模板中的所有作者 <strong>All authors </strong>链接。按照我们更新“所有图书”<strong>All books</strong> 链接时,所做的相同过程。</li>
+ <li>为作者详细信息页面,创建URL映射器之后,还应更新书本详细信息视图模板(<strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>),以便作者链接,指向新的作者详细信息页面(而不是一个空的URL)。该行将更改为添加下面以粗体显示的模板标记。
+ <pre class="brush: html">&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href="<strong>{% url 'author-detail' book.author.pk %}</strong>"&gt;\{{ book.author }}&lt;/a&gt;&lt;/p&gt;
+</pre>
+ </li>
+</ul>
+</div>
+
+<p>完成后,您的页面应该类似于下面的屏幕截图。</p>
+
+<p><img alt="Author List Page" src="https://mdn.mozillademos.org/files/14053/author_list_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<ul>
+</ul>
+
+<p><img alt="Author Detail Page" src="https://mdn.mozillademos.org/files/14055/author_detail_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 358px; margin: 0px auto; width: 825px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="总结">总结</h2>
+
+<p>恭喜,我们的图书馆的基本功能现在完成了!</p>
+
+<p>本文中,我们学到如何使用基于类别的通用列表视图与详细视图,并使用它们创建页面,以查看我们的书本和作者。在此过程中,我们了解了与正则表达式匹配的模式,以及如何将数据从URL传递到视图。我们还学习了一些使用模板的技巧。最后,我们已经展示了如何对列表视图进行分页,这样即使我们有很多记录,我们也可以管理列表。</p>
+
+<p>在我们的下一篇文章,我们将扩充此图书馆,以支持使用者帐户,并从而演示使用者授权、许可、授权、会话, 以及表单。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/">Built-in class-based generic views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/class-based-views/generic-display/">Generic display views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/intro/">Introduction to class-based views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins">Built-in template tags and filters</a> (Django docs).</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/pagination/">Pagination</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="本教程">本教程</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django 介绍</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">架设 Django 开发环境</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django 教程: 本地图书馆网站</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django 教程 2: 创建骨架站点</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django 教程 3: 使用模型</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django 教程 4: Django 管理站点</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django 教程 5: 创建主页</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django 教程 6: 通用列表与详细信息视图</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django 教程 7: 会话框架</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django 教程 8: 用户认证与许可</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django 教程 9: 使用表单</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django 教程 10: 测试 Django 网页应用</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django 教程 11: 部署 Django 到生产环境</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django 网页应用安全</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django 微博客</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/django/index.html b/files/zh-cn/learn/server-side/django/index.html
new file mode 100644
index 0000000000..cb92e9cc05
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/index.html
@@ -0,0 +1,60 @@
+---
+title: Django Web 框架(python)
+slug: learn/Server-side/Django
+translation_of: Learn/Server-side/Django
+---
+<div>{{LearnSidebar}}</div>
+
+<p>Django 是使用 Python 语言编写的一个广受欢迎且功能完整的服务器端网站框架。 本模块将为您展示为什么 Django 能够成为一个广受欢迎的服务器端框架,如何设置开发环境,以及如何开始创建你自己的网络应用。</p>
+
+<h2 id="先决条件">先决条件</h2>
+
+<p>开始学习本模块并不需要任何 Django 知识. 但您要理解什么是服务器端网络编程、什么是网络框架,最好能够阅读我们的<a href="/zh-CN/docs/Learn/Server-side/First_steps">服务端网站编程的第一步</a>模块。</p>
+
+<p>最好能有基本的编程概念并了解 <a href="/zh-CN/docs/Glossary/Python">Python</a> 语言,但其并不是理解本教程的核心概念的必然条件。</p>
+
+<div class="note">
+<p><span style="font-size: 14px;"><strong>注意:</strong></span>对于初学者来说,Python 是最容易阅读和理解的编程语言之一。也就是说,如果您想更好的理解本教程,网上有很多免费书籍及免费教程可供参考学习(建议初学者查看 Python 官网的 <a href="https://wiki.python.org/moin/BeginnersGuide/NonProgrammers">Python for Non Programmers</a>  教程)。</p>
+</div>
+
+<h2 id="指南">指南</h2>
+
+<dl>
+ <dt><a href="https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Django/Introduction">Django简介</a></dt>
+ <dd>在第一篇关于Django的文章里,我们会回答"什么是Django?",并概述这个网络框架的特殊之处.我们会列出主要的功能,包括一些高级的功能特性,这些高级特性我们在这部分教程里没有时间详细说明.在你设置好Django应用并开始把玩它之前,我们会展示Django应用的一些主要模块,让你明白Django应用能做什么.</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/development_environment">创建Django开发环境</a></dt>
+ <dd>现在你知道Django是做什么的,我们会展示怎样在Windows, Linux(Ubuntu)和Mac OS X上创建和测试Django的开发环境—不管你是用什么操作系统,这篇文章会教给你能够开发Django应用所需要的开发环境.</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django教程1:本地图书馆网站</a></dt>
+ <dd>我们实用教程系列的第一篇文章会解释你将学习到什么,并提供"本地图书馆"网站这个例子的概述.我们会在接下来的文章里完成并不断的进化这个网站.</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/skeleton_website">Django教程2:创建网站的框架</a></dt>
+ <dd>这篇文章会教你怎样创建一个网站的"框架".以这个网站为基础,你可以填充网站特定的settings,urls, models,views和templates</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Models">Django教程3:使用模型</a></dt>
+ <dd>这篇文章会为 <em>本地图书馆 </em>网站定义数据模板—数据模板是我们为应用存储的数据结构.并且允许Django在数据库中存储数据(以后可以修改).文章解释了什么是数据模板,怎样声明它和一些主要的数据种类.文章还简要的介绍了一些你可以获得数据模板的方法.</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django 管理站点</a></dt>
+ <dd>现在我们已经为本地图书馆网站创建了模型,我们将使用 Django 管理站点 添加一些 ‘真实的’ 的图书数据。首先,我们将向你介绍如何使用管理站点注册模型,然后我们介绍如何登录和创建一些数据。最后我们展示一些进一步改进管理站点的演示方法。</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: 创建我们的主页</a></dt>
+ <dd>我们现在可以添加代码来展示我们的第一次完整页面—本地图书馆主页,来显示我们对每个模型类型有多少条记录,并提供我们其他页面的侧边栏导航链接。一路上,我们将获得编写基本URL地图和视图,从数据库获取记录以及使用模版的实践经验。</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: 通用列表和详细视图</a></dt>
+ <dd>本教程扩展了我们的本地图书馆网站,添加书籍和作者和详细页面。在这里,我们将了解基于类的通用视图,并展示如何减少常用代码用例的代码量。我们还将更详细地深入理解URL处理,显示如何执行基本模式匹配。</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: 会话框架</a></dt>
+ <dd>本教程扩展本地图书馆网站,向主页添加了一个基于会话的访问计数器。这是个比较简单的例子,但它显示如何使用会话框架为你自己的站点中的匿名用户提供一致的行为。</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: 用户身份验证和权限</a></dt>
+ <dd>本教程,我们将向你展示如何允许用户使用自己的账户登录到你的网站,以及如何根据他们是否登录及其权限来控制他们可以做什么和看到什么。作为此次演示的一部分,我们将扩展本地图书馆网站,添加登录和注销页面以及用户和工作人员特定页面,以查看已借用的书籍。</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: 使用表单</a></dt>
+ <dd>本教程,我们将向你展示如何使用Django 中的<a href="https://developer.mozilla.org/zh-CN/docs/Web/Guide/HTML/Forms">HTML表单</a>,特别是编写创建表单,更新和删除模型实例的最简单方法。作为此次演示的一部分,我们将扩展本地图书馆网站,以便图书馆员可以使用我们自己的表单(而不是使用管理应用程序) 来更新书籍,创建,更新和删除作者。</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10:测试Django Web 应用程序</a></dt>
+ <dd>随着网站的的发展,手工测试越来越难测试—不仅要测试更多,而且随着组件之间的相互作用变得越来越复杂,一个领域的一个小的变化可能需要许多额外的测试来验证其对其他领域的影响。减轻这些问题的一种方法是编写自动化测试,每次更改时都可以轻松可靠地运行。本教程将介绍如何使用 Django 的测试框架对你的网站进行 <em><strong>单元测试</strong></em>自动化。</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: 将Django部署到生产</a></dt>
+ <dd>现在,你已创建(并测试)一个酷的 本地图书馆网站,你将要把它安装在公共Web服务器上,以便图书馆员工和成员可以通过Internet访问。本文概述了如何找到主机来部署你的网站,以及你需要做什么才能使你的网站准备好进行生产。</dd>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/web_application_security">Django web 应用程序安全</a></dt>
+ <dd>保护用户数据是任何网站设计的重要组成部分,我们以前解释了Web安全文章中一些更常见的安全威胁—本文提供了Django内置如何保护处理这种危险的实际演示。</dd>
+</dl>
+
+<h2 id="评估">评估</h2>
+
+<p>以下评估将测试你对如何使用Django创建网站的理解,如上述指南中所述。</p>
+
+<dl>
+ <dt><a href="/zh-CN/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></dt>
+ <dd>在这个评估中,你将使用你从本单元中学到的一些知识来创建自己的博客。</dd>
+</dl>
diff --git a/files/zh-cn/learn/server-side/django/introduction/index.html b/files/zh-cn/learn/server-side/django/introduction/index.html
new file mode 100644
index 0000000000..4bb940e2f3
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/introduction/index.html
@@ -0,0 +1,268 @@
+---
+title: Django 介绍
+slug: learn/Server-side/Django/Introduction
+translation_of: Learn/Server-side/Django/Introduction
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">在这第一Django文章中,我们将回答“什么是Django”这个问题,并概述这个网络框架有什么特性。我们将描述主要功能,包括一些高级功能,但我们并不会在本单元中详细介绍。我们还会展示一些Django应用程序的主要构建模块(尽管此时你还没有要测试的开发环境)。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>基本的电脑知识. 对 <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps">服务器端网站编程的一般了解</a>, 特别是 <a href="/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview">网站中客户端-服务器交互的机制</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">目的:</th>
+ <td>了解Django是什么,它提供了哪些功能,以及Django应用程序的主要构建块。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Django是什么">Django是什么?</h2>
+
+<p>Django 是一个高级的 Python 网络框架,可以快速开发安全和可维护的网站。由经验丰富的开发者构建,Django负责处理网站开发中麻烦的部分,因此你可以专注于编写应用程序,而无需重新开发。<br>
+ 它是免费和开源的,有活跃繁荣的社区,丰富的文档,以及很多免费和付费的解决方案。</p>
+
+<p>Django 可以使你的应用具有以下优点:</p>
+
+<dl>
+ <dt>完备性</dt>
+ <dd>Django遵循“功能完备”的理念,提供开发人员可能想要“开箱即用”的几乎所有功能。因为你需要的一切都是一个”产品“的一部分,它们都可以无缝结合在一起,遵循一致性设计原则,并且具有广泛和<a href="https://docs.djangoproject.com/en/1.10/">最新的文档</a>.</dd>
+ <dt>通用性</dt>
+ <dd>Django 可以(并已经)用于构建几乎任何类型的网站—从内容管理系统和维基,到社交网络和新闻网站。它可以与任何客户端框架一起工作,并且可以提供几乎任何格式(包括 HTML,Rss源,JSON,XML等)的内容。你正在阅读的网站就是基于Django。<br>
+ <br>
+ 在内部,尽管它为几乎所有可能需要的功能(例如几个流行的数据库,模版引擎等)提供了选择,但是如果需要,它也可以扩展到使用其他组件。</dd>
+ <dt>安全性</dt>
+ <dd>Django 帮助开发人员通过提供一个被设计为“做正确的事情”来自动保护网站的框架来避免许多常见的安全错误。例如,Django提供了一种安全的方式来管理用户账户和密码,避免了常见的错误,比如将session放在cookie中这种易受攻击的做法(取而代之的是cookies只包含一个密钥,实际数据存储在数据库中)或直接存储密码而不是密码哈希。<br>
+ <br>
+ 密码哈希是通过<em><a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function">密码散列函数</a>发送密码而创建的固定长度值。 Django 能通过运行哈希函数来检查输入的密码-就是-将输出的哈希值与存储的哈希值进行比较是否正确。然而由于功能的“单向”性质,即时存储的哈希值受到威胁,攻击者也难以解决原始密码。(但其实有彩虹表-译者观点)</em><br>
+ <br>
+ 默认情况下,Django 可以防范许多漏洞,包括SQL注入,跨站点脚本,跨站点请求伪造和点击劫持 (请参阅 <a href="/en-US/docs/Learn/Server-side/First_steps/Website_security">网站安全</a> 相关信息,如有兴趣).</dd>
+ <dt>可扩展</dt>
+ <dd>Django 使用基于组件的 “<a href="https://en.wikipedia.org/wiki/Shared_nothing_architecture">无共享</a>” 架构 (架构的每一部分独立于其他架构,因此可以根据需要进行替换或更改). 在不用部分之间有明确的分隔意味着它可以通过在任何级别添加硬件来扩展服务:缓存服务器,数据库服务器或应用程序服务器。一些最繁忙的网站已经成功地缩放了Django,以满足他们的需求(例如Instagram和Disqus,仅举两个例子,可自行添加)。</dd>
+ <dt>可维护性</dt>
+ <dd>Django 代码编写是遵照设计原则和模式,鼓励创建可维护和可重复使用的代码。特别是它使用了不要重复自己(DRY)原则,所以没有不必要的重复,减少了代码的数量。Django还将相关功能分组到可重用的“应用程序”中,并且在较低级别将相关代码分组或模块( <a href="/en-US/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture">模型视图控制器 (MVC)</a> 模式).</dd>
+ <dt>灵活性</dt>
+ <dd>Django 是用Python编写的,它在许多平台上运行。这意味着你不受任务特定的服务器平台的限制,并且可以在许多种类的Linux,Windows和Mac OsX 上运行应用程序。此外,Django得到许多网络托管提供商的好评,他们经常提供特定的基础设施和托管Django网站的文档。</dd>
+</dl>
+
+<h2 id="它的出生">它的出生?</h2>
+
+<p>Django 最初由2003年到2005年间由负责创建和维护报纸网站的网络团队开发。在创建了许多网站后,团队开始考虑并重用许多常见的代码和设计模式。这个共同的代码演变一个通用的网络开发框架,2005年7月被开源“Django”项目。</p>
+
+<p>Django 不断发展壮大—从2008年9月的第一个里程碑版本(1.0)到最近发布的(1.11)-(2017)版本。每个版本都添加了新功能和错误修复,从支持新类型的数据库,模版引擎和缓存,到添加“通用”视图函数和类(这减少了开发人员必须编写的代码量)一些编程任务。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 查看Django网站上<span style="line-height: 1.5;"> <a href="https://docs.djangoproject.com/en/1.10/releases/">发行说明</a>,看看最近版本发生了什么变化,以及Django能做多少工作。</span></p>
+</div>
+
+<p>Django 现在是一个蓬勃发展的合作开源项目<span style="line-height: 1.5;">,拥有数千个用户和贡献着。虽然它仍然具有反映其起源的一些功能,但Django已经发展成为能够开发任何类型的网站的多功能框架。 </span></p>
+
+<h2 id="Django有多受欢迎">Django有多受欢迎?</h2>
+
+<p>服务器端框架的受欢迎程度没有任何可靠和明确的测量(尽管<a href="http://hotframeworks.com/">Hot Frameworks</a>网站 尝试使用诸如计算每个平台的GitHub项目数量和StackOverflow问题的机制来评估流行度)。一个更好的问题是Django是否“足够流行”,以避免不受欢迎的平台的问题。它是否继续发展?如果您需要帮助,可以帮您吗?如果您学习Django,有机会获得付费工作吗?</p>
+
+<p>基于使用Django的流行网站数量,为代码库贡献的人数以及提供免费和付费支持的人数,那么是的,Django是一个流行的框架!</p>
+
+<p>使用Django的流行网站包括:Disqus,Instagram,骑士基金会,麦克阿瑟基金会,Mozilla,国家地理,开放知识基金会,Pinterest和开放栈(来源:<a href="https://www.djangoproject.com/">Django home page</a>).</p>
+
+<h2 id="Django_是特定">Django 是特定?</h2>
+
+<p>Web框架通常将自己称为“特定”或“无限制”。</p>
+
+<p>特定框架是对处理任何特定任务的“正确方法”有意见的框架。他们经常支持特定领域的快速发展(解决特定类型的问题),因为正确的做法是通常被很好地理解和记录在案。然而,他们在解决其主要领域之外的问题时可能不那么灵活,并且倾向于为可以使用哪些组件和方法提供较少的选择。</p>
+
+<p>相比之下,无限制的框架对于将组件粘合在一起以实现目标或甚至应使用哪些组件的最佳方式的限制较少。它们使开发人员更容易使用最合适的工具来完成特定任务,尽管您需要自己查找这些组件。</p>
+
+<p>Django“有点有意义”,因此提供了“两个世界的最佳”。它提供了一组组件来处理大多数Web开发任务和一个(或两个)首选的使用方法。然而,Django的解耦架构意味着您通常可以从多个不同的选项中进行选择,也可以根据需要添加对全新的支持。</p>
+
+<h2 id="Django代码是什么样的">Django代码是什么样的?</h2>
+
+<p>在传统的数据驱动网站中,Web应用程序会等待来自Web浏览器(或其他客户端)的 HTTP 请求。当接收到请求时,应用程序根据 URL 和可能的 POST 数据或 GET 数据中的信息确定需要的内容。根据需要,可以从数据库读取或写入信息,或执行满足请求所需的其他任务。然后,该应用程序将返回对Web浏览器的响应,通常通过将检索到的数据插入 HTML模板中的占位符来动态创建用于浏览器显示的 HTML 页面。</p>
+
+<p>Django 网络应用程序通常将处理每个步骤的代码分组到单独的文件中:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13931/basic-django.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<ul>
+ <li><strong>URLs: </strong>虽然可以通过单个功能来处理来自每个URL的请求,但是编写单独的视图函数来处理每个资源是更加可维护的。URL映射器用于根据请求URL将HTTP请求重定向到相应的视图。URL映射器还可以匹配出现在URL中的字符串或数字的特定模式,并将其作为数据传递给视图功能。<br>
+  </li>
+ <li><strong>View:</strong>  视图 是一个请求处理函数,它接收HTTP请求并返回HTTP响应。视图通过模型访问满足请求所需的数据,并将响应的格式委托给  模板。<br>
+  </li>
+ <li><strong>Models:</strong>  模型 是定义应用程序数据结构的Python对象,并提供在数据库中管理(添加,修改,删除)和查询记录的机制。<br>
+  </li>
+ <li><strong>Templates:</strong> 模板 是定义文件(例如HTML页面)的结构或布局的文本文件,用于表示实际内容的占位符。一个视图可以使用HTML模板,从数据填充它动态地创建一个HTML页面模型。可以使用模板来定义任何类型的文件的结构; 它不一定是HTML!</li>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>: Django将此组织称为“模型视图模板(MVT)”架构。它与更加熟悉的 <a href="/en-US/docs/Web/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture">Model View Controller</a> 架构有许多相似之处. </p>
+</div>
+
+<ul>
+</ul>
+
+<p>以下部分将为您提供Django应用程序的这些主要部分的想法(稍后我们将在进一步详细介绍后,我们将在开发环境中进行更详细的介绍)。</p>
+
+<h3 id="将请求发送到正确的视图_(urls.py)">将请求发送到正确的视图 (urls.py)</h3>
+
+<p>URL映射器通常存储在名为urls.py的文件中。在下面的示例中,mapper(urlpatterns)定义了特定URL 模式 和相应视图函数之间的映射列表。如果接收到具有与指定模式匹配的URL(例如r'^$',下面)的HTTP请求,则将调用 相关联的视图功能(例如  views.index)并传递请求。</p>
+
+<pre>urlpatterns = [
+ <strong>url(r'^$', views.index),</strong>
+ url(r'^([0-9]+)/$', views.best),
+]
+</pre>
+
+<div class="note">
+<p><strong>注意</strong>: 一点点Python:</p>
+
+<ul>
+ <li>该 <code>urlpatterns</code> 对象的列表url() 功能。在Python中,使用方括号定义列表。项目以逗号分隔,并可能有一个 <a href="https://docs.python.org/2/faq/design.html#why-does-python-allow-commas-at-the-end-of-lists-and-tuples">可选的逗号</a>. 例如: <code>[item1, item2, item3,]</code>.</li>
+ <li>该模式的奇怪的语法称为正则表达式。我们将在后面的文章中讨论这些内容!</li>
+ <li>第二个参数 url() 是当模式匹配时,将被调用的另一个函数。符号views.index 表示该函数被调用,index()并且可以在被调用的模块中找到views (即在一个名为views.py的文件中)。</li>
+</ul>
+</div>
+
+<h3 id="处理请求_(views.py)">处理请求 (views.py)</h3>
+
+<p>视图是Web应用程序的核心,从Web客户端接收HTTP请求并返回HTTP响应。在两者之间,他们编制框架的其他资源来访问数据库,渲染模板等。</p>
+
+<p>下面的例子显示了一个最小的视图功能index(),这可以通过我们的URL映射器在上一节中调用。像所有视图函数一样,它接收一个HttpRequest对象作为参数(request)并返回一个HttpResponse对象。在这种情况下,我们对请求不做任何事情,我们的响应只是返回一个硬编码的字符串。我们会向您显示一个请求,在稍后的部分中会提供更有趣的内容。</p>
+
+<pre class="brush: python">## filename: views.py (Django view functions)
+
+from django.http import HttpResponse
+
+def index(request):
+ # Get an HttpRequest - the request parameter
+ # perform operations using information from the request.
+  # Return HttpResponse
+ return HttpResponse('Hello from Django!')
+</pre>
+
+<div class="note">
+<p><strong>注意</strong>: 一点点Python:</p>
+
+<ul>
+ <li><a href="https://docs.python.org/3/tutorial/modules.html">Python 模块</a> 是函数的“库”,存储在单独的文件中,我们可能想在我们的代码中使用它们。在这里我们只从django.http模块导入了HttpResponse对象,使我们可以在视图中使用它:<br>
+ from django.http import HttpResponse。<br>
+ 还有其他方法可以从模块导入一些或所有对象。</li>
+ <li>
+ <p>如上所示,使用<code>def</code>关键字声明函数<strong>,</strong>在函数名称后面的括号中列出命名参数;整行以冒号结尾。注意下一行是否都进行了<strong>缩进</strong>。缩进很重要,因为它指定代码行在该特定块内 (强制缩进是Python的一个关键特征,也是Python代码很容易阅读的一个原因)。</p>
+ </li>
+</ul>
+</div>
+
+<ul>
+</ul>
+
+<p>视图通常存储在一个名为 <strong>views.py</strong> 的文件中。</p>
+
+<h3 id="定义数据模型_(models.py)">定义数据模型 (models.py)</h3>
+
+<p>Django Web应用程序通过被称为模型的Python对象来管理和查询数据。模型定义存储数据的结构,包括字段类型  以及字段可能的最大值,默认值,选择列表选项,文档帮助文本,表单的标签文本等。模型的定义与底层数据库无关 -您可以选择其中一个作为项目设置的一部分。一旦您选择了要使用的数据库,您就不需要直接与之交谈 - 只需编写模型结构和其他代码,Django可以处理与数据库通信的所有辛苦的工作。</p>
+
+<p>下面的代码片段为<strong>Team</strong>对象展示了一个非常简单的Django模型。本<strong>Team</strong>类是从Django的类派生<strong>models.Model</strong>。它将团队名称和团队级别定义为字符字段,并为每个记录指定了要存储的最大字符数。<strong>team_level</strong> 可以是几个值中的一个,因此,我们将其定义为一个选择字段,并在被展示的数据和被储存的数据之间建立映射,并设置一个默认值。</p>
+
+<p> </p>
+
+<pre class="brush: python"># filename: models.py
+
+from django.db import models
+
+class Team(models.Model):
+  team_name = models.CharField(max_length=40)
+
+    TEAM_LEVELS = (
+        ('U09', 'Under 09s'),
+        ('U10', 'Under 10s'),
+        ('U11', 'Under 11s'),
+  ... #list other team levels
+    )
+    team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
+</pre>
+
+<div class="note">
+<p><strong>注意</strong>: Python小知识:</p>
+
+<ul>
+ <li>Python支持“面向对象编程”,这是一种编程风格,我们将代码组织到对象中,其中包括用于对该对象进行操作的相关数据和功能。对象也可以从其他对象继承/扩展/派生,允许相关对象之间的共同行为被共享。在Python中,我们使用关键字<strong> Class </strong>定义对象的“蓝图”。我们可以根据类中的模型创建类型的多个 特定 实例。</li>
+ <li><br>
+ 例如,我们有个 <strong>Team</strong> 类,它来自于<strong>Model</strong>类。这意味着它是一个模型,并且将包含模型的所有方法,但是我们也可以给它自己的专门功能。在我们的模型中,我们定义了我们的数据库需要存储我们的数据字段,给出它们的具体名称。Django使用这些定义(包括字段名称)来创建底层数据库。</li>
+</ul>
+</div>
+
+<h3 id="查询数据_(views.py)">查询数据 (views.py)</h3>
+
+<p>Django模型提供了一个用于搜索数据库的简单查询API。这可以使用不同的标准(例如,精确,不区分大小写,大于等等)来匹配多个字段,并且可以支持复杂语句(例如,您可以在拥有一个团队的<strong> U11 </strong>团队上指定搜索名称以“Fr”开头或以“al”结尾)。</p>
+
+<p>代码片段显示了一个视图函数(资源处理程序),用于显示我们所有的<strong> U09 </strong>团队。粗体显示如何使用模型查询API过滤所有记录,其中该  <strong>team_level</strong> 字段具有正确的文本“<strong>U09</strong>”(请注意,该条件如何filter()作为参数传递给该函数,该字段名称和匹配类型由双下划线: <strong> team_level__exact</strong>)</p>
+
+<pre class="brush: python">## filename: views.py
+
+from django.shortcuts import render
+from .models import Team
+
+def index(request):
+    <strong>list_teams = Team.objects.filter(team_level__exact="U09")</strong>
+    context = {'youngest_teams': list_teams}
+    return <strong>render</strong>(request, '/best/index.html', context)
+</pre>
+
+<dl>
+</dl>
+
+<p>此功能使用 <strong>render</strong>() 功能创建 <strong>HttpResponse</strong> 发送回浏览器的功能。这个函数是一个快捷方式;它通过组合指定的HTML模版和一些数据来插入模版(在名为 “<strong>context</strong>” 的变量中提供)来创建一个<strong>HTML</strong>文件。在下一节中,我们将介绍如何在其中插入数据以创建<strong>HTML</strong>。</p>
+
+<h3 id="呈现数据_(HTML_模版)">呈现数据 (HTML 模版)</h3>
+
+<p>模版系统允许你指定输出文档的结构,使用<br>
+ 占位符<br>
+ {% if youngest_teams%}<br>
+ 来生成页面时填写的数据。模版通常用于创建HTMl,但也可以创建其他类型的文档。Django支持其原生模版系统和另一种流行的Python库(称为jinja2)开箱即用(如果需要,也可以支持其他系统)。</p>
+
+<p>代码片段显示render()了上一节中函数调用的HTML模版的外观。这个模版已经被写入这样的想法,即它将被访问一个列表变量,<br>
+ youngest_teams当它被渲染时</p>
+
+<pre class="brush: python">## filename: best/templates/best/index.html
+
+&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;body&gt;
+
+ {% if youngest_teams %}
+    &lt;ul&gt;
+    {% for team in youngest_teams %}
+        &lt;li&gt;\{\{ team.team_name \}\}&lt;/li&gt;
+    {% endfor %}
+    &lt;/ul&gt;
+{% else %}
+    &lt;p&gt;No teams are available.&lt;/p&gt;
+{% endif %}
+
+&lt;/body&gt;
+&lt;/html&gt;</pre>
+
+<h2 id="你还能做什么?">你还能做什么?</h2>
+
+<p>前面的部分显示了几乎每个Web应用程序将使用的主要功能:URL映射,视图,模型和模版。Django提供的其他内容包括:</p>
+
+<ul>
+ <li><strong>表单</strong>: HTML 表单用于收集用户数据以便在服务器上进行处理。Django简化了表单创建,验证和处理。</li>
+ <li><strong>用户身份验证和权限</strong>: Django包含了一个强大的用户身份验证和权限系统,该系统已经构建了安全性。</li>
+ <li><strong>缓存</strong>:  与提供静态内容相比,动态创建内容需要更大的计算强度(也更缓慢)。Django提供灵活的缓存,以便你可以存储所有或部分的页面。如无必要,不会重新呈现网页。</li>
+ <li><strong>管理网站</strong>: 当你使用基本骨架创建应用时,就已经默认包含了一个Django管理站点。它十分轻松地创建了一个管理页面,使网站管理员能够创建、编辑和查看站点中的任何数据模型。</li>
+ <li><strong>序列化数据</strong>: Django可以轻松地将数据序列化,并支持XML或JSON格式。这会有助于创建一个Web服务(Web服务指数据纯粹为其他应用程序或站点所用,并不会在自己的站点中显示),或是有助于创建一个由客户端代码处理和呈现所有数据的网站。</li>
+</ul>
+
+<h2 id="概要">概要</h2>
+
+<p>恭喜,您已经完成了Django之旅的第一步!您现在应该了解Django的主要优点,一些关于它的历史,以及Django应用程序的每个主要部分可能是什么样子。您还应该了解Python编程语言的一些内容,包括列表,函数和类的语法。</p>
+
+<p>您已经看到上面的一些真正的Django代码,但与客户端代码不同,您需要设置一个开发环境来运行它。这是我们的下一步。</p>
+
+<div>{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}</div>
diff --git a/files/zh-cn/learn/server-side/django/models/index.html b/files/zh-cn/learn/server-side/django/models/index.html
new file mode 100644
index 0000000000..23975c8d7a
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/models/index.html
@@ -0,0 +1,449 @@
+---
+title: 'Django Tutorial Part 3: 使用模型'
+slug: learn/Server-side/Django/Models
+translation_of: Learn/Server-side/Django/Models
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}</div>
+
+<div>这篇文章展示了如何为我们的LocalLibray(本地图书馆)网站定义models。它解释了一个模型是什么,它是怎么被声明的,和其中的一些主要域类型。</div>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prerequisites:</th>
+ <td><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objective:</th>
+ <td>能够设计并创建你自己的数据模型,并为其合适地选择域。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概要">概要</h2>
+
+<p>Django网络应用通过作为模型被参照的Python对象访问并管理数据。模型定义了储存数据的结构,包括域类型和可能的最大值,默认值,可选择的列表,帮助理解文档的文本,表格内的标签文本,等等。模型的定义是独立于数据库的——你可以为你自己的项目设置选择一种。一旦你已经选择了你想用的数据库,你不需要直接谈论它——你只是写出你的模型结构和其他代码,然后Django会为你处理所有繁琐的和数据库打交道的工作。</p>
+
+<p>这个教程展示了如何定义并访问 <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary website</a> 的模型。</p>
+
+<h2 id="设计LocalLibaray模型">设计LocalLibaray模型</h2>
+
+<p>在你继续开始为模型写代码前,用几分钟考虑一下我们需要储存什么数据和不同对象之间的关系是很有价值的。</p>
+
+<p>我们知道我们需要存储书籍(书名,简介,作者,写作语言,类别,ISBN编号)和我们可能有的可获取的副本数量(全局独立ID,可获取状态,等等)。我们可能需要存储更多的关于作者的信息而不仅仅是她们的名字。我们希望能够将信息根据书名,作者,写作语言和类别分类。</p>
+
+<p>当设计你的模型时,给予每个“对象”(一组关联信息)独立的模型似乎挺说得通的。当前情况下,最为明显的对象就是书籍和作者。</p>
+
+<p>你可能在想相比硬编码所有的选项到网站上,用模型来呈现选择列表(例如包括了许多选项的下拉菜单)——我们推荐这样,尤其是当选项未知或者可能改变时。显然,目前模型的候选者包括了书的流派(例如科幻小说,法国诗歌,等等)和语言(英语,法语,日语)。</p>
+
+<p>一旦我们决定了模型和域,我们需要考虑他们的关系。Django允许你定义一对一 (<code>OneToOneField</code>),一对多(<code>ForeignKey</code>)和多对多(<code>ManyToManyField</code>)的关系。(译者注:此处我们以关系型数据库为基准,如果采用NoSQL,如MangoDB则无法如此考虑)</p>
+
+<p>思考着以上内容,以下的UML关系表显示了我们在该例子里定义的模型。如上所述哦,我们已经为书籍(大概的细节),书籍实例(物理副本是否可获取状态)和作者创建了模型。我们也决定了为流派而创建的模型,所以变量值可以通过管理界面获取。此外,我们决定了不创建 <code>BookInstance:status</code>的模型——我们已经硬编码了这个值(<code>LOAN_STATUS</code>)因为我们不期望这些被改变。通过每个方框你可以看到模型名字,值域名和类型,还有方法和返回的类型。</p>
+
+<p>这个图表也显示了模型之间的关系, including their <em>multiplicities</em>. 这些在图表里每个模型边上的数字(最大和最小)显示了他们的关系。 例如,链接Book和Genre两个盒子的线表示它们是关联的。 靠近Book模型的数字显示一本书必须有一个或多个Genre(要多少有多少),然而另一端靠近Genre的数字显示了它可以有零或无数本相关的书籍。</p>
+
+<p><img alt="LocalLibrary Model UML - v3" src="https://mdn.mozillademos.org/files/14021/local_library_model_uml_v0_1.png" style="height: 660px; width: 937px;"></p>
+
+<div class="note">
+<p><strong>Note</strong>: 下一部分提供了基本的关于模型如何被定义和使用的解释。边阅读,边考虑以下我们是如何根据以上的图标构建数据库内的模型的。</p>
+</div>
+
+<h2 id="Model_primer">Model primer</h2>
+
+<p>This section provides a brief overview of how a model is defined and some of the more important fields and field arguments.</p>
+
+<h3 id="Model_definition">Model definition</h3>
+
+<p>Models are usually defined in an application's <strong>models.py</strong> file. They are implemented as subclasses of <code>django.db.models.Model</code>, and can include fields, methods and metadata. The code fragment below shows a "typical" model, named <code>MyModelName</code>:</p>
+
+<pre class="notranslate">from django.db import models
+
+class MyModelName(models.Model):
+    """
+    A typical class defining a model, derived from the Model class.
+    """
+
+  # Fields
+    my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")
+  ...
+
+  # Metadata
+ class Meta:
+  ordering = ["-my_field_name"]
+
+  # Methods
+    def get_absolute_url(self):
+         """
+         Returns the url to access a particular instance of MyModelName.
+         """
+         return reverse('model-detail-view', args=[str(self.id)])
+
+    def __str__(self):
+        """
+        String for representing the MyModelName object (in Admin site etc.)
+        """
+        return self.field_name</pre>
+
+<p>In the below sections we'll explore each of the features inside the model in detail:</p>
+
+<h4 id="域">域</h4>
+
+<p>一个模型可以有任意数量的域,或任意的类型——每个用一行呈现我们想存储进数据库的数据。让我们看一下以下的例子吧o(≧v≦)o:</p>
+
+<pre class="brush: js notranslate">my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")</pre>
+
+<p>上面的例子有一个单域,叫做my_field_name,类型是models.CharField——这意味着此域会包含着由字母组成的字符串们。域类型被特殊的class赋值,这确认了记录的类型是用来存入数据库的,以及当用户从HTML表格里提交值后,我们用来验证提交的值是否有效的条件。</p>
+
+<p><font><font><font><font>字段类型还可以获取参数,进一步指定字段如何存放或如何被使用。</font></font></font><font><font><font>在这里的情况下,我们给了字段两个参数:</font></font></font></font></p>
+
+<ul>
+ <li><code>max_length=20</code><font><font> <font><font>— 表示此字段中值的最大长度为20个字符的状态。</font></font></font></font></li>
+ <li><code>help_text="Enter field documentation"</code><font><font> <font><font>— 提供一个帮助用户的文本标签,让用户知道当前透过HTML表单输入时要提供什么值。</font></font></font></font></li>
+</ul>
+
+<p><font><font><font><font>字段名称用于在视图和模版中引用它。</font></font></font><font><font><font>字段还有一个标签,它被指定一个参数(</font></font></font></font><code>verbose_name</code><font><font><font><font>),或者通过大写字段的变量名的第一个字母,并用空格替换下划线(例如</font></font></font></font><code>my_field_name</code><font><font><font><font>的默认标签为My field name )。</font></font></font></font></p>
+
+<p><font><font><font><font>如果模型在表单中呈现(例如:在管理站点中),则声明该字段的顺序,将影响其默认顺序,但可能会被覆盖。</font></font></font></font></p>
+
+<h5 id="Common_field_arguments">Common field arguments</h5>
+
+<p>当声明很多/大多数不同的字段类型时,可以使用以下常用参数:</p>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#help-text" rel="noopener"><font><font><font><font>help_text</font></font></font></font></a><font><font> <font><font> :提供HTML表单文本标签(eg i在管理站点中),如上所述。</font></font></font></font></li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#verbose-name" rel="noopener"><font><font><font><font>verbose_name</font></font></font></font></a><font><font> <font><font> :字段标签中的可读性名称,如果没有被指定,Django将从字段名称推断默认的详细名称。</font></font></font></font></li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#default" rel="noopener"><font><font><font><font>default</font></font></font></font></a><font><font> <font><font> :该字段的默认值。</font></font></font><font><font><font>这可以是值或可呼叫物件(callable object),在这种情况下,每次创建新纪录时都将呼叫该物件。</font></font></font></font></li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#null" rel="noopener"><font><font><font><font>null</font></font></font></font></a><font><font><font><font>:如为</font></font><code>True</code><font><font>,即允许Django于资料库该栏位写入</font></font><code>NULL</code><font><font>(但栏位型态如为</font></font><code>CharField</code><font><font>则会写入空字串)。</font><font>预设值是</font></font><code>False</code><font><font>。</font></font></font></font></li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#blank" rel="noopener"><font><font><font><font>blank</font></font></font></font></a><font><font> <font><font> :如果</font></font></font></font><code><strong><font><font>True</font></font></strong></code><font><font><font><font>,表单中的字段被允许为空白。</font></font></font><font><font><font>默认是</font></font><code>False</code><font><font>,这意味着Django的表单验证将强制你输入一个值。</font></font></font><font><font><font>这通常搭配  </font></font><code>NULL=True</code><font><font> 使用,因为如果要允许空值,你还希望数据库能够适当地表示它们。</font></font></font></font></li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#choices" rel="noopener"><font><font><font><font>choices</font></font></font></font></a><font><font> <font><font> :这是给此字段的一组选项。</font></font></font><font><font><font>如果提供这一项,预设对应的表单部件是「该组选项的列表」,而不是原先的标准文本字段。</font></font></font></font></li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#primary-key" rel="noopener"><font><font><font><font>primary_key</font></font></font></font></a><font><font> <font><font> :如果是True,将当前字段设置为模型的主键(主键是被指定用来唯一辨识所有不同表记录的特殊数据库栏位(column))。</font></font></font><font><font><font>如果没有指定字段作为主键,则Django将自动为此添加一个字段。</font></font></font></font></li>
+</ul>
+
+<p><font><font><font><font>还有许多其他选项—你可以在</font></font></font></font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-options" rel="noopener"><font><font><font><font>这里看到完整的字段选项</font></font></font></font></a><font><font><font><font>。</font></font></font></font></p>
+
+<h5 id="Common_field_types">Common field types</h5>
+
+<p><font><font><font><font>以下列表描述了一些更常用的字段类型。</font></font></font></font></p>
+
+<ul>
+ <li><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.CharField" rel="noopener"><font><font>CharField</font></font></a> </font></font><font><font><font><font>是用来定义短到中等长度的字段字符串。</font></font></font><font><font><font>你必须指定</font></font><code>max_length</code><font><font>要存储的数据。</font></font></font></font></li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.TextField" rel="noopener"><font><font><font><font>TextField </font></font></font></font></a><font><font><font><font>用于大型任意长度的字符串。</font></font></font><font><font><font>你可以</font></font><code>max_length</code><font><font>为该字段指定一个字段,但仅当该字段以表单显示时才会使用(不会在数据库级别强制执行)。</font></font></font></font></li>
+ <li><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.IntegerField" rel="noopener" title="django.db.models.IntegerField"><font><font>IntegerField</font></font></a> </font></font><font><font><font><font>是一个用于存储整数(整数)值的字段,用于在表单中验证输入的值为整数。</font></font></font></font></li>
+ <li><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#datefield" rel="noopener"><font><font>DateField</font></font></a> </font></font><font><font><font><font>和</font></font></font></font><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#datetimefield" rel="noopener"><font><font>DateTimeField</font></font></a> </font></font><font><font><font><font>用于存储/表示日期和日期/时间信息(分别是</font></font><code>Python.datetime.date</code><font><font> 和  </font></font><code>datetime.datetime</code><font><font> 对象)。</font><font>这些字段可以另外表明(互斥)参数  </font></font><code>auto_now=Ture</code><font><font>(在每次保存模型时将该字段设置为当前日期),</font></font><code>auto_now_add</code><font><font>(仅设置模型首次创建时的日期)和  </font></font><code>default</code><font><font>(设置默认日期,可以被用户覆盖)。</font></font></font></font></li>
+ <li><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#emailfield" rel="noopener"><font><font>EmailField</font></font></a> </font></font><font><font><font><font>用于存储和验证电子邮件地址。</font></font></font></font></li>
+ <li><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#filefield" rel="noopener"><font><font>FileField</font></font></a> </font></font><font><font><font><font>和</font></font></font></font><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#imagefield" rel="noopener"><font><font>ImageField</font></font></a> </font></font><font><font><font><font>分别用于上传文件和图像(</font></font><code>ImageField</code><font><font> 只需添加上传的文件是图像的附加验证)。</font></font></font><font><font><font>这些参数用于定义上传文件的存储方式和位置。</font></font></font></font></li>
+ <li><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#autofield" rel="noopener"><font><font>AutoField</font></font></a> </font></font><font><font><font><font>是一种  </font></font></font></font><strong><font><font><font><font>IntegerField </font></font></font></font></strong><font><font><font><font>自动递增的特殊类型。</font></font></font><font><font><font>如果你没有明确指定一个主键,则此类型的主键将自动添加到模型中。</font></font></font></font></li>
+ <li><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey" rel="noopener"><font><font>ForeignKey</font></font></a> </font></font><font><font><font><font>用于指定与另一个数据库模型的一对多关系(例如,汽车有一个制造商,但制造商可以制作许多汽车)。</font></font></font><font><font><font>关系的“一”侧是包含密钥的模型。</font></font></font></font></li>
+ <li><font><font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#manytomanyfield" rel="noopener"><font><font>ManyToManyField</font></font></a> </font></font><font><font><font><font>用于指定</font></font></font></font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#manytomanyfield" rel="noopener"><font><font><font><font>多对多</font></font></font></font></a><font><font><font><font>关系(例如,一本书可以有几种类型,每种类型可以包含几本书)。</font></font></font><font><font><font>在我们的图书馆应用程序中,我们将非常类似地使用它们ForeignKeys,但是可以用更复杂的方式来描述组之间的关系。</font></font></font><font><font><font>这些具有参数  </font></font><code>on_delete</code><font><font> 来定义关联记录被删除时会发生什么(例如,值  </font></font><code>models.SET_NULL</code><font><font> 将简单地设置为值NULL )。</font></font></font></font></li>
+</ul>
+
+<p><font><font><font><font>还有许多其他类型的字段,包括不同类型数字的字段(大整数,小整数,浮点数),布林值,URLs,唯一ids和其他“时间相关”的信息(持续时间,时间等)。</font></font></font><font><font><font>你可以查阅</font></font></font></font><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types" rel="noopener"><font><font><font><font>完整列表</font></font></font></font></a><font><font> <font><font> .</font></font></font></font></p>
+
+<h4 id="元数据Metadata"><font><font><font><font>元数据(Metadata)</font></font></font></font></h4>
+
+<p><font><font>你可以通过宣告 class Meta 来宣告模型级别的元数据,如图所示:</font></font></p>
+
+<pre class="notranslate"><code>class Meta:
+ ordering = ['-my_field_name']
+</code></pre>
+
+<p><font><font><font><font>此元数据最有用的功能之一是控制在查询模型类型时返回之记录的默认排序。</font></font></font><font><font><font>你可以透过在</font></font></font></font><code>ordering</code><font><font><font><font>属性的字段名称列表中指定匹配顺序来执行此操作,如上所示。</font></font></font><font><font><font>排序将依赖字段的类型(字符串字段按字母顺序排序,而日期字段按时间顺序排序)。</font></font></font><font><font><font>如上所示,你可以使用减号(-)对字段名称进行前缀,以反转排序顺序。</font></font></font></font></p>
+
+<p><font><font><font><font>例如,如果我们选择依照此预设来排列书单:</font></font></font></font></p>
+
+<pre class="notranslate"><code>ordering = ['title', '-pubdate']</code></pre>
+
+<p><font><font><font><font>书单通过标题依据--字母排序--排列,从A到Z,然后再依每个标题的出版日期,从最新到最旧排列。</font></font></font></font></p>
+
+<p><font><font><font><font>另一个常见的属性是  </font></font></font></font><code>verbose_name</code><font><font><font><font>,一个  </font></font></font></font><code>verbose_name</code><font><font><font><font>说明单数和复数形式的类别。</font></font></font></font></p>
+
+<pre class="notranslate"><code>verbose_name = 'BetterName'</code></pre>
+
+<p><font><font><font><font>其他有用的属性允许你为模型创建和应用新的“访问权限”(预设权限会被自动套用),允许基于其他的字段排序,或声明该类是”抽象的“(你无法创建的记录基类,并将由其他型号派生)。</font></font></font></font></p>
+
+<p><font><font><font><font>许多其他元数据选项控制模型中必须使用哪些数据库以及数据的存储方式。</font></font></font><font><font><font>(如果你需要模型映射一个现有数据库,这会有用)。</font></font></font></font></p>
+
+<p><font><font><font><font>完整有用的元数据选项在这里</font></font></font></font><a href="https://docs.djangoproject.com/en/1.10/ref/models/options/" rel="noopener"><font><font><font><font>Model metadata options</font></font></font></font></a><font><font> <font><font> (Django docs).</font></font></font></font></p>
+
+<h4 id="方法Methods"><font><font><font><font>方法(Methods)</font></font></font></font></h4>
+
+<p><font><font><font><font>一个模型也可以有方法。</font></font></font></font></p>
+
+<p><strong><font><font><font><font>最起码,在每个模型中,你应该定义标准的Python类方法</font></font></font></font><code>__str__()</code> </strong><font><font><font><font>,</font></font><strong><font><font>来为每个物件返回一个人类可读的字符串</font></font></strong><font><font>。</font></font></font><font><font><font>此字符用于表示管理站点的各个记录(以及你需要引用模型实例的任何其他位置)。</font></font></font><font><font><font>通常这将返回模型中的标题或名称字段。</font></font></font></font></p>
+
+<pre class="notranslate"><code>def __str__(self):
+  return self.field_name</code></pre>
+
+<p><font><font><font>Django方法中另一个常用方法是</font></font></font> <code>get_absolute_url()</code> <font><font><font>,这函数返回一个在网站上显示个人模型记录的URL(如果你定义了该方法,那么Django将自动在“管理站点”中添加“在站点中查看“按钮在模型的记录编辑栏)。</font></font></font><code>get_absolute_url()</code><font><font><font>的典型示例如下:</font></font></font></p>
+
+<pre class="notranslate"><code>def get_absolute_url(self):
+ """Returns the url to access a particular instance of the model."""
+ return reverse('model-detail-view', args=[str(self.id)])
+</code></pre>
+
+<p><strong><font><font><font><font>注意</font></font></font></font></strong><font><font> <font><font> :假设你将使用URL </font></font></font></font><code>/myapplication/mymodelname/2</code> <font><font><font><font>来显示模型的单个记录(其中“2”是id特定记录),则需要创建一个URL映射器来将响应和id传递给“模型详细视图” (这将做出显示记录所需的工作)。</font></font></font><font><font><font>以上示例中,</font></font></font></font><code>reverse()</code><font><font><font><font>函数可以“反转”你的url映射器(在上诉命名为“model-detail-view”的案例中,以创建正确格式的URL。</font></font></font></font></p>
+
+<p><font><font><font><font>当然要做这个工作,你还是要写URL映射,视图和模版!</font></font></font></font></p>
+
+<p><font><font>你可以定义一些你喜欢的其他方法,并从你的代码或模版调用它们(只要它们不带任何参数)。</font></font></p>
+
+<h3 id="Model_management">Model management</h3>
+
+<p><font><font><font>一旦你定义了模型类,你可以使用它们来创建,更新或删除记录,并运行查询获取所有记录或特定的记录子集。</font></font></font><font><font><font>当我们定义我们的视图,我们将展示给你在这个教程如何去做。</font></font></font></p>
+
+<h4 id="创建和修改记录"><font><font><font><font>创建和修改记录</font></font></font></font></h4>
+
+<p><font><font>要创建一个记录,你可以定义一个模型实例,然后呼叫  </font></font><code>save()</code><font><font>。</font></font></p>
+
+<pre class="brush: python notranslate"># Create a new record using the model's constructor.
+a_record = MyModelName(my_field_name="Instance #1")
+
+# Save the object into the database.
+a_record.save()
+</pre>
+
+<div class="note">
+<p><strong><font><font>注:</font></font></strong><font><font>如果没有任何的栏位被宣告为</font></font><code>主鍵</code><font><font>,这笔新的纪录会被自动的赋予一个主键并将主键栏命名为</font></font><code>id</code><font><font>。</font><font>上例的那笔资料被储存后,试着查询这笔纪录会看到它被自动赋予1的编号。</font></font></p>
+</div>
+
+<p><font><font>你可以透过「点(dot)的语法」取得或变更这笔新资料的栏位(字段)。</font><font>你需要呼叫</font></font><code>save()</code><font><font>将变更过的资料存进资料库:</font></font></p>
+
+<pre class="brush: python notranslate"># Access model field values using Python attributes.
+print(a_record.id) #should return 1 for the first record.
+print(a_record.my_field_name) # should print 'Instance #1'
+
+# Change record by modifying the fields, then calling save().
+a_record.my_field_name="New Instance Name"
+a_record.save()</pre>
+
+<h4 id="搜寻纪录"><font><font>搜寻纪录</font></font></h4>
+
+<p><font><font>你可以使用模型的  </font></font><code>objects</code><font><font> 属性(由base class提供)搜寻符合某个条件的纪录</font></font></p>
+
+<div class="note">
+<p><strong>Note</strong>: <font><font>要用"抽象的"模型还有栏位说明怎么搜寻纪录可能会有点令人困惑。</font><font>我们会以一个Book模型,其包含</font></font><code>title</code><font><font>与</font></font><code>genre</code><font><font>字段,而genre也是一个仅有</font></font><code>name</code><font><font>一个字段的模型。</font></font></p>
+</div>
+
+<p><font><font>我们可以取得一个模型的所有纪录,为一个  </font></font><code>QuerySet</code><font><font> 使用</font></font><code>objects.all()。</code> <code>QuerySet</code><font><font> 是一个可迭代的物件,表示他含有多个物件,而我们可以藉由迭代/回圈取得每个物件。</font></font></p>
+
+<pre class="brush: python notranslate">all_books = Book.objects.all()
+</pre>
+
+<p><font><font>Django的  </font></font><code>filter()</code><font><font>方法让我们可以透过符合特定文字或数值的字段筛选回传的</font></font><code>QuerySet</code><font><font>。</font><font>例如筛选书名里有"wild"的书并且计算总数,如下面所示。</font></font></p>
+
+<pre class="brush: python notranslate">wild_books = Book.objects.filter(title__contains='wild')
+number_wild_books = Book.objects.filter(title__contains='wild').count()
+</pre>
+
+<p><font><font>要比对的字段与比对方法都要被定义在筛选的参数名称里,并且使用这个格式:</font></font><code>比對字段__比對方法</code><font><font> (请注意上方范例中的  </font></font><code>title</code><font><font> 与  </font></font><code>contains</code><font><font> 中间隔了两个底线唷)。</font><font>在上面我们使用大小写区分的方式比对</font></font><code>title</code><font><font>。</font><font>还有很多比对方式可以使用:  </font></font><code>icontains</code><font><font>(不区分大小写), </font></font><code>iexact</code><font><font>(大小写区分且完全符合), </font></font><code>exact</code><font><font>(不区分大小写但完全符合)还有  </font></font><code>in</code><font><font>, </font></font><code>gt</code><font><font>(大于), </font></font><code>startswith</code><font><font>,之类的。</font></font><a href="https://docs.djangoproject.com/en/2.0/ref/models/querysets/#field-lookups" rel="noopener"><font><font>全部的用法在这里。</font></font></a></p>
+
+<p><font><font>有时候你会须要透过某个一对多的字段来筛选(例如一个  </font></font><code>外鍵</code><font><font>)。</font><font>这样的状况下,你可以使用两个底线来指定相关模型的字段。</font><font>例如透过某个特定的genre名称筛选书籍,如下所示:</font></font></p>
+
+<pre class="brush: python notranslate">books_containing_genre = Book.objects.filter(genre<strong>__</strong>name<strong>__</strong>icontains='fiction') # Will match on: Fiction, Science fiction, non-fiction etc.
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 你可以用下划线来表示不同关系 (<code>ForeignKey</code>/<code>ManyToManyField</code>) .例如,一本书有不同的类型,用“cover“关系可能会帮助起一个参数名字 <code>type__cover__name__exact='hard'.</code></p>
+</div>
+
+<p><font><font>还有很多是你可以用索引(queries)来做的,包含从相关的模型做向后查询(backwards searches)、连锁过滤器(chaining filters)、回传「值的小集合」等。</font><font>更多资讯可以到  </font></font><a href="https://docs.djangoproject.com/en/2.0/topics/db/queries/" rel="noopener"><font><font>Making queries</font></font></a><font><font> (Django Docs)查询。</font></font></p>
+
+<h2 id="Defining_the_LocalLibrary_Models">Defining the LocalLibrary Models</h2>
+
+<p>In this section we will start defining the models for the library. Open <em>models.py (in /locallibrary/catalog/)</em>. The boilerplate at the top of the page imports the <em>models</em> module, which contains the model base class <code>models.Model</code> that our models will inherit from.</p>
+
+<pre class="brush: python notranslate">from django.db import models
+
+# Create your models here.</pre>
+
+<h3 id="Genre_model">Genre model</h3>
+
+<p>Copy the Genre model code shown below and paste it into the bottom of your <code>models.py</code> file. This model is used to store information about the book category — for example whether it is fiction or non-fiction, romance or military history, etc. As mentioned above, we've created the Genre as a model rather than as free text or a selection list so that the possible values can be managed through the database rather than being hard coded.</p>
+
+<pre class="brush: python notranslate">class Genre(models.Model):
+    """
+    Model representing a book genre (e.g. Science Fiction, Non Fiction).
+    """
+    name = models.CharField(max_length=200, help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)")
+
+    def __str__(self):
+        """
+        String for representing the Model object (in Admin site etc.)
+        """
+        return self.name</pre>
+
+<p>The model has a single <code>CharField</code> field (<code>name</code>), which is used to describe the genre (this is limited to 200 characters and has some <code>help_text</code>. At the end of the model we declare a <code>__str__()</code> method, which simply returns the name of the genre defined by a particular record. No verbose name has been defined, so the field will be called <code>Name</code> in forms.</p>
+
+<h3 id="Book_model">Book model</h3>
+
+<p>Copy the Book model below and again paste it into the bottom of your file. The book model represents all information about an available book in a general sense, but not a particular physical "instance" or "copy" available for loan. The model uses a <code>CharField</code> to represent the book's <code>title</code> and <code>isbn</code> (note how the <code>isbn</code> specifies its label as "ISBN" using the first unnamed parameter because the default label would otherwise be "Isbn"). The model uses <code>TextField</code> for the <code>summary</code>, because this text may need to be quite long.</p>
+
+<pre class="brush: python notranslate">from django.urls import reverse #Used to generate URLs by reversing the URL patterns
+
+class Book(models.Model):
+    """
+    Model representing a book (but not a specific copy of a book).
+    """
+    title = models.CharField(max_length=200)
+    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
+    # Foreign Key used because book can only have one author, but authors can have multiple books
+    # Author as a string rather than object because it hasn't been declared yet in the file.
+    summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
+    isbn = models.CharField('ISBN',max_length=13, help_text='13 Character &lt;a href="https://www.isbn-international.org/content/what-isbn"&gt;ISBN number&lt;/a&gt;')
+    genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
+    # ManyToManyField used because genre can contain many books. Books can cover many genres.
+    # Genre class has already been defined so we can specify the object above.
+
+    def __str__(self):
+        """
+        String for representing the Model object.
+        """
+        return self.title
+
+
+    def get_absolute_url(self):
+        """
+        Returns the url to access a particular book instance.
+        """
+        return reverse('book-detail', args=[str(self.id)])
+</pre>
+
+<p>The genre is a <code>ManyToManyField</code>, so that a book can have multiple genres and a genre can have many books. The author is declared as <code>ForeignKey</code>, so each book will only have one author, but an author may have many books (in practice a book might have multiple authors, but not in this implementation!)</p>
+
+<p>In both field types the related model class is declared as the first unnamed parameter using either the model class or a string containing the name of the related model. You must use the name of the model as a string if the associated class has not yet been defined in this file before it is referenced! The other parameters of interest in the <code>author</code> field are <code>null=True</code>, which allows the database to store a <code>Null</code> value if no author is selected, and <code>on_delete=models.SET_NULL</code>, which will set the value of the author to <code>Null</code> if the associated author record is deleted.</p>
+
+<p>The model also defines <code>__str__()</code> , using the book's <code>title</code> field to represent a <code>Book</code> record. The final method, <code>get_absolute_url()</code> returns a URL that can be used to access a detail record for this model (for this to work we will have to define a URL mapping that has the name <code>book-detail</code>, and define an associated view and template).</p>
+
+<h3 id="BookInstance_model">BookInstance model</h3>
+
+<p>Next, copy the <code>BookInstance</code> model (shown below) under the other models. The <code>BookInstance</code> represents a specific copy of a book that someone might borrow, and includes information about whether the copy is available or on what date it is expected back, "imprint" or version details, and a unique id for the book in the library.</p>
+
+<p>Some of the fields and methods will now be familiar. The model uses</p>
+
+<ul>
+ <li><code>ForeignKey</code> to identify the associated Book (each book can have many copies, but a copy can only have one <code>Book</code>).</li>
+ <li><code>CharField</code> to represent the imprint (specific release) of the book.</li>
+</ul>
+
+<pre class="brush: python notranslate">import uuid # Required for unique book instances
+
+class BookInstance(models.Model):
+ """
+ Model representing a specific copy of a book (i.e. that can be borrowed from the library).
+ """
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="Unique ID for this particular book across whole library")
+ book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True)
+ imprint = models.CharField(max_length=200)
+ due_back = models.DateField(null=True, blank=True)
+
+ LOAN_STATUS = (
+ ('m', 'Maintenance'),
+ ('o', 'On loan'),
+ ('a', 'Available'),
+ ('r', 'Reserved'),
+ )
+
+ status = models.CharField(max_length=1, choices=LOAN_STATUS, blank=True, default='m', help_text='Book availability')
+
+ class Meta:
+ ordering = ["due_back"]
+
+
+ def __str__(self):
+ """
+ String for representing the Model object
+ """
+ return '%s (%s)' % (self.id,self.book.title)</pre>
+
+<p>We additionally declare a few new types of field:</p>
+
+<ul>
+ <li><code>UUIDField</code> is used for the <code>id</code> field to set it as the <code>primary_key</code> for this model. This type of field allocates a globally unique value for each instance (one for every book you can find in the library).</li>
+ <li><code>DateField</code> is used for the <code>due_back</code> date (at which the book is expected to come available after being borrowed or in maintenance). This value can be <code>blank</code> or <code>null</code> (needed for when the book is available). The model metadata (<code>Class Meta</code>) uses this field to order records when they are returned in a query.</li>
+ <li><code>status</code> is a <code>CharField</code> that defines a choice/selection list. As you can see, we define a tuple containing tuples of key-value pairs and pass it to the choices argument. The value in a key/value pair is a display value that a user can select, while the keys are the values that are actually saved if the option is selected. We've also set a default value of 'm' (maintenance) as books will initially be created unavailable before they are stocked on the shelves.</li>
+</ul>
+
+<p>The model <code>__str__()</code> represents the <code>BookInstance</code> object using a combination of its unique id and the associated <code>Book</code>'s title.</p>
+
+<div class="note">
+<p><strong>Note</strong>: A little Python:</p>
+
+<ul>
+ <li>The value returned by <code>__str__()</code> is a <em>formatted string</em>. Within the string we use <code>%s</code> to declare "placeholders'. After the string we specify <code>%</code> and then a tuple containing the values to be inserted in the placeholders. If you just have one placeholder then you can omit the tuple — e.g. <code>'My value: %s' % variable.</code><br>
+ <br>
+ Note also that although this approach is perfectly valid, please be aware that it is no longer prefered. Since Python 3 you should instead use the format method, eg. '{0} ({1})'.format(self.id,self.book.title). You can read more about it <a href="https://www.python.org/dev/peps/pep-3101/">here</a>.</li>
+</ul>
+</div>
+
+<h3 id="Author_model">Author model</h3>
+
+<p>Copy the <code>Author</code> model (shown below) underneath the existing code in <strong>models.py</strong>.</p>
+
+<p>All of the fields/methods should now be familiar. The model defines an author as having a first name, last name, date of birth, and (optional) date of death. It specifies that by default the <code>__str__()</code> returns the name in <em>last name</em>, <em>firstname </em>order. The <code>get_absolute_url()</code> method reverses the <code>author-detail</code> URL mapping to get the URL for displaying an individual author.</p>
+
+<pre class="brush: python notranslate">class Author(models.Model):
+    """
+    Model representing an author.
+    """
+    first_name = models.CharField(max_length=100)
+    last_name = models.CharField(max_length=100)
+    date_of_birth = models.DateField(null=True, blank=True)
+    date_of_death = models.DateField('Died', null=True, blank=True)
+
+    def get_absolute_url(self):
+        """
+        Returns the url to access a particular author instance.
+        """
+        return reverse('author-detail', args=[str(self.id)])
+
+
+    def __str__(self):
+        """
+        String for representing the Model object.
+        """
+        return '%s, %s' % (self.last_name, self.first_name)
+</pre>
+
+<h2 id="Re-run_the_database_migrations">Re-run the database migrations</h2>
+
+<p>All your models have now been created. Now re-run your database migrations to add them to your database.</p>
+
+<pre class="notranslate"><code>python3 manage.py makemigrations
+python3 manage.py migrate</code></pre>
+
+<h2 id="Language_model_—_challenge">Language model — challenge</h2>
+
+<p>Imagine a local benefactor donates a number of new books written in another language (say, Farsi). The challenge is to work out how these would be best represented in our library website, and then to add them to the models.</p>
+
+<p>Some things to consider:</p>
+
+<ul>
+ <li>Should "language" be associated with a <code>Book</code>, <code>BookInstance</code>, or some other object?</li>
+ <li>Should the different languages be represented using model, a free text field, or a hard-coded selection list?</li>
+</ul>
+
+<p>After you've decided, add the field. You can see what we decided on Github <a href="https://github.com/mdn/django-locallibrary-tutorial/blob/master/catalog/models.py">here</a>.</p>
+
+<ul>
+</ul>
+
+<ul>
+</ul>
+
+<h2 id="Summary">Summary</h2>
+
+<p>In this article we've learned how models are defined, and then used this information to design and implement appropriate models for the <em>LocalLibrary</em> website.</p>
+
+<p>At this point we'll divert briefly from creating the site, and check out the <em>Django Administration site</em>. This site will allow us to add some data to the library, which we can then display using our (yet to be created) views and templates.</p>
+
+<h2 id="See_also">See also</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial02/">Writing your first Django app, part 2</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/db/queries/">Making queries</a> (Django Docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/querysets/">QuerySet API Reference</a> (Django Docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}</p>
diff --git a/files/zh-cn/learn/server-side/django/sessions/index.html b/files/zh-cn/learn/server-side/django/sessions/index.html
new file mode 100644
index 0000000000..d6b95b0157
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/sessions/index.html
@@ -0,0 +1,190 @@
+---
+title: 'Django 教程 7: 会话框架'
+slug: learn/Server-side/Django/Sessions
+translation_of: Learn/Server-side/Django/Sessions
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django")}}</div>
+
+<p class="summary" style='font-style: normal; margin: 0px 0px 20px; padding: 20px 0px; border-width: 3px 0px; border-style: solid; border-color: rgb(131, 208, 242); max-width: 42rem; font-size: 1.25rem; color: rgb(51, 51, 51); font-family: "Open Sans", arial, x-locale-body, sans-serif; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-style: initial;'><font><font>本教程扩展了我们的</font></font><a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website" style='font-style: normal; color: rgb(63, 135, 166); margin: 0px; padding: 0px; border: 0px; text-decoration: none; font-family: "Open Sans", arial, x-locale-body, sans-serif; font-size: 20px; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 255, 255);'><font><font>LocalLibrary</font></font></a><font><font>网站,为主页添加了一个基于会话的访问计数器。</font><font>这是一个相对简单的例子,但它确实显示了,如何使用会话框架为匿名用户提供持久的行为。</font></font></p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>完成之前的所有教程主题,包括<a href="/zh-CN/docs/Learn/Server-side/Django/Generic_views">Django教程6:通用列表和详细信息视图</a></td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>了解会话的使用方式。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概览">概览</h2>
+
+<p>我们在之前的教程中创建的<a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a>网站,允许用户浏览目录中的书本和作者。虽然内容是从数据库动态生成的,但每个用户在使用站点时,基本上都可以访问相同的页面,和相同类型的信息。</p>
+
+<p>在一个 “真实” 的图书馆中,您可能希望根据用户之前对站点的使用,首选项等,为个人用户提供自定义体验。例如,您可以在用户下次访问时,隐藏上次已经确认的警告消息。网站,或存储和尊重他们的偏好(例如,他们希望在每个页面上显示的搜索结果的数量)。</p>
+
+<p><br>
+ 会话框架允许您实现此类行为,允许您基于每个站点访问者,以储存和检索任意数据。</p>
+
+<h2 id="会话是什么?">会话是什么?</h2>
+
+<p>Web浏览器和服务器之间的所有通信,都是通过HTTP协议进行的,该协议是无状态的。协议无状态的事实,意味着客户端和服务器之间的消息,完全相互独立 - 没有基于先前消息的“序列”或行为的概念。因此,如果您想拥有一个追踪与客户的持续关系的网站,您需要自己实现。</p>
+
+<p>会话是Django(以及大多数Internet)用于跟踪站点和特定浏览器之间“状态”的机制。会话允许您为每个浏览器存储任意数据,并在浏览器连接时,将该数据提供给站点。然后,通过“密钥”引用与会话相关联的各个数据项,“密钥”用于存储和检索数据。</p>
+
+<p>Django使用包含特殊会话ID的cookie,来识别每个浏览器,及其与该站点的关联会话。默认情况下,实际会话数据存储在站点数据库中(这比将数据存储在cookie中更安全,因为它们更容易受到恶意用户的攻击)。您可以将Django配置为,将会话数据存储在其他位置(缓存,文件,“安全”cookie),但默认位置是一个良好且相对安全的选项。</p>
+
+<h2 id="启用会话">启用会话</h2>
+
+<p>我们<a href="/zh-CN/docs/Learn/Server-side/Django/skeleton_website">创建骨架网站</a>时,会自动启用会话(在教程2中)。</p>
+
+<p>配置在项目文件(<strong>locallibrary / locallibrary / settings.py</strong>)的<code>INSTALLED_APPS</code> 和 <code>MIDDLEWARE</code> 部分中设置,如下所示:</p>
+
+<pre class="brush: python">INSTALLED_APPS = [
+ ...
+<strong> 'django.contrib.sessions',</strong>
+ ....
+
+MIDDLEWARE = [
+ ...
+<strong> 'django.contrib.sessions.middleware.SessionMiddleware',</strong>
+ ....</pre>
+
+<h2 id="使用会话">使用会话</h2>
+
+<p>您可以从<code>request</code>请求参数访问视图中的<code>session</code>会话属性(作为视图的第一个参数传入的<code>HttpRequest</code>)。此会话属性,表示与当前用户的特定连接(或者更确切地说,是与当前浏览器的连接,由此站点的浏览器cookie中的会话ID标识)。</p>
+
+<p>会话<code>session</code>属性是一个类似字典的对象,您可以在视图中多次读取和写入,并根据需要进行修改。您可以执行所有常规的字典操作,包括清除所有数据,测试是否存在密钥,循环数据等。大多数情况下,您只需使用标准 “字典” API,来获取和设置值。</p>
+
+<p>下面的代码片段,显示了如何使用与当前会话(浏览器)关联的密钥“<code>my_car</code>”来获取,设置和删除某些数据。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 关于Django的一个好处是,你不需要考虑在你的视图中,将会话与当前请求联系起来的机制。如果我们在视图中,使用下面的片段,我们就知道有关<code>my_car</code>的信息,仅与发送当前请求的浏览器相关联。</p>
+</div>
+
+<pre class="brush: python"># Get a session value by its key (e.g. 'my_car'), raising a KeyError if the key is not present
+my_car = request.session['my_car']
+
+# Get a session value, setting a default if it is not present ('mini')
+my_car = request.session.get('my_car', 'mini')
+
+# Set a session value
+request.session['my_car'] = 'mini'
+
+# Delete a session value
+del request.session['my_car']
+</pre>
+
+<p>API还提供了许多其他方法,主要用于管理关联的会话cookie。例如,有一些方法,可以测试客户端浏览器,是否支持cookie,设置和检查cookie过期日期,以及从数据存储中清除过期的会话。您可以在<a href="https://docs.djangoproject.com/en/2.0/topics/http/sessions/">如何使用会话</a>(Django文档)中找到完整的API。</p>
+
+<h2 id="保存会话数据">保存会话数据</h2>
+
+<p>默认情况下,Django仅保存到会话数据库,并在会话被修改(分配)或删除时,将会话cookie发送到客户端。如果您使用会话密钥更新某些数据,如上一节所示,那么您无需担心这一点!例如:</p>
+
+<pre class="brush: python"># This is detected as an update to the session, so session data is saved.
+request.session['my_car'] = 'mini'</pre>
+
+<p>如果您正在更新会话数据中的某些信息,那么Django将无法识别您已对会话进行了更改并保存了数据(例如,如果您要更改“<code>my_car</code>”数据中的“轮子”<code>wheels</code>数据,如下所示)。在这种情况下,您需要将会话明确标记为已修改。</p>
+
+<pre class="brush: python"># Session object not directly modified, only data within the session. Session changes not saved!
+request.session['my_car']['wheels'] = 'alloy'
+
+# Set session as modified to force data updates/cookie to be saved.
+<code>request.session.modified = True</code>
+</pre>
+
+<div class="note">
+<p><strong>注意</strong>: 您可以通过将<code>SESSION_SAVE_EVERY_REQUEST = True</code>添加到项目设置(<strong>locallibrary/locallibrary/settings.py</strong>),以更改站点行为,站点将在每个请求上更新数据库/发送cookie。</p>
+</div>
+
+<h2 id="简单的例子_-_获取访问次数">简单的例子 - 获取访问次数</h2>
+
+<p>作为一个简单的现实世界的例子,我们将更新我们的图书馆,告诉当前用户,他们访问 LocalLibrary 主页的次数。</p>
+
+<p>打开<strong>/locallibrary/catalog/views.py</strong>,并在下面以粗体显示更改。</p>
+
+<pre class="brush: python">def index(request):
+ ...
+
+ num_authors=Author.objects.count() # The 'all()' is implied by default.
+
+<strong> # Number of visits to this view, as counted in the session variable.
+ num_visits=request.session.get('num_visits', 0)
+ request.session['num_visits'] = num_visits+1</strong>
+
+ # Render the HTML template index.html with the data in the context variable.
+ return render(
+ request,
+ 'index.html',
+<strong> context={'num_books':num_books,'num_instances':num_instances,'num_instances_available':num_instances_available,'num_authors':num_authors,
+ 'num_visits':num_visits}, # num_visits appended</strong>
+ )</pre>
+
+<p>这里,我们首先得到'<code>num_visits</code>'会话密钥的值,如果之前没有设置,则将值设置为0。每次收到请求时,我们都会递增该值,并将其存回会话中(下次用户访问该页面时)。然后将<code>num_visits</code>变量,传递给上下文变量中的模板。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 我们也可能会测试浏览器中是否支持cookie(请参阅<a href="https://docs.djangoproject.com/en/2.0/topics/http/sessions/">如何使用会话</a>作为示例),或设计我们的UI,以便无论cookie是否受支持都无关紧要。</p>
+</div>
+
+<p>将以下区块底部那一行,添加到主HTML模板(<strong>/locallibrary/catalog/templates/index.html</strong>)的 “动态内容” 部分底部,以显示上下文变量:</p>
+
+<pre class="brush: html">&lt;h2&gt;Dynamic content&lt;/h2&gt;
+
+&lt;p&gt;The library has the following record counts:&lt;/p&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;strong&gt;Books:&lt;/strong&gt; \{{ num_books }}&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;Copies:&lt;/strong&gt; \{{ num_instances }}&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;Copies available:&lt;/strong&gt; \{{ num_instances_available }}&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;Authors:&lt;/strong&gt; \{{ num_authors }}&lt;/li&gt;
+&lt;/ul&gt;
+
+<strong>&lt;p&gt;You have visited this page \{{ num_visits }}{% if num_visits == 1 %} time{% else %} times{% endif %}.&lt;/p&gt;</strong>
+</pre>
+
+<p>保存更改,并重新启动测试服务器。每次刷新页面时,数字都应该更新。</p>
+
+<ul>
+</ul>
+
+<h2 id="总结">总结</h2>
+
+<p>你现在知道,使用sessions 改善与匿名使用者的互动,有多么容易了。</p>
+
+<p>在我们的下一篇文章,我们将解释授权与许可框架,并演示如何支持使用者帐户。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/http/sessions/">如何使用会话</a> (Django 文档)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django/Authentication", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="本教程">本教程</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Introduction">Django 介绍</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/development_environment">架设 Django 开发环境</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django 教程: The Local Library website</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/skeleton_website">Django 教程 2: Creating a skeleton website</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Models">Django 教程 3: Using models</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Admin_site">Django 教程 4: Django admin site</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Home_page">Django 教程 5: Creating our home page</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Generic_views">Django 教程 6: Generic list and detail views</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Sessions">Django 教程 7: Sessions framework</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Authentication">Django 教程 8: User authentication and permissions</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Forms">Django 教程 9: Working with forms</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Testing">Django 教程 10: Testing a Django web application</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/Deployment">Django 教程 11: Deploying Django to production</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/web_application_security">Django 网络应用安全</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django 微博</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/django/skeleton_website/index.html b/files/zh-cn/learn/server-side/django/skeleton_website/index.html
new file mode 100644
index 0000000000..7b02df197e
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/skeleton_website/index.html
@@ -0,0 +1,372 @@
+---
+title: 'Django Tutorial Part 2: 创建网站的地基'
+slug: learn/Server-side/Django/skeleton_website
+translation_of: Learn/Server-side/Django/skeleton_website
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}</div>
+
+<p class="summary"><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django教程</a>的第二篇文章会展示怎样创建一个网站的"框架",在这个框架的基础上,你可以继续填充整站使用的settings, urls,模型(models),视图(views)和模板(templates)。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td><a href="/en-US/docs/Learn/Server-side/Django/development_environment">创建Django的开发环境</a>。复习 <a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django教程</a>。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>能够使用Django提供的工具包搭建你自己的网站工程。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概述">概述</h2>
+
+<p>这篇文章会展示怎样创建一个网站的"框架",在这个框架的基础上,你可以继续填充整站使用的settings, urls,模型(models),视图(views)和模板(templates)(我们会在接下来的文章里讨论)。</p>
+
+<p>搭建“框架”的过程很直接:</p>
+
+<ol>
+ <li>使用django-admin工具创建工程的文件夹,基本的文件模板和工程管理脚本(<strong>manage.py</strong>)。</li>
+ <li><span style="line-height: 1.5;">用</span><strong style="line-height: 1.5;">manage.py</strong><span style="line-height: 1.5;"> 创建一个或多个应用。</span>
+ <div class="note">
+ <p><span style="font-size: 14px;"><strong>注意:</strong>一个网站可能由多个部分组成,比如,主要页面,博客,wiki,下载区域等。Django鼓励将这些部分作为分开的应用开发。如果这样的话,在需要可以在不同的工程中复用这些应用。</span></p>
+ </div>
+ </li>
+ <li> 在工程里注册新的应用。</li>
+ <li>为每个应用分配url。</li>
+</ol>
+
+<p>为 <a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">locallibrary</a> 这个项目创建的网站文件夹和它的工程文件夹都命名为<em>locallibrary</em>。我们只创建一个名为<em>catalog</em>的应用。最高层的项目文件结构如下所示:</p>
+
+<pre class="brush: bash"><em>locallibrary/ # 网站文件夹</em>
+  <strong>manage.py </strong># 用来运行Django工具的脚本(由django-admin创建)
+  <em>locallibrary/ # 网站/项目文件夹(由django-admin创建)</em>
+ <em>catalog/ # 应用文件夹 </em>(由manage.py创建)</pre>
+
+<p>接下来的部分会详细讨论创建网站框架的过程,并会展示怎么测试这些变化。最后,我们会讨论在这个阶段里你可以设置的整站级的配置。</p>
+
+<h2 id="创建项目">创建项目</h2>
+
+<p>首先打开命令行工具,进入你想要创建Django应用的地方(最好是你容易找到的地方),为新网站创建一个文件夹(这里是:<em>locallibrary</em>)。用cd命令进入文件夹:</p>
+
+<pre class="brush: bash">mkdir locallibrary
+cd locallibrary</pre>
+
+<p>用<code>django-admin startproject</code>命令创建新项目,并进入该文件夹。</p>
+
+<pre class="brush: bash">django-admin startproject locallibrary
+cd locallibrary</pre>
+
+<p><code>django-admin</code>工具会创建如下所示的文件夹结构</p>
+
+<pre class="brush: bash"><em>locallibrary/</em>
+  <strong>manage.py</strong>
+  <em>locallibrary/</em>
+    settings.py
+    urls.py
+    wsgi.py</pre>
+
+<p>locallibrary项目的子文件夹是整个网站的进入点:</p>
+
+<ul>
+ <li><strong>settings.py</strong> 包含所有的网站设置。这是可以注册所有创建的应用的地方,也是静态文件,数据库配置的地方,等等。</li>
+ <li><strong>urls.py </strong>定义了网站url到view的映射<strong>。</strong>虽然这里可以包含所有的url,但是更常见的做法是把应用相关的url包含在相关应用中,你可以在接下来的教程里看到。</li>
+ <li><strong style="line-height: 1.5;">wsgi.py</strong><span style="line-height: 1.5;"> 帮助Django应用和网络服务器间的通讯。你可以把这个当作模板。</span></li>
+</ul>
+
+<p><strong>manage.py</strong>脚本可以创建应用,和数据库通讯,启动开发用网络服务器。</p>
+
+<h2 id="创建catalog应用">创建catalog应用</h2>
+
+<p>接下来,在locallibrary项目里,使用下面的命令创建catalog应用(和您项目的<strong>manage.py</strong>在同一个文件夹下)</p>
+
+<pre class="brush: bash">python3 manage.py startapp catalog</pre>
+
+<div class="note">
+<p><span style="font-size: 14px;"><strong>注意:</strong>Linux/Mac OS X应用可以使用上面的命令。在windows平台下应该改为:</span> <code>py -3 manage.py startapp catalog</code></p>
+
+<p>如果你是windows系统,在这个部分用<code>py -3</code> 替代<code>python3</code>。</p>
+</div>
+
+<p>这个工具创建了一个新的文件夹,并为该应用创建了不同的文件(下面黑体所示)。绝大多数文件的命令和它们的目的有关(比如视图函数就是<strong>views.py,</strong>模型就是<strong>models.py,</strong>测试是<strong>tests.py,</strong>网站管理设置是<strong>admin.py,</strong>注册应用是<strong>apps.py)</strong>,并且还包含了为项目所用的最小模板。</p>
+
+<p>执行命令后的文件夹结构如下所示:</p>
+
+<pre class="brush: bash"><em>locallibrary/</em>
+  manage.py
+  <em>locallibrary/
+</em><strong>  <em>catalog/</em>
+      admin.py
+      apps.py
+      models.py
+      tests.py
+      views.py
+      __init__.py
+  <em>migrations/</em></strong></pre>
+
+<p>除上面所说的文件外,我们还有:</p>
+
+<ul>
+ <li>一个<em>migration</em>文件夹,用来存储“migrations”——当你修改你的数据模型时,这个文件会自动升级你的数据库。</li>
+ <li><strong>__init__.py</strong> — 一个空文件,Django/Python会将这个文件作为<a href="https://docs.python.org/3/tutorial/modules.html#packages">Python 包</a>并允许你在项目的其他部分使用它。</li>
+</ul>
+
+<div class="note">
+<p><span style="font-size: 14px;"><strong>注意</strong></span>: 你注意到上面的文件里有些缺失嘛? 尽管由views和models的文件,可是url映射,网站模板,静态文件在哪里呢?我们会在接下来的部分展示如何创建它们(并不是每个网站都需要,不过这个例子需要)</p>
+</div>
+
+<h2 id="注册catalog应用">注册catalog应用</h2>
+
+<p>既然应用已经创建好了,我们还必须在项目里注册它,以便工具在运行时它会包括在里面(比如在数据库里添加模型时)。在项目的settings里,把应用添加进<code>INSTALLED_APPS</code> ,就完成了注册。</p>
+
+<p>打开项目设置文件 <strong>locallibrary/locallibrary/settings.py</strong> 找到  <code>INSTALLED_APPS</code> 列表里的定义。 如下所示,在列表的最后添加新的一行。</p>
+
+<pre class="brush: bash">INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+<strong> 'catalog.apps.CatalogConfig', </strong>
+]</pre>
+
+<p>新的这行详细说明了应用配置文件在 (<code>CatalogConfig</code>) <strong>/locallibrary/catalog/apps.py</strong> 里,当你创建应用时就完成了这个过程。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 注意到<code>INSTALLED_APPS已经有许多其他的应用了</code> (还有 <code>MIDDLEWARE</code>, 在settings的下面)。这些应用为  <a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django administration site</a> 提供了支持和许多功能(包括会话,认证系统等)。</p>
+</div>
+
+<h2 id="配置数据库">配置数据库</h2>
+
+<p>现在可以为项目配置数据库了——为了避免性能上的差异,最好在生产和开发中使用同一种数据库。你可以在<a href="https://docs.djangoproject.com/en/1.10/ref/settings/#databases">数据库</a> 里找到不同的设置方法(Django文档)。 </p>
+
+<p>在这个项目里,我们使用SQLite。因为在展示用的数据库中,我们不会有很多并发存取的行为。同时,也因为SQLite不需要额外的配置工作。你可以在<strong>settings.py</strong>里看到这个数据库怎样配置的。(更多信息如下所示)</p>
+
+<pre class="brush: python">DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+ }
+}
+</pre>
+
+<p>因为我们使用SQLite,不需要其他的设置了。我们继续吧!</p>
+
+<h2 id="其他项目设置">其他项目设置</h2>
+
+<p>settings.py里还包括其他的一些设置,现在只需要改变<a href="https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-TIME_ZONE">时区</a> — 改为和 标准<a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">tz时区数据表</a> 里的字符串相同就可以了(数据表里的TZ 列有你想要的时区)。 把<code>TIME_ZONE</code>的值改为你的时区,比如</p>
+
+<pre class="brush: python">TIME_ZONE = 'Asia/Shanghai'</pre>
+
+<p>有两个设置你现在不会用到,不过你应该留意:</p>
+
+<ul>
+ <li><code>SECRET_KEY</code>. 这个密匙值是Django网站安全策略的一部分。如果在开发环境中没有包好这个密匙,把代码投入生产环境时最好用不同的密匙代替。(可能从环境变量或文件中读取)。</li>
+ <li><code>DEBUG</code>. 这个会在debug日志里输出错误信息,而不是输入HTTP的返回码。在生产环境中,它应设置为false,因为输出的错误信息会帮助想要攻击网站的人。</li>
+</ul>
+
+<h2 id="链接URL映射器">链接URL映射器</h2>
+
+<p>在项目文件夹里,创建网站时同时生成了URL映射器(<strong>urls.py</strong>)。尽管你可以用它来管理所有的URL映射,但是更常用的做法是把URL映射留到它们相关的应用中。</p>
+
+<p>打开<strong>locallibrary/locallibrary/urls.py</strong> 并注意指导文字解释了一些使用URL映射器的方法。</p>
+
+<pre><code>"""locallibrary URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.0/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path
+
+urlpatterns = [
+    path('admin/', admin.site.urls),
+]</code></pre>
+
+<p> </p>
+
+<p><span style="letter-spacing: -0.00333rem;">URL 映射通过</span><code style="font-style: normal; letter-spacing: -0.00333rem;">urlpatterns</code><span style="letter-spacing: -0.00333rem;"> 变量管理,它是</span><code style="font-style: normal; letter-spacing: -0.00333rem;">path()</code><span style="letter-spacing: -0.00333rem;"> 函数的一个Python列表结构。 每个</span><code style="font-style: normal; letter-spacing: -0.00333rem;">path()</code><span style="letter-spacing: -0.00333rem;">函数要么将URL式样(URL pattern)关联到特定视图(</span><em>specific view)</em><span style="letter-spacing: -0.00333rem;">,将在模式匹配时显示;要么关联到某个URL式样列表的测试代码。 (第二种情况下,URL式样是目标模型里的“base URL”). </span><code style="font-style: normal; letter-spacing: -0.00333rem;">urlpatterns</code><span style="letter-spacing: -0.00333rem;"> 列表最开始定义了一个函数,这个函数将所有带有模型 </span><em>admin/</em><span style="letter-spacing: -0.00333rem;"> 的URL映射到模块</span><code style="font-style: normal; letter-spacing: -0.00333rem;">admin.site.urls</code><span style="letter-spacing: -0.00333rem;">。这个函数包含了Administration 应用自己的URL映射定义。</span></p>
+
+<div class="note">
+<p>注意:path() 中的路由是一个字符串,用于定义要匹配的URL模式。该字符串可能包括一个命名变量(尖括号中)</p>
+
+<p>例:<code>'catalog/&lt;id&gt;/'</code>。此模式将匹配如 <strong>/catalog/<em>any_chars</em>/</strong> 的URL ,并将 any_chars 作为具有参数名称 <code>id</code> 的字符串传递给视图。我们将在后面的主题中进一步讨论路径方法和路由模式</p>
+</div>
+
+<p>将下面的行添加到文件的底部,以便将新的项添加到 <code>urlpatterns</code> 列表中。这个新项目包括一个 <code>path()</code> ,它将带有 <code>catalog/</code> 的请求转发到模块 <code>catalog.urls</code> (使用相对路径 URL <strong>/catalog/urls.py</strong>)。</p>
+
+<pre><code># Use include() to add paths from the catalog application
+from django.conf.urls import include
+from django.urls import path
+
+urlpatterns += [
+    path('catalog/', include('catalog.urls')),
+]</code></pre>
+
+<p>现在让我们把网站的根URL(例:<code>127.0.0.1:8000</code>)重定向到该URL:<code>127.0.0.1:8000/catalog/</code>; 这是我们将在这个项目中使用的唯一应用程序,所以我们最好这样做。为了完成这个目标,我们将使用一个特殊的视图函数(<code>RedirectView</code>), 当在 <code>path()</code> 函数中指定的URL模式匹配时(在这个例子中是根URL),它将新的相对URL作为其第一个参数重定向到(<code>/catalog/</code>)。</p>
+
+<p>将以下行再次添加到文件的底部:</p>
+
+<pre><code>#Add URL maps to redirect the base URL to our application
+from django.views.generic import RedirectView
+urlpatterns += [
+ path('', RedirectView.as_view(url='/catalog/')),
+]</code></pre>
+
+<p>将路径函数的第一个参数留空以表示'/'。如果你将第一个参数写为'/',Django会在你启动服务器时给出以下警告:</p>
+
+<p> </p>
+
+<p> </p>
+
+<p> </p>
+
+<pre><code>System check identified some issues:
+
+WARNINGS:
+?: (urls.W002) Your URL pattern '/' has a route beginning with a '/'.
+Remove this slash as it is unnecessary.
+If this pattern is targeted in an include(), ensure the include() pattern has a trailing '/'.</code></pre>
+
+<p> </p>
+
+<p>Django 默认不提供CSS, JavaScript, 和图片等静态文件 。但是当你在开发环境中开发时,这些静态文件也很有用。作为对这个URL映射器的最后一项添加,你可以通过添加以下行在开发期间启用静态文件的服务。</p>
+
+<p>把下面的代码加到文件最后:</p>
+
+<p> </p>
+
+<pre><code># Use static() to add url mapping to serve static files during development (only)
+from django.conf import settings
+from django.conf.urls.static import static
+
+urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)</code></pre>
+
+<p> </p>
+
+<div class="note">
+<p><strong>注意</strong>: 有很多方法扩展<code>urlpatterns</code> 列表(在上面的代码里我们通过 <code>+=</code> 运算符来区分新旧代码)。我们同样可以用原先列表的定义:</p>
+
+<pre>urlpatterns = [
+  path('admin/', admin.site.urls),
+  path('catalog/', include('catalog.urls')),
+ path('', RedirectView.as_view(url='/catalog/', permanent=True)),
+] + <code>static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)</code></pre>
+
+<p> </p>
+
+<p><span style="font-size: 1.2rem; letter-spacing: -0.00333rem;">除此以外,我们也可以包含import代码行 (</span><code style="font-style: normal; letter-spacing: -0.00333rem;">from django.conf.urls import include</code><span style="font-size: 1.2rem; letter-spacing: -0.00333rem;">) ,这样更容易看出我们添加的代码,通常我们把import代码行放在Python文件的开头。</span></p>
+</div>
+
+<p>最后,在catalog 文件夹下创建一个名为 <strong>urls.py </strong>的文件,并添加以下文本以定义导入(空)的 <code>urlpatterns</code>。这是我们在编写应用时添加式样的地方。</p>
+
+<pre><code>from django.urls import path
+from catalog import views
+
+urlpatterns = [
+
+]</code></pre>
+
+<h2 id="测试网站框架">测试网站框架</h2>
+
+<p>现在我们有了一个完整的框架项目。这个网站现在还什么都不能做,但是我们仍然要运行以下,以确保我们的更改是有效的。</p>
+
+<p>在运行前,我们应该向运行<em>数据库迁移</em>。这会更新我们的数据库并且包含所有安装的应用(同时去除一些警告)。</p>
+
+<h3 id="运行数据库迁移">运行数据库迁移</h3>
+
+<p>Django 使用对象关系映射器(ORM)将Django代码中的模型定义映射到底层数据库使用的数据结构。当我们更改模型定义时,Django会跟踪更改并创建数据库迁移脚本 (in <strong>/locallibrary/catalog/migrations/</strong>) 来自动迁移数据库中的底层数据结构来</p>
+
+<p>当我们创建网站时,Django会自动添加一些模型供网站的管理部分使用(稍后我们会解释)。运行以下命令来定义数据库中这些模型的表(确保你位于包含<strong> manage.py 的目录中</strong>):</p>
+
+<pre class="brush: bash">python3 manage.py makemigrations
+python3 manage.py migrate
+</pre>
+
+<div class="warning">
+<p><strong>重要信息</strong>: 每次模型改变,都需要运行以上命令,来影响需要存储的数据结构(包括添加和删除整个模型和单个字段)。</p>
+</div>
+
+<p>该 <strong><code>makemigrations</code></strong> 命令创建(但不适用)项目中安装的所有应用程序的迁移(你可以指定应用程序名称,也可以为单个项目运行迁移)。这让你有机会在应用这些迁移之前检查这些迁移代码—当你是Django专家时,你可以选择稍微调整它们。</p>
+
+<p>这 <strong><code>migrate</code></strong> 命令 明确应用迁移你的数据库(Django跟踪哪些已添加到当前数据库)。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 看 <a href="https://docs.djangoproject.com/en/1.10/topics/migrations/">Migrations</a> (Django docs) ,了解较少使用的迁移命令的其他信息。</p>
+</div>
+
+<h3 id="运行网站">运行网站</h3>
+
+<p>在开发期间,你首先要使用开发网络服务器和浏览你本机的浏览器,来测试你的网站。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 这个开发网络服务器并不够强大以及不足以用于生产使用,但是它能非常容易得使你在开发期间,获得你的Django网站和运行它,以此来进行快速测试。<br>
+ 默认情况下,服务器会开通(http://127.0.0.1:8000/),但你也可以选择其他端口。有关更多信息,查阅( <a href="https://docs.djangoproject.com/en/1.10/ref/django-admin/#runserver">django-admin and manage.py: runserver</a> )(Django docs).</p>
+</div>
+
+<p>通过调用 <code>runserver</code> 命令运行Web服务器(与<strong>manage.py</strong>位于同一目录下):</p>
+
+<pre class="brush: bash">python3 manage.py runserver
+
+ Performing system checks...
+
+ System check identified no issues (0 silenced).
+ September 22, 2016 - 16:11:26
+ Django version 1.10, using settings 'locallibrary.settings'
+ Starting development server at http://127.0.0.1:8000/
+ Quit the server with CTRL-BREAK.
+</pre>
+
+<p>一旦服务器运行,你可以用你的浏览器导航到 <a href="http://127.0.0.1:8000/"><code>http://127.0.0.1:8000/</code> </a>查看。你应该会看到一个错误页面,如下所示。</p>
+
+<p><img alt="Django debug page for a 404 not found error" src="https://mdn.mozillademos.org/files/14009/django_404_debug_page.png" style="display: block; height: 545px; margin: 0px auto; width: 871px;"></p>
+
+<p>别担心,这个错误页面是预期结果。因为我们没有在 <code>catalogs.urls</code> 模块中定义任何页面/网址。<strong>(留意</strong>:当我们导航网站根目录URL时,我们被重定向到了<strong>/catalog 。)</strong></p>
+
+<div class="note">
+<p><strong>注意</strong>: 上面的页面展示了一个重要的Django功能—自动调试日志记录。每当找不到页面,或者代码引发任何错误,就会显示错误页面,其中会提供有用的信息。在这种情况下,你可以看到我们提供的 URL 与我们任何 URL 模式都不匹配(像列出的那样)。生产环境中,日志功能将被关闭(当我们将网站存放在网络上时),这种情况下,将提供的信息量更少,但用户友好的页面。</p>
+</div>
+
+<p>这个时候,我们知道Django正在工作。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 每当进行重大更改时,都应重新运行迁移并重新测试站点。这并不需要很长时间。</p>
+</div>
+
+<h2 id="挑战自我">挑战自我</h2>
+
+<p>该 <strong>catalog/ </strong>目录包含视图,模型和应用程序其他部分的文件。你可以打开这些文件并查看样板。</p>
+
+<p>如上所述,管理站点的 URL 映射已经添加到项目的 <strong>urls.py</strong> 中。导航到浏览器中的管理区域,看看会发生什么(您可以从上面的映射中推断出正确的URL)。</p>
+
+<ul>
+</ul>
+
+<h2 id="概要">概要</h2>
+
+<p>你现在已经创建了一个完整的基本网站项目骨架,你可以继续填加网址,模型,视图和模版。</p>
+
+<p>现在, <a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Local Library website</a> 的骨架已经完成并运行,是时候开始编写代码,让这个网站做它应该做的事情了。</p>
+
+<h2 id="更多">更多</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial01/">编写你的第一个Django应用 - part 1</a>  (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/applications/#configuring-applications">Applications</a> (Django Docs). 包括配置应用的信息。</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}</p>
diff --git a/files/zh-cn/learn/server-side/django/testing/index.html b/files/zh-cn/learn/server-side/django/testing/index.html
new file mode 100644
index 0000000000..26ca9b62bf
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/testing/index.html
@@ -0,0 +1,917 @@
+---
+title: 'Django 教程 10: 测试 Django 网页应用'
+slug: learn/Server-side/Django/Testing
+translation_of: Learn/Server-side/Django/Testing
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">随<font><font>着网站的增长,他们越来越难以手动测试。</font><font>不仅要进行更多的测试,而且随着组件之间的交互变得越来越复杂,一个区域的小改变可能会影响到其他区域,所以需要做更多的改变来确保一切正常运行,并且在进行更多更改时不会引入错误。</font><font>减轻这些问题的一种方法是编写自动化测试,每当您进行更改时,都可以轻松可靠地运行测试。</font><font>本教程演示如何</font><font>使用Django的测试框架</font><font>自动化</font><font>您的网站的</font></font><em><font><font>单元测试</font></font></em>。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>完成之前的所有教程主题,包括 Django教程 9:使用表单。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>了解如何为基于 Django 的网站编写单元测试。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概览">概览</h2>
+
+<p>LocalLibrary 目前有页面显示所有书本和作者的列表,书本和作者项目的详细视图,续借<code>BookInstances</code>的页面,以及创建,更新和删除作者项目的页面(如果您完成了<strong>Django 教程 9:使用表单</strong>中的自我挑战,也可以创建,更新和删除书本记录)。即使使用这个相对较小的站点,手动导航到每个页面,并且表面地检查一切是否按预期工作,可能需要几分钟。当我们进行更改,并扩展网站时,手动检查所有内容 “正常” 工作所需的时间只会增长。如果我们继续这样做,最终我们将花费大部分时间进行测试,并且很少有时间来改进我们的代码。</p>
+
+<p>自动化测试可以真正帮助解决这个问题!显而易见的好处,是它们可以比手动测试运行得更快,可以测试更底层级别的细节,并且每次都测试完全相同的功能(人类测试员远远没有这么可靠!)因为它们很快速,自动化的测试可以更频繁地执行,如果测试失败,他们会指出代码未按预期执行的位置。</p>
+
+<p>此外,自动化测试可以充当代码的第一个真实“用户”,迫使您严格定义和记录网站的行为方式。它们通常是您的代码示例,和文档的基础。由于这些原因,一些软件开发过程,从测试定义和实现开始,之后编写代码以匹配所需的行为(例如,测试驱动<a href="https://en.wikipedia.org/wiki/Test-driven_development">test-driven</a> 和行为驱动 <a href="https://en.wikipedia.org/wiki/Behavior-driven_development">behaviour-driven</a>的开发)。</p>
+
+<p>本教程通过向 LocalLibrary 网站添加大量测试,来演示如何为 Django 编写自动化测试。</p>
+
+<h3 id="测试的类型">测试的类型</h3>
+
+<p>测试和测试方法有许多类型,级别和分类。最重要的自动化测试是:</p>
+
+<dl>
+ <dt>单元测试Unit tests</dt>
+ <dd>验证各个组件的功能行为,通常是类别和功能级别。</dd>
+ <dt>回归测试</dt>
+ <dd>测试重现历史错误。最初运行每个测试,以验证错误是否已修复,然后重新运行,以确保在以后更改代码之后,未重新引入该错误。</dd>
+ <dt>集成测试</dt>
+ <dd>验证组件分组在一起使用时的工作方式。集成测试了解组件之间所需的交互,但不一定了解每个组件的内部操作。它们可能涵盖整个网站的简单组件分组。</dd>
+</dl>
+
+<div class="note">
+<p><strong>注意: </strong>其他常见类型的测试,包括黑盒,白盒,手动,自动,金丝雀,烟雾,一致性,验收,功能,系统,性能,负载和压力测试。查找它们以获取更多信息。</p>
+</div>
+
+<h3 id="Django为测试提供了什么?">Django为测试提供了什么?</h3>
+
+<p>测试网站是一项复杂的任务,因为它由多层逻辑组成 - 从 HTTP 级请求处理,查询模型,到表单验证和处理,以及模板呈现。</p>
+
+<p>Django 提供了一个测试框架,其中包含基于 Python 标准<code><a href="https://docs.python.org/3/library/unittest.html#module-unittest" title="(in Python v3.5)">unittest</a></code>库的小型层次结构。尽管名称如此,但该测试框架适用于单元测试和集成测试。 Django 框架添加了 API 方法和工具,以帮助测试 Web 和 Django 特定的行为。这允许您模拟请求,插入测试数据以及检查应用程序的输出。 Django 还提供了一个API(<a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#liveservertestcase">LiveServerTestCase</a>)和<a href="https://docs.djangoproject.com/en/2.0/topics/testing/advanced/#other-testing-frameworks">使用不同测试框架</a>的工具,例如,您可以与流行的 <a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> 框架集成,以模拟用户与实时浏览器交互。</p>
+
+<p>要编写测试,您可以从任何 Django(或unittest)测试基类(<a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#simpletestcase">SimpleTestCase</a>, <a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#transactiontestcase">TransactionTestCase</a>, <a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#testcase">TestCase</a>, <a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#liveservertestcase">LiveServerTestCase</a>)派生,然后编写单独的方法,来检查特定功能,是否按预期工作(测试使用 “assert” 方法来测试表达式导致 <code>True</code>或 <code>False</code>值,或者两个值相等,等等。)当您开始测试运行时,框架将在派生类中执行所选的测试方法。测试方法独立运行,具有在类中定义的常见设置和/或拆卸行为,如下所示。</p>
+
+<pre class="brush: python">class YourTestClass(TestCase):
+
+    def setUp(self):
+  #Setup run before every test method.
+        pass
+
+    def tearDown(self):
+  #Clean up run after every test method.
+        pass
+
+    def test_something_that_will_pass(self):
+        self.assertFalse(False)
+
+    def test_something_that_will_fail(self):
+        self.assertTrue(False)
+</pre>
+
+<p>大多数测试的最佳基类是 <a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#testcase">django.test.TestCase</a>。此测试类在运行测试之前,创建一个干净的数据库,并在自己的事务中,运行每个测试函数。该类还拥有一个<a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.Client">测试客户端</a>,您可以使用该客户端,模拟在视图级别与代码交互的用户。在下面的部分中,我们将集中讨论使用此<a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#testcase">TestCase</a> 基类创建的单元测试。</p>
+
+<div class="note">
+<p><strong>注意:</strong> <a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#testcase">django.test.TestCase</a> 类非常方便,但可能会导致某些测试,比它们需要的速度慢(并非每个测试,都需要设置自己的数据库,或模拟视图交互)。一旦熟悉了这个类可以做什么,您可能希望用可以用更简单的测试类,替换一些测试。</p>
+</div>
+
+<h3 id="你应该测试什么?">你应该测试什么?</h3>
+
+<p>您应该测试自己代码的所有方面,但不要测试 Python 或 Django 的一部分提供的任何库或功能。</p>
+
+<p>例如,考虑下面定义的 <code>Author</code>模型。您不需要显式测试 <code>first_name</code> 和 <code>last_name</code> 是否已在数据库中正确储存为<code>CharField</code>,因为这是 Django 定义的内容(当然,在实践中,您将不可避免地在开发期间测试此功能)。你也不需要测试<code>date_of_birth</code>是否已被验证为日期字段,因为这也是 Django 中实现的东西。</p>
+
+<p>但是,您应该检查用于标签的文本(名字,姓氏,出生日期,死亡),以及为文本分配的字段大小(100个字符),因为这些是您的设计的一部分,可能会在将来被打破/改变。</p>
+
+<pre class="brush: python">class Author(models.Model):
+    first_name = models.CharField(max_length=100)
+    last_name = models.CharField(max_length=100)
+    date_of_birth = models.DateField(null=True, blank=True)
+    date_of_death = models.DateField('Died', null=True, blank=True)
+
+    def get_absolute_url(self):
+        return reverse('author-detail', args=[str(self.id)])
+
+    def __str__(self):
+        return '%s, %s' % (self.last_name, self.first_name)</pre>
+
+<p>同样,您应该检查自定义方法 <code style="font-style: normal; font-weight: normal;">get_absolute_url()</code> 和 <code style="font-style: normal; font-weight: normal;">__str__()</code> 是否符合要求,因为它们是您的代码/业务逻辑。在<code style="font-style: normal; font-weight: normal;">get_absolute_url()</code>的情况下,您可以相信 Django <code>reverse()</code>方法已经正确实现,因此您正在测试的是实际上已经定义了关联的视图。</p>
+
+<p> </p>
+
+<div class="note">
+<p><strong>注意:</strong> 精明的读者可能会注意到,我们也希望将出生和死亡的日期限制在合理的值,并检查出生后是否死亡。在 Django中,此约束将添加到表单类中(尽管您可以为字段定义验证器,这些字段似乎仅在表单级别使用,而不是在模型级别使用)。</p>
+</div>
+
+<p>考虑到这些,让我们开始研究如何定义和运行测试。</p>
+
+<h2 id="测试结构概述">测试结构概述</h2>
+
+<p>在我们详细讨论“测试内容”之前,让我们先简要介绍一下测试的定位和方式。</p>
+
+<p>Django 使用 unittest 模块的<a href="https://docs.python.org/3/library/unittest.html#unittest-test-discovery">内置测试查找</a>,它将在任何使用模式<strong>test*.py </strong>命名的文件中,查找当前工作目录下的测试。如果您正确命名文件,则可以使用您喜欢的任何结构。我们建议您为测试代码创建一个模块,并为模型,视图,表单和您需要测试的任何其他类型的代码,分别创建文件。例如:</p>
+
+<pre>catalog/
+  /tests/
+  __init__.py
+  test_models.py
+  test_forms.py
+  test_views.py
+</pre>
+
+<p>在 LocalLibrary 项目中,创建如上所示的文件结构。<strong>__init__.py </strong>应该是一个空文件(这告诉 Python 该目录是一个套件包)。您可以通过复制和重命名框架测试文件<strong>/catalog/tests.py</strong>,来创建三个测试文件。</p>
+
+<p> </p>
+
+<div class="note">
+<p><strong>注意:</strong> 我们构建 Django 骨架网站时,会自动创建骨架测试文件<strong>/catalog/tests.py</strong> 。将所有测试放入其中是完全“合法的”,但如果测试正确,您将很快得到一个非常庞大且难以管理的测试文件。</p>
+
+<p>删除骨架文件,因为我们不需要它。</p>
+</div>
+
+<p>打开 <strong>/catalog/tests/test_models.py</strong>。 该文件应导入<code>django.test.TestCase</code>,如下所示:</p>
+
+<pre class="brush: python">from django.test import TestCase
+
+# Create your tests here.
+</pre>
+
+<p>通常,您将为要测试的每个模型/视图/表单添加测试类别,并使用个别方法来测试特定功能。在其他情况下,您可能希望有一个分开的类别,来测试特定用例,使用个别的测试函数,来测试该用例的各个方面(例如,测试模型字段已正确验证的类,以及测试每个可能的失败案例的函数)。相同地,这样的结构非常适合您,但最好您能保持一致。</p>
+
+<p>将下面的测试类别,添加到文件的底部。该类别演示了,如何通过派生<code>TestCase</code>,构建测试用例类。</p>
+
+<pre class="brush: python">class YourTestClass(TestCase):
+
+    @classmethod
+    def setUpTestData(cls):
+        print("setUpTestData: Run once to set up non-modified data for all class methods.")
+        pass
+
+    def setUp(self):
+        print("setUp: Run once for every test method to setup clean data.")
+        pass
+
+    def test_false_is_false(self):
+        print("Method: test_false_is_false.")
+        self.assertFalse(False)
+
+    def test_false_is_true(self):
+        print("Method: test_false_is_true.")
+        self.assertTrue(False)
+
+    def test_one_plus_one_equals_two(self):
+        print("Method: test_one_plus_one_equals_two.")
+        self.assertEqual(1 + 1, 2)</pre>
+
+<p>新的类别定义了两个可用于测试之前的配置的方法(例如,创建测试所需的任何模型或其他对象):</p>
+
+<ul>
+ <li><code>setUpTestData()</code> 用于类级别设置,在测试运行开始的时侯,会调用一次。您可以使用它来创建在任何测试方法中,都不会修改或更改的对象。</li>
+ <li><code>setUp()</code> 在每个测试函数之前被调用,以设置可能被测试修改的任何对象(每个测试函数,都将获得这些对象的 “新” 版本)。</li>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>:测试类别还有一个我们还没有使用的<code>tearDown()</code>方法。此方法对数据库测试不是特别有用,因为<code>TestCase</code>基类会为您处理数据库拆卸。</p>
+</div>
+
+<p>下面我们有一些测试方法,它们使用 <code>Assert </code>函数来测试条件是真,假或相等(<code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code>)。如果条件评估不如预期,则测试将失败,并将错误报告给控制台。</p>
+
+<p><code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual </code>是 <strong>unittest </strong>提供的标准断言。框架中还有其他标准断言,还有 <a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#assertions">Django 特定的断言</a>,来测试视图是否重定向(<code>assertRedirects</code>),或测试是否已使用特定模板(<code>assertTemplateUsed</code>)等。</p>
+
+<div class="note">
+<p><strong>注意</strong>:您通常<strong>不应</strong>在测试中包含<strong>print()</strong> 函数,如上所示。我们这样做,只是为了让您可以看到在控制台中,调用设置功能的顺序(在下一节中)。</p>
+</div>
+
+<h2 id="如何运行测试">如何运行测试</h2>
+
+<p>要运行所有测试,最简单的方法,是使用以下命令:</p>
+
+<pre class="brush: bash">python3 manage.py test</pre>
+
+<p>这将查找当前目录下,使用模式 <strong>test*.py</strong> 命名的所有文件,并运行使用适当基类定义的所有测试(这里我们有许多测试文件,但只有 <strong>/catalog/tests/test_models.py</strong> 目前包含任何测试。)。默认情况下,测试将仅单独报告测试失败,然后是测试摘要。</p>
+
+<p> </p>
+
+<div class="note">
+<p>如果您收到类似于以下内容的错误:<code>ValueError: Missing staticfiles manifest entry ...</code> 这可能是因为默认情况下,测试不会运行 collectstatic,而您的应用程序正在使用需要它的储存类别(有关更多信息,请参阅 <a href="https://docs.djangoproject.com/en/2.0/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.ManifestStaticFilesStorage.manifest_strict">manifest_strict</a>)。有许多方法可以解决这个问题 - 最简单的方法,是在运行测试之前,简单地运行collectstatic:</p>
+
+<pre class="brush: bash">python3 manage.py collectstatic
+</pre>
+</div>
+
+<p>在 LocalLibrary 的根目录中,运行测试。您应该看到如下所示的输出。</p>
+
+<pre class="brush: bash">&gt;python3 manage.py test
+
+Creating test database for alias 'default'...
+<strong>setUpTestData: Run once to set up non-modified data for all class methods.
+setUp: Run once for every test method to setup clean data.
+Method: test_false_is_false.
+.setUp: Run once for every test method to setup clean data.
+Method: test_false_is_true.
+FsetUp: Run once for every test method to setup clean data.
+Method: test_one_plus_one_equals_two.</strong>
+.
+======================================================================
+FAIL: test_false_is_true (catalog.tests.tests_models.YourTestClass)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+  File "D:\Github\django_tmp\library_w_t_2\locallibrary\catalog\tests\tests_models.py", line 22, in test_false_is_true
+    self.assertTrue(False)
+AssertionError: False is not true
+
+----------------------------------------------------------------------
+Ran 3 tests in 0.075s
+
+FAILED (failures=1)
+Destroying test database for alias 'default'...</pre>
+
+<p>在这里,我们看到有一个测试失败,我们可以确切地看到哪个函数失败了、为什么失败(这个失败是预期的,因为 <code>False</code>不是 <code>True</code>!)。</p>
+
+<div class="note">
+<p><strong>提示: </strong>从上面的测试输出中,学到的最重要事情是,如果为对象和方法使用描述性/信息性名称,它会更有价值。</p>
+</div>
+
+<p>上面以<strong>粗体</strong>显示的文本,通常不会出现在测试输出中(这是由我们的测试中的<code>print()</code>函数生成的)。这显示了如何为类调用<code>setUpTestData()</code>方法,并在每个方法之前调用<code>setUp()</code>。</p>
+
+<p>接下来的部分,将介绍如何运行特定测试,以及如何控制测试显示的信息量。</p>
+
+<h3 id="显示更多测试信息">显示更多测试信息</h3>
+
+<p>如果您想获得有关测试运行的更多信息,可以更改详细程度。例如,要列出测试成功和失败(以及有关如何设置测试数据库的大量信息),您可以将详细程度设置为 “2”,如下所示:</p>
+
+<pre class="brush: bash">python3 manage.py test --verbosity 2</pre>
+
+<p>允许的详细级别为 0, 1 ,2 和 3,默认值为 “1”。</p>
+
+<h3 id="运行特定测试">运行特定测试</h3>
+
+<p>如果要运行测试的子集,可以通过指定包,模块,<code>TestCase</code>子类或方法的完整路径​​(包含点)来执行此操作:</p>
+
+<pre class="brush: bash">python3 manage.py test catalog.tests # Run the specified module
+python3 manage.py test catalog.tests.test_models # Run the specified module
+python3 manage.py test catalog.tests.test_models.YourTestClass # Run the specified class
+python3 manage.py test catalog.tests.test_models.YourTestClass.test_one_plus_one_equals_two # Run the specified method
+</pre>
+
+<h2 id="LocalLibrary_测试">LocalLibrary 测试</h2>
+
+<p>现在我们知道,如何运行我们的测试,以及我们需要测试哪些东西,让我们看一些实际的例子。</p>
+
+<div class="note">
+<p><strong>注意: </strong>我们不会编写所有可能的测试,但这应该可以让您了解测试的工作原理,以及您可以做些什么。</p>
+</div>
+
+<h3 id="模型">模型</h3>
+
+<p>如上所述,我们应该测试我们设计的任何内容,或由我们编写的代码定义的内容,而不是已经由 Django 或 Python 开发团队测试过的库/代码。</p>
+
+<p>例如,请考虑下面的作者模型<code> Author</code>。在这里,我们应该测试所有字段的标签,因为即使我们没有明确指定它们中的大部分,我们也有一个设计,说明这些值应该是什么。如果我们不测试值,那么我们不知道字段标签,是否具有其预期值。同样,虽然我们相信 Django 会创建一个指定长度的字段,但值得为这个长度指定一个测试,以确保它按计划实现。</p>
+
+<pre class="brush: python">class Author(models.Model):
+    first_name = models.CharField(max_length=100)
+    last_name = models.CharField(max_length=100)
+    date_of_birth = models.DateField(null=True, blank=True)
+    date_of_death = models.DateField('Died', null=True, blank=True)
+
+    def get_absolute_url(self):
+        return reverse('author-detail', args=[str(self.id)])
+
+    def __str__(self):
+        return '%s, %s' % (self.last_name, self.first_name)</pre>
+
+<p>打开我们的 <strong>/catalog/tests/test_models.py</strong>,并用 <code>Author</code>模型的以下测试代码,替换任何现有代码。</p>
+
+<p>在这里,您将看到我们首先导入 <code>TestCase</code>,并使用描述性名称,从中派生我们的测试类(<code>AuthorModelTest</code>),以便我们可以轻松识别测试输出中的任何失败测试。然后我们调用<code>setUpTestData()</code>,来创建一个我们将使用,但不在任何测试中修改的作者对象。</p>
+
+<pre class="brush: python">from django.test import TestCase
+
+# Create your tests here.
+
+from catalog.models import Author
+
+class AuthorModelTest(TestCase):
+
+ @classmethod
+ def setUpTestData(cls):
+ #Set up non-modified objects used by all test methods
+ Author.objects.create(first_name='Big', last_name='Bob')
+
+ def test_first_name_label(self):
+ author=Author.objects.get(id=1)
+ field_label = author._meta.get_field('first_name').verbose_name
+ self.assertEquals(field_label,'first name')
+
+ def test_date_of_death_label(self):
+ author=Author.objects.get(id=1)
+ field_label = author._meta.get_field('date_of_death').verbose_name
+ self.assertEquals(field_label,'died')
+
+ def test_first_name_max_length(self):
+ author=Author.objects.get(id=1)
+ max_length = author._meta.get_field('first_name').max_length
+ self.assertEquals(max_length,100)
+
+ def test_object_name_is_last_name_comma_first_name(self):
+ author=Author.objects.get(id=1)
+ expected_object_name = '%s, %s' % (author.last_name, author.first_name)
+ self.assertEquals(expected_object_name,str(author))
+
+ def test_get_absolute_url(self):
+ author=Author.objects.get(id=1)
+ #This will also fail if the urlconf is not defined.
+ self.assertEquals(author.get_absolute_url(),'/catalog/author/1')</pre>
+
+<p>字段测试检查字段标签(<code>verbose_name</code>)的值,以及字符字段的大小,是否符合预期。这些方法都有描述性名称,并遵循相同的模式:</p>
+
+<pre class="brush: python">author=Author.objects.get(id=1) # Get an author object to test
+field_label = author._meta.get_field('first_name').verbose_name # Get the metadata for the required field and use it to query the required field data
+self.assertEquals(field_label,'first name') # Compare the value to the expected result</pre>
+
+<p>有趣的事情是:</p>
+
+<ul>
+ <li>我们无法使用 <code>author.first_name.verbose_name</code>直接获取 <code>verbose_name</code>,因为<code>author.first_name </code>是一个字符串(不是我们可以用来访问其属性的<code>first_name</code> 对象的句柄)。取而代之的是,我们需要使用作者的 <code>_meta</code>属性,来获取字段的实例,并使用它来查询其他信息。<br>
+    </li>
+ <li>我们选择使用 <code>assertEquals(field_label,'first name')</code> ,而不是<code>assertTrue(field_label == 'first name')</code>。这样做的原因是,如果测试失败,前者的输出,会告诉您标签实际上是什么,这使得调试问题变得更容易一些。</li>
+</ul>
+
+<div class="note">
+<p><strong>注意:</strong> 已省略对<code>last_name</code> 和 <code>date_of_birth</code>标签的测试,以及 <code>last_name</code>字段长度的测试。现在按照上面显示的命名约定和方法,添加您自己的版本。</p>
+</div>
+
+<p>我们还需要测试我们的自定义方法。这些基本上只是检查对象名称,是否按照我们的预期,使用“姓氏”,“名字”格式构建,并且我们为<code>Author</code>获取的 URL,是我们所期望的。</p>
+
+<pre class="brush: python">def test_object_name_is_last_name_comma_first_name(self):
+    author=Author.objects.get(id=1)
+    expected_object_name = '%s, %s' % (author.last_name, author.first_name)
+    self.assertEquals(expected_object_name,str(author))
+
+def test_get_absolute_url(self):
+    author=Author.objects.get(id=1)
+    #This will also fail if the urlconf is not defined.
+    self.assertEquals(author.get_absolute_url(),'/catalog/author/1')</pre>
+
+<p>立即运行测试。如果您按照模型教程中的描述,创建了作者模型,则很可能会出现<code>date_of_death</code>标签的错误,如下所示。测试失败,是因为它写的是期望标签定义遵循 Django 的约定,即没有大写标签的第一个字母(Django 会为你做这个)。</p>
+
+<pre class="brush: bash">======================================================================
+FAIL: test_date_of_death_label (catalog.tests.test_models.AuthorModelTest)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+ File "D:\...\locallibrary\catalog\tests\test_models.py", line 32, in test_date_of_death_label
+ self.assertEquals(field_label,'died')
+AssertionError: 'Died' != 'died'
+- Died
+? ^
++ died
+? ^</pre>
+
+<p>这是一个非常小的错误,但它确实强调了,编写测试如何能够更彻底地检查,您可能做出的任何假设。</p>
+
+<div class="note">
+<p><strong>注意: </strong>将 date_of_death字段(/catalog/models.py)的标签更改为“death”并重新运行测试。</p>
+</div>
+
+<p>用于测试其他模型的模式,也类似于此,因此我们不会继续进一步讨论这些模式。请随意为其他模型,创建您自己的测试。</p>
+
+<h3 id="表单">表单</h3>
+
+<p>测试表单的理念,与测试模型的理念相同;您需要测试您编码、或设计指定的任何内容,但不测试底层框架,和其他第三方库的行为。</p>
+
+<p>通常,这意味着您应该测试表单,是否包含您想要的字段,并使用适当的标签和帮助文本,显示这些字段。您无需验证 Django 是否正确验证了字段类型(除非您创建了自己的自定义字段和验证) - 即您不需要测试电子邮件字段,是否只接受电子邮件。但是,您需要测试,您希望在字段上执行的任何其他验证,以及您的代码将为错误生成的任何消息。</p>
+
+<p>考虑我们更新书本的表格。这只有一个继续借阅的日期字段,它将包含我们需要验证的标签,和帮助文本。</p>
+
+<pre class="brush: python">class RenewBookForm(forms.Form):
+ """
+ Form for a librarian to renew books.
+ """
+ renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
+
+ def clean_renewal_date(self):
+ data = self.cleaned_data['renewal_date']
+
+ #Check date is not in past.
+ if data &lt; datetime.date.today():
+ raise ValidationError(_('Invalid date - renewal in past'))
+ #Check date is in range librarian allowed to change (+4 weeks)
+ if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
+ raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
+
+ # Remember to always return the cleaned data.
+ return data</pre>
+
+<p>打开我们的<strong> /catalog/tests/test_forms.py</strong> 文件,并用<code>RenewBookForm</code>表单的以下测试代码,替换任何现有代码。我们首先导入我们的表单,和一些 Python 和 Django 库,以帮助测试与时间相关的功能。然后,我们以与模型相同的方式,声明我们的表单测试类,使用我们的 <code>TestCase</code> 派生测试类的描述性名称。</p>
+
+<pre class="brush: python">from django.test import TestCase
+
+# Create your tests here.
+
+import datetime
+from django.utils import timezone
+from catalog.forms import RenewBookForm
+
+class RenewBookFormTest(TestCase):
+
+    def test_renew_form_date_field_label(self):
+        form = RenewBookForm()
+        self.assertTrue(form.fields['renewal_date'].label == None or form.fields['renewal_date'].label == 'renewal date')
+
+    def test_renew_form_date_field_help_text(self):
+        form = RenewBookForm()
+        self.assertEqual(form.fields['renewal_date'].help_text,'Enter a date between now and 4 weeks (default 3).')
+
+    def test_renew_form_date_in_past(self):
+        date = datetime.date.today() - datetime.timedelta(days=1)
+        form_data = {'renewal_date': date}
+        form = RenewBookForm(data=form_data)
+        self.assertFalse(form.is_valid())
+
+    def test_renew_form_date_too_far_in_future(self):
+        date = datetime.date.today() + datetime.timedelta(weeks=4) + datetime.timedelta(days=1)
+        form_data = {'renewal_date': date}
+        form = RenewBookForm(data=form_data)
+        self.assertFalse(form.is_valid())
+
+    def test_renew_form_date_today(self):
+        date = datetime.date.today()
+        form_data = {'renewal_date': date}
+        form = RenewBookForm(data=form_data)
+        self.assertTrue(form.is_valid())
+
+    def test_renew_form_date_max(self):
+        date = timezone.now() + datetime.timedelta(weeks=4)
+        form_data = {'renewal_date': date}
+        form = RenewBookForm(data=form_data)
+        self.assertTrue(form.is_valid())
+</pre>
+
+<p>前两个函数,测试字段的<code>label</code> 和 <code>help_text</code>,是否符合预期。我们必须使用字段字典访问该字段(例如<code>form.fields['renewal_date']</code>)。请注意,我们还必须测试标签值,是否为<code>None</code>,因为即使 Django 将呈现正确的标签,如果未明确设置该值,它也会返回<code>None</code>。</p>
+
+<p>其余函数,测试表单对于续借日期,在可接受范围内是否有效,对于范围外的值,是否无效。请注意我们如何使用<code>datetime.timedelta()</code>,在当前日期(<code>datetime.date.today()</code>)周围构建测试日期值(在这种情况下指定天数或周数)。然后我们只需创建表单,传入我们的数据,并测试它是否有效。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 这里我们实际上并没有使用数据库,或测试客户端。考虑修改这些测试,以使用<a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.SimpleTestCase">SimpleTestCase</a>。</p>
+
+<p>如果表单无效,我们还需要验证是否引发了正确的错误,但这通常是作为视图处理的一部分完成的,因此我们将在下一节中处理。</p>
+</div>
+
+<p>这就是表单的全部;我们确实有其他一些的东西,但它们是由基于类的通用编辑视图自动创建的,应该在那里进行测试!运行测试,并确认我们的代码仍然通过!</p>
+
+<h3 id="视图">视图</h3>
+
+<p>为了验证我们的视图行为,我们使用 <a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.Client">Django 的测试客户端</a>。这个类,就像一个虚拟的Web浏览器,我们可以使用它,来模拟URL上的<code>GET</code>和<code>POST</code>请求,并观察响应。我们几乎可以看到,关于响应的所有内容,从低层级的 HTTP(结果标头和状态代码),到我们用来呈现HTML的模板,以及我们传递给它的上下文数据。我们还可以看到重定向链(如果有的话),并在每一步检查URL,和状态代码。这允许我们验证每个视图,是否正在执行预期的操作。</p>
+
+<p>让我们从最简单的视图开始,它提供了所有作者的列表。它显示在 URL <strong>/catalog/authors/</strong> 当中(URL 配置中,名为 “authors” 的 URL)。</p>
+
+<pre class="brush: python">class AuthorListView(generic.ListView):
+ model = Author
+ paginate_by = 10
+</pre>
+
+<p>由于这是一个通用列表视图,几乎所有内容,都由 Django 为我们完成。可以说,如果您信任 Django,那么您唯一需要测试的,是视图可以通过正确的 URL 访问,并且可以使用其名称进行访问。但是,如果您使用的是测试驱动的开发过程,则首先编写测试,确认视图显示所有作者,并将其分成10个。</p>
+
+<p>打开 <strong>/catalog/tests/test_views.py</strong> 文件,并用<code>AuthorListView</code>的以下测试代码,替换任何现有文本。和以前一样,我们导入模型,和一些有用的类。在<code>setUpTestData()</code>方法中,我们设置了许多<code>Author</code>对象,以便我们可以测试我们的分页。</p>
+
+<pre class="brush: python">from django.test import TestCase
+
+# Create your tests here.
+
+from catalog.models import Author
+from django.urls import reverse
+
+class AuthorListViewTest(TestCase):
+
+    @classmethod
+    def setUpTestData(cls):
+        #Create 13 authors for pagination tests
+        number_of_authors = 13
+        for author_num in range(number_of_authors):
+            Author.objects.create(first_name='Christian %s' % author_num, last_name = 'Surname %s' % author_num,)
+
+    def test_view_url_exists_at_desired_location(self):
+        resp = self.client.get('/catalog/authors/')
+        self.assertEqual(resp.status_code, 200)
+
+    def test_view_url_accessible_by_name(self):
+        resp = self.client.get(reverse('authors'))
+        self.assertEqual(resp.status_code, 200)
+
+    def test_view_uses_correct_template(self):
+        resp = self.client.get(reverse('authors'))
+        self.assertEqual(resp.status_code, 200)
+
+        self.assertTemplateUsed(resp, 'catalog/author_list.html')
+
+    def test_pagination_is_ten(self):
+        resp = self.client.get(reverse('authors'))
+        self.assertEqual(resp.status_code, 200)
+        self.assertTrue('is_paginated' in resp.context)
+        self.assertTrue(resp.context['is_paginated'] == True)
+        self.assertTrue( len(resp.context['author_list']) == 10)
+
+    def test_lists_all_authors(self):
+        #Get second page and confirm it has (exactly) remaining 3 items
+        resp = self.client.get(reverse('authors')+'?page=2')
+        self.assertEqual(resp.status_code, 200)
+        self.assertTrue('is_paginated' in resp.context)
+        self.assertTrue(resp.context['is_paginated'] == True)
+        self.assertTrue( len(resp.context['author_list']) == 3)</pre>
+
+<p>所有测试,都使用客户端(属于我们的<code>TestCase</code>的派生类)来模拟<code>GET</code>请求,并获得响应(<code>resp</code>)。第一个版本检查特定 URL(注意,只是没有域名的特定路径),而第二个版本从 URL配置中的名称生成 URL。</p>
+
+<pre class="brush: python">resp = self.client.get('/catalog/authors/')
+resp = self.client.get(reverse('authors'))
+</pre>
+
+<p>获得响应后,我们会查询其状态代码,使用的模板,响应是否已分页,返回的项目数以及项目总数。</p>
+
+<p><br>
+ 我们在上面演示的最有趣的变量是<code>resp.context</code>,它是视图传递给模板的上下文变量。这对测试非常有用,因为它允许我们确认模板正在获取所需的所有数据。换句话说,我们可以检查是否正在使用预期的模板,以及模板获得的数据,这对于验证任何渲染问题,是否真的仅仅归因于模板有很大帮助。</p>
+
+<h4 id="仅限登录用户的视图">仅限登录用户的视图</h4>
+
+<p>在某些情况下,您需要测试仅限登录用户的视图。例如,我们的<code>LoanedBooksByUserListView</code>与我们之前的视图非常相似,但仅供登录用户使用,并且仅显示当前用户借用的<code>BookInstance</code>记录,具有出借中“on loan”状态,并且排序方式为“旧的优先”。</p>
+
+<pre class="brush: python">from django.contrib.auth.mixins import LoginRequiredMixin
+
+class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
+ """
+ Generic class-based view listing books on loan to current user.
+ """
+ model = BookInstance
+ template_name ='catalog/bookinstance_list_borrowed_user.html'
+ paginate_by = 10
+
+ def get_queryset(self):
+ return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')</pre>
+
+<p>将以下测试代码,添加到 <strong>/catalog/tests/test_views.py</strong>。这里我们首先使用<code>SetUp()</code>创建一些用户登录帐户,和<code>BookInstance</code>对象(以及它们的相关书本,和其他记录),我们稍后将在测试中使用它们。每个测试用户都借用了一半的书本,但我们最初,将所有书本的状态设置为“维护”。我们使用了<code>SetUp()</code>而不是<code>setUpTestData()</code>,因为我们稍后会修改其中的一些对象。</p>
+
+<p> </p>
+
+<div class="note">
+<p><strong>注意:</strong> 下面的<code>setUp()</code>代码,会创建一个具有指定语言<code>Language</code>的书本,但您的代码可能不包含语言模型<code>Language</code>,因为它是作为挑战创建的。如果是这种情况,只需注释掉创建或导入语言对象的代码部分。您还应该在随后的<code>RenewBookInstancesViewTest</code>部分中,执行此操作。</p>
+</div>
+
+<pre class="brush: python">import datetime
+from django.utils import timezone
+
+from catalog.models import BookInstance, Book, Genre, Language
+from django.contrib.auth.models import User #Required to assign User as a borrower
+
+class LoanedBookInstancesByUserListViewTest(TestCase):
+
+    def setUp(self):
+        #Create two users
+        test_user1 = User.objects.create_user(username='testuser1', password='12345')
+        test_user1.save()
+        test_user2 = User.objects.create_user(username='testuser2', password='12345')
+        test_user2.save()
+
+        #Create a book
+        test_author = Author.objects.create(first_name='John', last_name='Smith')
+        test_genre = Genre.objects.create(name='Fantasy')
+        test_language = Language.objects.create(name='English')
+        test_book = Book.objects.create(title='Book Title', summary = 'My book summary', isbn='ABCDEFG', author=test_author, language=test_language)
+        # Create genre as a post-step
+        genre_objects_for_book = Genre.objects.all()
+  test_book.genre.set(genre_objects_for_book) #Direct assignment of many-to-many types not allowed.
+        test_book.save()
+
+        #Create 30 BookInstance objects
+        number_of_book_copies = 30
+        for book_copy in range(number_of_book_copies):
+            return_date= timezone.now() + datetime.timedelta(days=book_copy%5)
+            if book_copy % 2:
+                the_borrower=test_user1
+            else:
+                the_borrower=test_user2
+            status='m'
+            BookInstance.objects.create(book=test_book,imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=the_borrower, status=status)
+
+    def test_redirect_if_not_logged_in(self):
+        resp = self.client.get(reverse('my-borrowed'))
+        self.assertRedirects(resp, '/accounts/login/?next=/catalog/mybooks/')
+
+    def test_logged_in_uses_correct_template(self):
+        login = self.client.login(username='testuser1', password='12345')
+        resp = self.client.get(reverse('my-borrowed'))
+
+        #Check our user is logged in
+        self.assertEqual(str(resp.context['user']), 'testuser1')
+        #Check that we got a response "success"
+        self.assertEqual(resp.status_code, 200)
+
+        #Check we used correct template
+        self.assertTemplateUsed(resp, 'catalog/bookinstance_list_borrowed_user.html')
+</pre>
+
+<p>要验证如果用户未登录,视图将重定向到登录页面,我们使用<code>assertRedirects</code>,如<code>test_redirect_if_not_logged_in()</code>中所示。要验证是否已为登录用户显示该页面,我们首先登录我们的测试用户,然后再次访问该页面,并检查我们获得的<code>status_code</code>为200(成功)。</p>
+
+<p>测试的其余部分,验证我们的观点,仅返回借给当前借用人的书本。复制上面测试类末尾的(自解释)代码。</p>
+
+<pre class="brush: python">    def test_only_borrowed_books_in_list(self):
+        login = self.client.login(username='testuser1', password='12345')
+        resp = self.client.get(reverse('my-borrowed'))
+
+        #Check our user is logged in
+        self.assertEqual(str(resp.context['user']), 'testuser1')
+        #Check that we got a response "success"
+        self.assertEqual(resp.status_code, 200)
+
+        #Check that initially we don't have any books in list (none on loan)
+        self.assertTrue('bookinstance_list' in resp.context)
+        self.assertEqual( len(resp.context['bookinstance_list']),0)
+
+        #Now change all books to be on loan
+        get_ten_books = BookInstance.objects.all()[:10]
+
+        for copy in get_ten_books:
+            copy.status='o'
+            copy.save()
+
+        #Check that now we have borrowed books in the list
+        resp = self.client.get(reverse('my-borrowed'))
+        #Check our user is logged in
+        self.assertEqual(str(resp.context['user']), 'testuser1')
+        #Check that we got a response "success"
+        self.assertEqual(resp.status_code, 200)
+
+        self.assertTrue('bookinstance_list' in resp.context)
+
+        #Confirm all books belong to testuser1 and are on loan
+        for bookitem in resp.context['bookinstance_list']:
+            self.assertEqual(resp.context['user'], bookitem.borrower)
+            self.assertEqual('o', bookitem.status)
+
+    def test_pages_ordered_by_due_date(self):
+
+        #Change all books to be on loan
+        for copy in BookInstance.objects.all():
+            copy.status='o'
+            copy.save()
+
+        login = self.client.login(username='testuser1', password='12345')
+        resp = self.client.get(reverse('my-borrowed'))
+
+        #Check our user is logged in
+        self.assertEqual(str(resp.context['user']), 'testuser1')
+        #Check that we got a response "success"
+        self.assertEqual(resp.status_code, 200)
+
+        #Confirm that of the items, only 10 are displayed due to pagination.
+        self.assertEqual( len(resp.context['bookinstance_list']),10)
+
+        last_date=0
+        for copy in resp.context['bookinstance_list']:
+            if last_date==0:
+                last_date=copy.due_back
+            else:
+                self.assertTrue(last_date &lt;= copy.due_back)</pre>
+
+<p>你也可以添加分页测试,如果你愿意的话!</p>
+
+<h4 id="使用表单测试视图">使用表单测试视图</h4>
+
+<p>使用表单测试视图,比上面的情况稍微复杂一些,因为您需要测试更多代码路径:初始显示,数据验证失败后显示,以及验证成功后显示。好消息是,我们使用客户端进行测试的方式,与我们对仅显示视图的方式,几乎完全相同。</p>
+
+<p>为了演示,让我们为用于续借书本的视图,编写一些测试(<code>renew_book_librarian()</code>):</p>
+
+<pre class="brush: python">from .forms import RenewBookForm
+
+@permission_required('catalog.can_mark_returned')
+def renew_book_librarian(request, pk):
+    """
+    View function for renewing a specific BookInstance by librarian
+    """
+    book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a POST request then process the Form data
+    if request.method == 'POST':
+
+        # Create a form instance and populate it with data from the request (binding):
+        form = RenewBookForm(request.POST)
+
+        # Check if the form is valid:
+        if form.is_valid():
+            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+            book_inst.due_back = form.cleaned_data['renewal_date']
+            book_inst.save()
+
+            # redirect to a new URL:
+            return HttpResponseRedirect(reverse('all-borrowed') )
+
+    # If this is a GET (or any other method) create the default form
+    else:
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
+        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<p>我们需要测试该视图,仅供具有<code>can_mark_returned</code>权限的用户使用,并且如果用户尝试续借不存在的<code>BookInstance</code>,则会将用户重定向到HTTP 404错误页面。我们应该检查表单的初始值,是否以未来三周的日期为参考值,如果验证成功,我们将被重定向到 “所有借阅的书本” 视图。作为验证 - 失败测试的一部分,我们还将检查我们的表单,是否发送了相应的错误消息。</p>
+
+<p>将测试类的第一部分(如下所示),添加到 <strong>/catalog/tests/test_views.py</strong> 的底部。这将创建两个用户和两个书本实例,但只为一个用户提供访问该视图所需的权限。在测试期间,授予权限的代码以<strong>粗体</strong>显示:</p>
+
+<pre class="brush: python">from django.contrib.auth.models import Permission # Required to grant the permission needed to set a book as returned.
+
+class RenewBookInstancesViewTest(TestCase):
+
+ def setUp(self):
+ #Create a user
+ test_user1 = User.objects.create_user(username='testuser1', password='12345')
+ test_user1.save()
+
+ test_user2 = User.objects.create_user(username='testuser2', password='12345')
+ test_user2.save()
+<strong> permission = Permission.objects.get(name='Set book as returned')
+ test_user2.user_permissions.add(permission)
+ test_user2.save()</strong>
+
+ #Create a book
+ test_author = Author.objects.create(first_name='John', last_name='Smith')
+ test_genre = Genre.objects.create(name='Fantasy')
+ test_language = Language.objects.create(name='English')
+ test_book = Book.objects.create(title='Book Title', summary = 'My book summary', isbn='ABCDEFG', author=test_author, language=test_language,)
+ # Create genre as a post-step
+ genre_objects_for_book = Genre.objects.all()
+ test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed.
+ test_book.save()
+
+ #Create a BookInstance object for test_user1
+ return_date= datetime.date.today() + datetime.timedelta(days=5)
+ self.test_bookinstance1=BookInstance.objects.create(book=test_book,imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=test_user1, status='o')
+
+ #Create a BookInstance object for test_user2
+ return_date= datetime.date.today() + datetime.timedelta(days=5)
+ self.test_bookinstance2=BookInstance.objects.create(book=test_book,imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=test_user2, status='o')</pre>
+
+<p>将以下测试添加到测试类的底部。这些检查只有具有正确权限的用户(testuser2)才能访问该视图。我们检查所有情况:当用户没有登录时、当用户登录但没有正确的权限,当用户有权限但不是借用人(应该成功),以及当他们尝试访问不存在的<code>BookInstance</code>,会发生什么。我们还检查是否使用了正确的模板。</p>
+
+<pre class="brush: python">   def test_redirect_if_not_logged_in(self):
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+        #Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable)
+        self.assertEqual( resp.status_code,302)
+        self.assertTrue( resp.url.startswith('/accounts/login/') )
+
+    def test_redirect_if_logged_in_but_not_correct_permission(self):
+        login = self.client.login(username='testuser1', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+
+        #Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable)
+        self.assertEqual( resp.status_code,302)
+        self.assertTrue( resp.url.startswith('/accounts/login/') )
+
+    def test_logged_in_with_permission_borrowed_book(self):
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance2.pk,}) )
+
+        #Check that it lets us login - this is our book and we have the right permissions.
+        self.assertEqual( resp.status_code,200)
+
+    def test_logged_in_with_permission_another_users_borrowed_book(self):
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+
+        #Check that it lets us login. We're a librarian, so we can view any users book
+        self.assertEqual( resp.status_code,200)
+
+    def test_HTTP404_for_invalid_book_if_logged_in(self):
+        import uuid
+        test_uid = uuid.uuid4() #unlikely UID to match our bookinstance!
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':test_uid,}) )
+        self.assertEqual( resp.status_code,404)
+
+    def test_uses_correct_template(self):
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+        self.assertEqual( resp.status_code,200)
+
+        #Check we used correct template
+        self.assertTemplateUsed(resp, 'catalog/book_renew_librarian.html')
+</pre>
+
+<p>添加下一个测试方法,如下所示。这将检查表单的初始日期,是将来三周。请注意我们如何能够访问表单字段的初始值内的值(以<strong>粗体</strong>显示)。</p>
+
+<pre class="brush: python">    def test_form_renewal_date_initially_has_date_three_weeks_in_future(self):
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+        self.assertEqual( resp.status_code,200)
+
+        date_3_weeks_in_future = datetime.date.today() + datetime.timedelta(weeks=3)
+        self.assertEqual(<strong>resp.context['form'].initial['renewal_date']</strong>, date_3_weeks_in_future )
+</pre>
+
+<p>下一个测试(将其添加到类中)会检查如果续借成功,视图会重定向到所有借书的列表。这里的不同之处在于,我们首次展示了,如何使用客户端发布(<code>POST</code>)数据。 post数据是post函数的第二个参数,并被指定为键/值的字典。</p>
+
+<pre class="brush: python">    def test_redirects_to_all_borrowed_book_list_on_success(self):
+        login = self.client.login(username='testuser2', password='12345')
+        valid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=2)
+        resp = <strong>self.client.<em>post</em>(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future} )</strong>
+        self.assertRedirects(resp, reverse('all-borrowed') )
+</pre>
+
+<div class="warning">
+<p><strong>重要</strong>:全部借用的视图作为额外挑战,您的代码可能会改为重定向到主页'/'。如果是这样,请将测试代码的最后两行,修改为与下面的代码类似。请求中的<code>follow=True</code>,确保请求返回最终目标URL(因此检查<code>/catalog/</code>而不是<code>/</code>)。</p>
+
+<pre class="brush: python"> resp = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future},<strong>follow=True</strong> )
+ <strong>self.assertRedirects(resp, '/catalog/')</strong></pre>
+</div>
+
+<p>将最后两个函数,复制到类中,如下所示。这些再次测试<code>POST</code>请求,但在这种情况下具有无效的续借日期。我们使用<code>assertFormError() </code>,来验证错误消息是否符合预期。</p>
+
+<pre class="brush: python">    def test_form_invalid_renewal_date_past(self):
+        login = self.client.login(username='testuser2', password='12345')
+        date_in_past = datetime.date.today() - datetime.timedelta(weeks=1)
+        resp = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':date_in_past} )
+        self.assertEqual( resp.status_code,200)
+        <strong>self.assertFormError(resp, 'form', 'renewal_date', 'Invalid date - renewal in past')</strong>
+
+    def test_form_invalid_renewal_date_future(self):
+        login = self.client.login(username='testuser2', password='12345')
+        invalid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=5)
+        resp = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':invalid_date_in_future} )
+        self.assertEqual( resp.status_code,200)
+        <strong>self.assertFormError(resp, 'form', 'renewal_date', 'Invalid date - renewal more than 4 weeks ahead')</strong>
+</pre>
+
+<p>可以使用相似的技术,来测试其他视图。</p>
+
+<h3 id="模板">模板</h3>
+
+<p>Django 提供测试 API 来检查您的视图,是否正在调用正确的模板,并允许您验证,是否正在发送正确的信息。但是,没有特定的 API,支持在 Django中测试 HTML输出,是否按预期呈现。</p>
+
+<h2 id="其他推荐的测试工具">其他推荐的测试工具</h2>
+
+<p>Django 的测试框架,可以帮助您编写有效的单元和集成测试 - 我们只涉及底层单元测试框架<strong>unittest</strong>可以做什么,而不去谈 Django 的其他部分(例如,查看如何使用<a href="https://docs.python.org/3.5/library/unittest.mock-examples.html">unittest.mock</a> 修补第三方库,以便您可以更彻底地测试自己的代码)。</p>
+
+<p>虽然您可以使用许多其他测试工具,但我们只重点介绍两个:</p>
+
+<ul>
+ <li><a href="http://coverage.readthedocs.io/en/latest/">Coverage</a>: 此Python工具报告您的测试,实际执行了多少代码。当开始使用时,你正试图找出你应该测试的确切内容,它会特别有用。</li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> 是一个在真实浏览器中,自动化测试的框架。它允许您模拟与站点交互的真实用户,并为系统测试您的站点,提供了一个很好的框架(从集成测试开始的下一步)。</li>
+</ul>
+
+<h2 id="挑战自己">挑战自己</h2>
+
+<p>有许多模型与视图,我们可以用来测试。比如一个简单的任务,试着为<code>AuthorCreate</code>视图,创造一个测试案例。</p>
+
+<pre class="brush: python">class AuthorCreate(PermissionRequiredMixin, CreateView):
+ model = Author
+ fields = '__all__'
+ initial={'date_of_death':'12/10/2016',}
+ permission_required = 'catalog.can_mark_returned'</pre>
+
+<p>请记住,您需要检查您指定的任何内容、或设计的一部分。这将包括谁有权访问,初始日期,使用的模板,以及视图在成功时,重定向的位置。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>撰写测试代码既不有趣也不吸引人,因此在创造一个网站时,经常被留到最后才处理(或者完全不处理)。然而,它是一个基础的部分,以保证你的程式码,在更改之后是安全、可发布的,并且维护起来不会花费太多成本。</p>
+
+<p>本教程中,我們演示了如何为模型、表单和视图,编写并运行测试。最重要的是,我们已经提供给您,应该测试的内容的简短摘要,这通常是您开始时,最难解决的问题。还有很多东西要知道,但即使你已经学到了什么,你也应该能够为你的网站创建有效的单元测试。</p>
+
+<p>下一个、也是最后一个教程,展示了如何部署精彩的(并经过全面测试的!)Django网站。</p>
+
+<h2 id="也可以参考">也可以参考</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/testing/overview/">Writing and running tests</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/intro/tutorial05/">Writing your first Django app, part 5 &gt; Introducing automated testing</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/testing/tools/">Testing tools reference</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/testing/advanced/">Advanced testing topics</a> (Django docs)</li>
+ <li><a href="http://toastdriven.com/blog/2011/apr/10/guide-to-testing-in-django/">A Guide to Testing in Django</a> (Toast Driven Blog, 2011)</li>
+ <li><a href="http://test-driven-django-development.readthedocs.io/en/latest/index.html">Workshop: Test-Driven Web Development with Django</a> (San Diego Python, 2014)</li>
+ <li><a href="https://realpython.com/blog/python/testing-in-django-part-1-best-practices-and-examples/">Testing in Django (Part 1) - Best Practices and Examples</a> (RealPython, 2013)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="本系列教程">本系列教程</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/django/tutorial_local_library_website/index.html b/files/zh-cn/learn/server-side/django/tutorial_local_library_website/index.html
new file mode 100644
index 0000000000..5b2a54fc9e
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/tutorial_local_library_website/index.html
@@ -0,0 +1,70 @@
+---
+title: 'Django Tutorial: The Local Library website'
+slug: learn/Server-side/Django/Tutorial_local_library_website
+translation_of: Learn/Server-side/Django/Tutorial_local_library_website
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">我们实战教程系列的第一篇教程会解释你将学到什么。并提供一个“本地图书馆”的例子作为概述。在接下来的教程里,我们会不断完善和改进这个网站。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>阅读 <a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django介绍</a>。在接下来的文章里你需要 <a href="/en-US/docs/Learn/Server-side/Django/development_environment">创建Django开发环境</a>. </td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>介绍教程里使用的网站应用,让读者明白要讨论的主题。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概述">概述</h2>
+
+<p>欢迎来到MDN的”本地图书馆“Django教程。在教程里,我们会开发一个网站,用来管理本地图书馆的目录。</p>
+
+<p>在这一系列的教程里你将:</p>
+
+<ul>
+ <li>运用Django的工具创建网站和应用的框架。</li>
+ <li>启动和停止开发用的服务器。</li>
+ <li>创建模型(models)用来代表应用里的数据。</li>
+ <li>运用Django的admin站点填充网站数据。</li>
+ <li>面对不同的网络请求,创建视图函数(views)取回相应的数据。并把数据用模板(templates )渲染成HTML展示在浏览器里。</li>
+ <li>创建网络分发器,将不同的URL模式分发给特定的视图函数(views)。</li>
+ <li>添加用户认证和会话(sessions)管理网站行为和进入权限。</li>
+ <li>使用表单。</li>
+ <li>为应用编写测试。</li>
+ <li>有效运用Django的安全系统。</li>
+ <li>把应用布置到生产环境中。</li>
+</ul>
+
+<p>关于这些主题,你已经学会了一些,并对其他的也有了简单的了解。在这系列教程的最后,你会学到足够多而可以自己开发简单的Django应用了。</p>
+
+<h2 id="本地图书馆网站">本地图书馆网站</h2>
+
+<p><em>本地图书馆</em>是我们在本系列教程里创建和不断改善的网站。跟你期望的一样,这个网站的目标是为一个小型的图书馆提供一个在线的目录。在这个小型图书管里,用户能浏览书籍和管理他们的账户。</p>
+
+<p>这个例子是精心挑选出来的,因为它可以根据我们的需要增加或多或少的细节。也能用来展示几乎所有的Django特性。更重要的是,它提供了一条指南式的路线,在这条路线中,我们会用到Django网络框架最重要的功能:</p>
+
+<ul>
+ <li>在第一篇教程里,我们会定义一个简单到只能浏览的图书馆。图书馆的会员可以查找哪些书可以借阅。我们得以探索那些几乎所有网站都会运用的操作:阅读和展示数据库里的内容。</li>
+ <li>接下来,图书馆会慢慢扩展来展示更高级的Django特性。例如,我们会扩展功能,让会员能够保留图书。这个特性会展示如何使用表单,并支持用户认证。</li>
+</ul>
+
+<p>尽管这是一个非常容易扩展的例子,它被称为本地图书馆是有原因的——我们希望用最少的信息帮助你快速创建和运用Django。最后,我们会存储图书信息,图书数量,作者和其他重要信息。我们不会存储图书馆可能会存储的其他信息,或是提供一个支持多个图书馆或是”大型图书馆“功能的构建。</p>
+
+<h2 id="我卡住了,从哪里获得源代码呢?">我卡住了,从哪里获得源代码呢?</h2>
+
+<p>在学习本系列教程时,我们会提供合适的代码片段,你可以粘贴复制,但是有些代码我们希望你能自己扩展(在提示下)。</p>
+
+<p>如果你卡在某个地方,你可以在<a href="https://github.com/mdn/django-locallibrary-tutorial">Github</a>里找到网站的完整代码。I</p>
+
+<h2 id="总结">总结</h2>
+
+<p>现在你对本地图书馆网站有了一些了解并知道你会学到什么。是时候创建我们例子会用到的<a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">网站框架</a>了。</p>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}</p>
diff --git a/files/zh-cn/learn/server-side/django/web_application_security/index.html b/files/zh-cn/learn/server-side/django/web_application_security/index.html
new file mode 100644
index 0000000000..fa0664bb33
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/web_application_security/index.html
@@ -0,0 +1,180 @@
+---
+title: Django Web应用安全
+slug: learn/Server-side/Django/web_application_security
+translation_of: Learn/Server-side/Django/web_application_security
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Deployment", "Learn/Server-side/Django/django_assessment_blog", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">保<font><font>护用户数据是任何网站设计的重要部分。</font><font>我们之前在文章<a href="https://developer.mozilla.org/en-US/docs/Web/Security">web安全</a>中</font></font><font><font>解释了一些更常见的安全威胁--</font><font>本文提供了Django的内置保护如何处理这些威胁的实际演示</font></font>。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>阅读服务器端网页编程中的 "<a href="https://developer.mozilla.org/zh-CN/docs/learn/Server-side/First_steps/Website_security">Website security</a>" 主题。并请至少完成Django Web框架教程 <a href="https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: 使用表单</a> 及以前的教程。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解保障Django网站安全应该(和不应该)做的事情。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概述">概述</h2>
+
+<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/Security">web安全</a><a href="https://developer.mozilla.org/en-US/docs/Web/Security"> </a>主題提供一个概述,说明了网站安全对于服务器端设计的意义,以及以及一些需要应对的常见威胁。本文中包含一个关键的概念:如果网站信任任何来自浏览器的数据,几乎所有的攻击方法都会成功。</p>
+
+<div class="warning">
+<p><strong>重要提示:</strong> 切记,对于网站安全来说最重要一点就是<strong>“永远不要相信浏览器端提交的数据”</strong>。 这些数据包括使用<code>GET</code>方式请求时URL中的参数,<code>POST</code> 方式请求的数据,HTTP headers 和 cookies,以及用户上传的文件等等. 请确保一定要检查和清洗这些提交的数据。对于网站安全来说,总是要做好最坏的打算。</p>
+</div>
+
+<p>对Django用户来说,好消息是Django框架已经处理了大量的常见威胁。请阅读Django官方文档中的"<a href="https://docs.djangoproject.com/en/2.0/topics/security/">Security in Django</a>"部分来了解Django的安全细节,以及如何确保基于Django的网站的安全。</p>
+
+<h2 id="常见威胁及保护">常见威胁及保护</h2>
+
+<p>在本文中,我们将使用前面章节中的“<a href="https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django/Tutorial_local_library_website">本地图书馆</a>”项目作为示范来演示一些Django的安全特性。</p>
+
+<h3 id="跨站脚本_(XSS)">跨站脚本 (XSS)</h3>
+
+<p>XSS(英语:Cross site scripting,通常简称:XSS)是指一类恶意攻击者将代码通过网站注入到其他用户浏览器中的攻击方式。一般攻击者会把恶意代码作为普通数据放入到网站数据库中,这样其他用户在获取和展示数据的过程中就会受到攻击。此外,攻击者还可以通过引诱用户点击某些链接来执行恶意的JavaScript代码。</p>
+
+<p>Django的模板系统可以帮您抵挡大部分的XSS攻击,实现的方式在于转义对于HTML来说比较<strong>“危险”</strong>的特殊字符(可参考官方文档:<a href="https://docs.djangoproject.com/en/2.0/ref/templates/language/#automatic-html-escaping">escaping specific characters</a>)。现在,我们用<a href="https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a> 这一章中的“创建作者”表单来做个演示,尝试向我们的本地图书馆网站注入一些JavaScript脚本。</p>
+
+<ol>
+ <li>使用开发服务器启动网站(参考命令:<code>python3 manage.py runserver</code>)。</li>
+ <li>在浏览器中打开网站,并用超级用户身份登录。</li>
+ <li>进入创建作者页面 (地址可能会是:<code><a href="http://127.0.0.1:8000/catalog/author/create/">http://127.0.0.1:8000/catalog/author/create/</a></code>)。</li>
+ <li>输入姓名、生日等信息,随后在Last Name这个字段里面填入以下的内容:<br>
+ <code>&lt;script&gt;alert('Test alert');&lt;/script&gt;</code><br>
+ <img alt="Author Form XSS test" src="https://mdn.mozillademos.org/files/14305/author_create_form_alert_xss.png" style="border-style: solid; border-width: 1px; height: 245px; width: 594px;">
+ <div class="note">
+ <p><strong>注意:</strong> 这一段代码并没有任何杀伤力,在执行的时候只会在浏览器中弹出一个警告提示框。如果这个警告提示框出现,则表明本网站存在可被XSS攻击的漏洞。</p>
+ </div>
+ </li>
+ <li>点击 <strong>Submit</strong> 按钮保存信息。</li>
+ <li>保存后的作者信息将会显示为下图的样式。因为XSS防护措施的存在,注入代码中的<code>alert()</code>部分并没有执行,而只是用文本的方式直接显示了出来。<img alt="Author detail view XSS test" src="https://mdn.mozillademos.org/files/14307/author_detail_alert_xss.png" style="border-style: solid; border-width: 1px; height: 248px; width: 986px;"></li>
+</ol>
+
+<p>如果你有兴趣阅读下页面的HTML源码,则会发现危险的字符已被转义成了无害的字符(例如: <code>&gt;</code> 被转义为了 <code>&amp;gt;</code> )</p>
+
+<pre class="brush: html">&lt;h1&gt;Author: Boon&amp;lt;script&amp;gt;alert(&amp;#39;Test alert&amp;#39;);&amp;lt;/script&amp;gt;, David (Boonie) &lt;/h1&gt;
+</pre>
+
+<p>Django的模板系统可以帮助抵御大部分的XSS攻击。当然,XSS保护功能也可以被关闭,而且XSS保护一般对非用户输入的内容不会自动进行防护(例如表单中字段的<code>help_text</code>通常不会是用户提交的,所以这部分数据Django也不会进行转义)</p>
+
+<p>XSS攻击也可能来自于其他不可信的数据来源,例如cookies,Web服务或上传的文件(实际上只要是未经清洗的数据直接展示出来都会有被攻击的可能)。如果你要显示这些不可信来源的数据,切记一定要自己做好数据清洗的工作。</p>
+
+<h3 id="防护跨站请求伪造_(CSRF)">防护跨站请求伪造 (CSRF) </h3>
+
+<p>CSRF(英语:Cross-site request forgery,通常简称:CSRF或XSRF)攻击可以让恶意攻击者在用户不知情的情况下,使用用户的身份来进行系统操作。举个例子,现在有一名黑客想要在我们的本地图书馆中添加一些作者信息。</p>
+
+<div class="note">
+<p><strong>注意:这个示例里面的黑客没有考虑对钱下手。而现实生活中的黑客则极有可能会产生更加危险的操作(例如,把钱转入他们自己的账户中等等)。</strong></p>
+</div>
+
+<p>为了实现这个目的,黑客可以创建一个类似于下面示例的HTML文件,这个文件包含了一个创建作者的表单(类似我们在之前章节中用过的),并且一旦加载完毕就会立即进行提交。随后黑客可以将这个文件发送至所有的图书管理员,并且引诱他们打开这个文件(文件中真的没有啥有害的信息)。如果任何一个已登录的图书管理员不慎打开了这个文件,那么文件中的表单就会利用图书管理员的身份来提交,随后就会创建出一个新的作者来。</p>
+
+<pre class="brush: html">&lt;html&gt;
+&lt;body onload='document.EvilForm.submit()'&gt;
+
+&lt;form action="http://127.0.0.1:8000/catalog/author/create/" method="post" name='EvilForm'&gt;
+ &lt;table&gt;
+ &lt;tr&gt;&lt;th&gt;&lt;label for="id_first_name"&gt;First name:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input id="id_first_name" maxlength="100" name="first_name" type="text" value="Mad" required /&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th&gt;&lt;label for="id_last_name"&gt;Last name:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input id="id_last_name" maxlength="100" name="last_name" type="text" value="Man" required /&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th&gt;&lt;label for="id_date_of_birth"&gt;Date of birth:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input id="id_date_of_birth" name="date_of_birth" type="text" /&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th&gt;&lt;label for="id_date_of_death"&gt;Died:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input id="id_date_of_death" name="date_of_death" type="text" value="12/10/2016" /&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;/table&gt;
+ &lt;input type="submit" value="Submit" /&gt;
+&lt;/form&gt;
+
+&lt;/body&gt;
+&lt;/html&gt;
+</pre>
+
+<p>运行Django开发服务器,然后使用超级管理员账号进行登录。将上面的代码贴到一个文件中,并在浏览器中打开这个文件,随后你就会看到一个CSRF错误,这是因为Django的安全机制防护了此类的攻击。</p>
+
+<p>在表单定义的时候加入 <code>{% csrf_token %} </code>这个模板标签, CSRF保护功能即可启用。在模板渲染的时候,这个token在 HTML代码中将会按照下面的格式显示,包含了一个与当前用户和当前浏览器关联的值。</p>
+
+<pre class="brush: html">&lt;input type='hidden' name='csrfmiddlewaretoken' value='0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW' /&gt;
+</pre>
+
+<p>Django生成这个用户/浏览器关联key的目的在于可以据此来拒绝那些不包含这个key的表单请求,也可以拒绝那些包含了错误了用户/浏览器关联key的表单请求。</p>
+
+<p>有了这种保护机制后,攻击者要发起攻击就需要找到目标用户的CSRF key。通过广撒网给所有的图书管理员发送恶意代码文件的方式也很难奏效,因为CSRF key是和浏览器相关联的。</p>
+
+<p>Django的CSRF防御默认是开启的。一定要在表单的位置使用 <code>{% csrf_token %}</code>这个标签,同时,切记使用<code>POST</code>方式来发起新增和更新数据的请求。</p>
+
+<h3 id="其他防护措施">其他防护措施</h3>
+
+<p>Django还提供了很多其他形式的防护措施 (大部分不是很容易进行演示):</p>
+
+<dl>
+ <dt>SQL注入防护</dt>
+ <dd>SQL注入漏洞可以让攻击者直接对网站数据库执行构造好的SQL语句,在无需用户权限的情况下即可实现对数据的访问、修改甚至是删除。绝大多数的情况下,使用Django的查询集/模型直接进行数据库访问时,实际使用的SQL语句已经被底层的数据库驱动妥善地进行了转义。如果必须要直接执行自定义的SQL语句,那么也请一定要注意防范SQL注入的问题。</dd>
+ <dt>点击劫持防护</dt>
+ <dd>点击劫持是指攻击者通过诱导用户,用户本意要访问A网站,最终却访问到了B网站。举例说明,攻击者可以给用户显示一个合法的银行网站,同时把用户名密码登录框改为不可见的<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe">&lt;iframe&gt; </a>标签,以此来窃取用户的登录信息。Django通过 <code><a href="https://docs.djangoproject.com/en/2.0/ref/middleware/#django.middleware.clickjacking.XFrameOptionsMiddleware" title="django.middleware.clickjacking.XFrameOptionsMiddleware">X-Frame-Options</a></code>中间件来防御点击劫持攻击,在支持的浏览器中,这种方式可以避免网站在iframe中显示。</dd>
+ <dt>强制SSL/HTTPS</dt>
+ <dd>web服务器可通过启用SSL/HTTPS来加密网站和浏览器之间的所有通信流量,包括了身份认证及其他通过纯文本方式来发送的数据流量(强烈建议启用HTTPS)。如果HTTPS已启用,Django还提供了一起实用的保护措施:</dd>
+</dl>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-SECURE_PROXY_SSL_HEADER"><code>SECURE_PROXY_SSL_HEADER</code></a> 设置可以用于检查内容是否安全,可用于代理和Django之间使用非HTTPS方式通讯的情况下。</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-SECURE_SSL_REDIRECT"><code>SECURE_SSL_REDIRECT</code></a> 可以将所有HTTP的请求重定向到HTTPS。</li>
+ <li>使用 <a href="https://docs.djangoproject.com/en/2.0/ref/middleware/#http-strict-transport-security">HTTP Strict Transport Security</a> (HSTS) 头来通知浏览器未来与此网站的连接仅使用HTTPS。与HTTP连接重定向至HTTPS的配置相结合后,HSTS可以确保之后的连接强制使用HTTPS。HSTS还有 <code><a href="https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-SECURE_HSTS_SECONDS">SECURE_HSTS_SECOND</a></code>和 <a href="https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-SECURE_HSTS_INCLUDE_SUBDOMAINS"><code>SECURE_HSTS_INCLUDE_SUBDOMAINS</code></a> 等选项可以进行配置。</li>
+ <li>将 <a href="https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-SESSION_COOKIE_SECURE"><code>SESSION_COOKIE_SECURE</code></a> 和 <a href="https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-CSRF_COOKIE_SECURE"><code>CSRF_COOKIE_SECURE</code></a> 设置为 <code>True</code>。这些配置将确保session和csrf的cookie仅使用HTTPS连接来发送。</li>
+</ul>
+
+<dl>
+ <dt>Host头校验</dt>
+ <dd>使用 <code><a href="https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-ALLOWED_HOSTS">ALLOWED_HOSTS</a></code> 配置仅接受由信任的host发起的请求。</dd>
+</dl>
+
+<p>还有很多其他的安全措施及使用这些安全措施的注意事项我们没有提到。我们这里仅仅提供了Django安全措施的一个概览,更多的信息请参阅Django官方安全文档。</p>
+
+<ul>
+</ul>
+
+<h2 id="总结">总结</h2>
+
+<p>Django具备有效的防护措施,以对抗一些常見的威胁,包括 XSS 和 CSRF 攻击。本文中,我们已经使用本地图书馆网站来了演示Django如何处理一些特定的攻击。我们也提供了关于其它保护措施的简单概述。</p>
+
+<p>但这仅仅是对网站安全的一个入门。我们强烈建议您阅读 <a href="https://docs.djangoproject.com/en/2.0/topics/security/">Django中的安全</a> 以获得更加深入的理解。</p>
+
+<p>本Django教程的下一步,也是最后一步,是完成 <a href="https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Django/django_assessment_blog">评估任务</a>。</p>
+
+<h2 id="参阅">参阅</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/security/">Security in Django</a> (Django官方文档)</li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/Security">Server side website security</a> (MDN)</li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/Security">Web security</a> (MDN)</li>
+ <li><a href="/en-US/docs/Web/Security/Securing_your_site">Securing your site</a> (MDN)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Deployment", "Learn/Server-side/Django/django_assessment_blog", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="In_this_module">In this module</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/django/主页构建/index.html b/files/zh-cn/learn/server-side/django/主页构建/index.html
new file mode 100644
index 0000000000..0527ba8731
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/主页构建/index.html
@@ -0,0 +1,358 @@
+---
+title: 'Django Tutorial Part 5: 主页构建'
+slug: learn/Server-side/Django/主页构建
+translation_of: Learn/Server-side/Django/Home_page
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">  我们现在可以添加代码来显示我们的第一个完整页面 -  <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> 网站的主页,显示每个模型类型有多少条记录,并提供我们其他页面的侧边栏导航链接。一路上,我们将获得编写基本URL地图和视图,从数据库获取记录以及使用模板的实践经验。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>读 the <a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django Introduction</a>. 完成上章节 (including <a href="/zh-CN/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a>).</td>
+ </tr>
+ <tr>
+ <th scope="row">目的:</th>
+ <td>了解如何创建简单的URL映射和视图(没有数据编码在URL中)以及如何从模型中获取数据并创建模版。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概要">概要</h2>
+
+<p>现在我们已经定义了我们的模型,并创建了一些初始库记录来处理,现在是编写代码以向用户呈现该信息的时候了。我们需要做的第一件事是确定我们希望能够在我们的页面中显示哪些信息,然后为返回这些资源定义适当的URL。那么我们将需要创建一个url映射器,视图和模板来显示这些页面。</p>
+
+<p>以下图表提供了处理HTTP请求/响应时需要实现的数据和事情的主要流程。我们已经创建了这个模型,我们需要创建的主要内容是:</p>
+
+<ul>
+ <li>URL映射-根据-支持的URL(以及任何编码在URL里的信息)跳转到相应的<strong>View</strong>功能函数。</li>
+ <li><strong>View</strong> 函数从模型中获取请求的数据,创建一个显示数据的HTML页面,并将其返回给用户在浏览器查看。</li>
+ <li><strong>Templates</strong> 在View视图中进行数据渲染的时候使用。</li>
+</ul>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13931/basic-django.png" style="display: block; margin: 0px auto;"></p>
+
+<p>正如你将在下一节中看到的,我们将要显示5个页面,这在一篇文章中是很重要的。因此,本文的大部分内容将重点介绍如何实现主页(我们将在随后的文章中介绍其他页面)。这应该让您对URL映射器,视图和模型在实践中如何工作有一个很好的端到端的了解。</p>
+
+<h2 id="定义资源URL">定义资源URL</h2>
+
+<p>由于本版本的LocalLibrary对于最终用户本质上是只读的,所以我们只需要为该网站(主页)提供一个着陆页,以及显示书籍和作者的列表和详细视图的页面。</p>
+
+<p>下面这些URL 是我们页面需要的:</p>
+
+<ul>
+ <li><code>catalog/</code> — 主页</li>
+ <li><code>catalog/books/</code> — 书单页</li>
+ <li><code>catalog/authors/</code> — 作者页</li>
+ <li><code>catalog/book/<em>&lt;id&gt;</em></code> — 主键字段 ID的具体书(默认) —详细视图。如下例子 <code>/catalog/book/3</code>,第三本书。</li>
+ <li><code>catalog/author/<em>&lt;id&gt;</em></code><em> </em>— 主键字段 ID的具体作者(默认) —详细视图。如下例子 <code>/catalog/author/11</code>,第11个作者。</li>
+</ul>
+
+<p>前三个URL用于列出索引,书籍和作者。这些不会对任何附加信息进行编码,而返回的结果将取决于数据库中的内容,运行获取信息的查询将始终保持一致。</p>
+
+<p>相比之下,最后两个URL用于显示有关特定书籍或作者的详细信息 - 这些URL将编码要显示在URL中的项目的标识(如上所示&lt;id&gt;)。URL映射器可以提取编码信息并将其传递给视图,然后将动态地确定从数据库获取哪些信息。通过对我们的URL中的信息进行编码,我们只需要一个URL映射,视图和模板来处理每本书(或作者)。</p>
+
+<div class="note">
+<p><strong style='background-color: #fff3d4; border: 0px; color: #333333; font-family: x-locale-heading-primary,zillaslab,Palatino,"Palatino Linotype",x-locale-heading-secondary,serif; font-size: 18px; font-style: normal; font-weight: 700; letter-spacing: normal; margin: 0px; padding: 0px; text-align: start; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal;'><font><font>注意</font></font></strong><font><font>:Django允许您以任何您喜欢的方式构建您的URL - 您可以如上所示编码URL正文中的信息,或使用URL<span> </span></font></font><code style='margin: 0px; padding: 0px; border: 0px; font-style: normal; font-weight: normal; font-family: consolas, "Liberation Mono", courier, monospace; word-wrap: break-word; color: rgb(51, 51, 51); font-size: 18px; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 243, 212); text-decoration-style: initial;'>GET</code><font><font>参数(例如 <span> </span></font></font><code style='margin: 0px; padding: 0px; border: 0px; font-style: normal; font-weight: normal; font-family: consolas, "Liberation Mono", courier, monospace; word-wrap: break-word; color: rgb(51, 51, 51); font-size: 18px; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; background-color: rgb(255, 243, 212); text-decoration-style: initial;'>/book/?id=6</code><font><font>)。</font><font>无论您使用哪种方法,URL都应保持清洁,逻辑和可读性</font></font> (<a href="https://www.w3.org/Provider/Style/URI">check out the W3C advice here</a>).<br>
+ <br>
+ <font><font>Django文档倾向于在URL的主体中推荐编码信息,这是他们觉得鼓励更好的URL设计的实践。</font></font></p>
+</div>
+
+<p>如概述,本文其余部分介绍如何构建索引页</p>
+
+<h2 id="创建索引页">创建索引页</h2>
+
+<p>我们创建的第一个页面将会是索引页(catalog/)。这会显示一些静态HTML,以及数据库中不同记录的一些计算的“计数“。为了使其工作,我们必须创建一个URL映射,视图和模版。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 本节应该特别注意。一些”材料“在所有页面都通用。</p>
+</div>
+
+<h3 id="URL_映射">URL 映射</h3>
+
+<p>在我们创建的<a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">基础网站</a>上,更新 <strong>/locallibrary/urls.py</strong> 文件。以确保每当收到以<code><strong>catalog/</strong></code>开头的URL时,URLConf模块中的<font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);"><strong>catalog.urls</strong></span></font> 会处理剩余的字符串。</p>
+
+<p>打开 catalog/<strong>urls.py</strong> ,复制下面代码</p>
+
+<pre class="brush: python">urlpatterns = [
+<strong> path('', views.index, name='index'),</strong>
+]</pre>
+
+<p>如果检测到URL模式'',(views.index——在view.py中函数命名index() )将被调用。URL模式是<a href="https://docs.python.org/3/library/re.html">Python 正则表达式</a> (RE)。我们将在本教程中进一步介绍RE。</p>
+
+<div class="note">
+<p><strong>注意: </strong>在  <strong>/locallibrary/locallibrary/urls.py</strong> </p>
+
+<pre><code>urlpatterns += [
+    path('catalog/', include('catalog.urls')),
+]</code></pre>
+
+<p>每当Django 使用 include() (<a href="https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.include" title="django.conf.urls.include"><code>django.conf.urls.include()),</code></a><code>它排除与该点 匹配URL的任何部分,并将剩余的字符串发送到随附的 URLconf 进行一步处理。</code></p>
+
+<p>匹配的URL 实际上是 <code>catalog/</code>+&lt;空字符串&gt; (<code>/catalog/</code> 假定是因为 <code>include()</code>是使用的方法)。如果我们收到一个URL的HTTP请求,我们的第一个视图函数将被调用<code>/catalog/。</code></p>
+</div>
+
+<p>此函数还说明了一个<code>name</code>参数,此唯一标识指定 URL 映射。你可以使用 "reverse" 映射—去动态创建指定映射设计处理的资源的一个URL。例如,我们现在可以通过在我们的模版中创建以下链接到我们的主页:</p>
+
+<pre class="brush: html">&lt;a href="<strong>{% url 'index' %}</strong>"&gt;Home&lt;/a&gt;.</pre>
+
+<div class="note">
+<p><strong>注意</strong>: 我们当然可以硬编码上面的链接(如:<code>&lt;a href="<strong>/catalog/</strong>"&gt;Home&lt;/a&gt;</code>),但是如果我们改变了主页的模式,模版将不再正确链接,使用反向网址映射会更灵活和强大。</p>
+</div>
+
+<h3 id="View_基于功能">View (基于功能)</h3>
+
+<p>视图是处理HTTP请求的功能,根据需要从数据库获取数据,通过使用HTML模板呈现此数据生成HTML页面,然后以HTTP响应返回HTML以显示给用户。索引视图遵循此模型 - 它提取有关数据库中有多少<code>Book</code>,<code>BookInstance </code>可用 <code>BookInstance</code> 和<code> Author</code> 记录的信息,并将其传递给模板以进行显示。</p>
+
+<p>打开catalog / views.py,并注意该文件已经导入了 使用模板和数据生成HTML文件的 <a href="https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#django.shortcuts.render">render()</a> 快捷方式函数。</p>
+
+<pre class="brush: python">from django.shortcuts import render
+
+# Create your views here.
+</pre>
+
+<p>复制文件底部的以下代码。第一行导入我们将用于访问所有视图中数据的模型类。</p>
+
+<pre class="brush: python">from .models import Book, Author, BookInstance, Genre
+
+def index(request):
+ """
+ View function for home page of site.
+ """
+ # Generate counts of some of the main objects
+ num_books=Book.objects.all().count()
+ num_instances=BookInstance.objects.all().count()
+ # Available books (status = 'a')
+ num_instances_available=BookInstance.objects.filter(status__exact='a').count()
+ num_authors=Author.objects.count() # The 'all()' is implied by default.
+
+ # Render the HTML template index.html with the data in the context variable
+ return render(
+ request,
+ 'index.html',
+ context={'num_books':num_books,'num_instances':num_instances,'num_instances_available':num_instances_available,'num_authors':num_authors},
+ )</pre>
+
+<p>视图函数的第一部分使用<code>objects.all()</code>模型类的属性来获取记录计数。它还会获取一个<code>BookInstance</code>状态字段值为“a”(可用)的对象列表。您可以在前面的教程 (<a href="/en-US/docs/Learn/Server-side/Django/Models#Searching_for_records">Django Tutorial Part 3: Using models &gt; Searching for records</a>)中找到更多关于如何访问模型的信息。</p>
+
+<p>在函数结束时,我们将该函数称为<code>render()</code>创建和返回HTML页面作为响应(此快捷方式函数包含许多其他函数,简化了这种非常常见的用例)。它将原始<code>request</code>对象(an HttpRequest)作为参数,具有数据占位符的HTML模板以及<code>context</code>变量(包含要插入到这些占位符中的数据的Python字典)。</p>
+
+<p>我们将在下一节中详细介绍模板和上下文变量; 让我们创建我们的模板,以便我们可以向用户显示一些内容</p>
+
+<h3 id="模版">模版</h3>
+
+<p>模版是定义一个文件(例如HTML页面)的结构与布局的文本文件,其中占位符用于表示实际内容。Django将自动在应用程序“templates”目录查找模版。所以例如,在我们刚刚加的索引页,<code>render()</code> 函数会期望能够找到<strong>/locallibrary/catalog/templates/<em>index.html</em></strong>这个文件,如何找不到该文件,则会引发错误。如果保存以前的更改并返回到浏览器,你可以看到访问 <code><a href="127.0.0.1:8000">127.0.0.1:8000</a> 现在将提供你一个相当直观的错误信息</code>"<strong>TemplateDoesNotExist at /catalog/</strong>“以及其他详细信息。</p>
+
+<div class="note">
+<p><strong>注意</strong>: Django 将根据你的项目的设置文件, 来查看模版的许多位置 (在已安装的应用程序中进行搜索是默认设置). 你可以查阅更多关于Django如何找到模版以及它支持的模版格式在<a href="https://docs.djangoproject.com/en/1.10/topics/templates/">(Templates</a> )。</p>
+</div>
+
+<h4 id="扩展模版">扩展模版</h4>
+
+<p>索引模版将需要标准的HTML标记头部和正文,以及用于导航的部分(去我们尚为创建的网站其他的页面)以及显示一些介绍文本和我们书籍数据。我们网站上的每一页,大部分文字(HTML和导航结构)都是一样的。Django模版语言不是强制开发人员在每个页面中复制这个“样板”,而是让你声明一个基本模版,然后再扩展它,仅替换每个特定页面不同的位置。</p>
+
+<p>例如,基本模版 <code>base_generic.html</code> 可能看起来像下面的文本。正如你所见的,它包含一些“常见“HTML”和标题,侧边栏和使用命名 <code>block</code> 和 <code>endblock</code> 模版标记(粗体显示)标记的内容部分。块可以是空的,或者包含将被派生页“默认使用”的内容。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 模版标签就像你可以在模版中使用的函数循环列表,基于变量的值执行条件操作等。除了模版标签,模版语法允许你引用模版变量(通过从视图进入模版),并使用模版过滤器,其中重新格式化变量(例如,将字符串设置为小写)。</p>
+</div>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;head&gt;
+ <strong>{% block title %}</strong>&lt;title&gt;Local Library&lt;/title&gt;<strong>{% endblock %}</strong>
+&lt;/head&gt;
+
+&lt;body&gt;
+ <strong>{% block sidebar %}</strong>&lt;!-- insert default navigation text for every page --&gt;<strong>{% endblock %}</strong>
+ <strong>{% block content %}</strong>&lt;!-- default content text (typically empty) --&gt;<strong>{% endblock %}</strong>
+&lt;/body&gt;
+&lt;/html&gt;
+</pre>
+
+<p>当我们要为特定视图定义一个模版时,我们首先指定基本模版(使用 <code>extends</code> 模版标签—查看下一个代码片段)。如果我们想要在模版中替换的章节,会使用相同的 <code>block/endblock </code>部分在基本模版表明。</p>
+
+<p>例如,下面我们使用 <code>extends</code> 模版标签,并覆盖 <code>content</code> 块。生成的最终HTML页面将具有基本模版中定义的所以HTML和结构(包括你在<code>title</code>块中定义的默认内容),但你新的 <code>content</code> 块插入到了默认的那块。</p>
+
+<p><code>base_generic.html</code> 详细会在下文中,请耐心往下看。</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+&lt;h1&gt;Local Library Home&lt;/h1&gt;
+&lt;p&gt;Welcome to &lt;em&gt;LocalLibrary&lt;/em&gt;, a very basic Django website developed as a tutorial example on the Mozilla Developer Network.&lt;/p&gt;
+{% endblock %}</pre>
+
+<h4 id="本地图书馆-基本模版">本地图书馆-基本模版</h4>
+
+<p>下面就是我们计划的基本模版用于本地图书馆网站。正如所看到的,内容包括一些HTML和定义块 <code>title</code> ,<code>sidebar</code> 和 <code>content</code>。我们有默认的 <code>title</code>(当然我们可以改)和默认的所以书籍和作者的链接列表 <code>sidebar</code> (我们可能并不会怎么改,但需要时,我们通过把想法放入块<code>block</code>中,比如想法是—允许范围)。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 我们再介绍两个额外的模版标签: <code>url</code> 和 <code>load static </code>。下文中我们会详细介绍。</p>
+</div>
+
+<p>创建一个新的文件 — <strong>/locallibrary/catalog/templates/<em>base_generic.html</em></strong> — 写入如下代码</p>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;head&gt;
+
+ {% block title %}&lt;title&gt;Local Library&lt;/title&gt;{% endblock %}
+ &lt;meta charset="utf-8"&gt;
+ &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
+ &lt;link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"&gt;
+ &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"&gt;&lt;/script&gt;
+ &lt;script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"&gt;&lt;/script&gt;
+
+ &lt;!-- Add additional CSS in static file --&gt;
+ {% load static %}
+ &lt;link rel="stylesheet" href="{% static 'css/styles.css' %}"&gt;
+&lt;/head&gt;
+
+&lt;body&gt;
+
+ &lt;div class="container-fluid"&gt;
+
+ &lt;div class="row"&gt;
+ &lt;div class="col-sm-2"&gt;
+ {% block sidebar %}
+ &lt;ul class="sidebar-nav"&gt;
+ &lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+ &lt;li&gt;&lt;a href=""&gt;All books&lt;/a&gt;&lt;/li&gt;
+ &lt;li&gt;&lt;a href=""&gt;All authors&lt;/a&gt;&lt;/li&gt;
+ &lt;/ul&gt;
+ {% endblock %}
+ &lt;/div&gt;
+ &lt;div class="col-sm-10 "&gt;
+ {% block content %}{% endblock %}
+ &lt;/div&gt;
+ &lt;/div&gt;
+
+ &lt;/div&gt;
+&lt;/body&gt;
+&lt;/html&gt;</pre>
+
+<p>该模版使用(并包含)JavaScript 和  <a href="http://getbootstrap.com/">Bootstrap  </a>(css框架)来改进HTML页面的布局和显示,这个框架或者另一个客户端网络框架,这是快速创建一个可用页面来适应在不同浏览器尺寸和允许我们处理页面呈现且不用一点细节—我们只需要专注在服务器端。</p>
+
+<p>基本模版还引用了一个本地css文件 (<strong>styles.css</strong>) ,它提供了一些额外的样式。 新建 <strong>/locallibrary/catalog/static/css/styles.css</strong> 如下:</p>
+
+<pre class="brush: css">.sidebar-nav {
+ margin-top: 20px;
+ padding: 0;
+ list-style: none;
+}</pre>
+
+<h4 id="索引模版">索引模版</h4>
+
+<p>新建HTML文件 <strong>/locallibrary/catalog/templates/<em>index.html</em></strong> 写入下面代码。第一行我们扩展了我们的基本模版, 使用 <code>content</code>替换默认块。</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+&lt;h1&gt;Local Library Home&lt;/h1&gt;
+
+ &lt;p&gt;Welcome to &lt;em&gt;LocalLibrary&lt;/em&gt;, a very basic Django website developed as a tutorial example on the Mozilla Developer Network.&lt;/p&gt;
+
+&lt;h2&gt;Dynamic content&lt;/h2&gt;
+
+ &lt;p&gt;The library has the following record counts:&lt;/p&gt;
+ &lt;ul&gt;
+ &lt;li&gt;&lt;strong&gt;Books:&lt;/strong&gt; <strong>\{{ num_books }}</strong>&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Copies:&lt;/strong&gt; <strong>\{{ num_instances }}</strong>&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Copies available:&lt;/strong&gt; <strong>\{{ num_instances_available }}</strong>&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Authors:&lt;/strong&gt; <strong>\{{ num_authors }}</strong>&lt;/li&gt;
+ &lt;/ul&gt;
+
+{% endblock %}</pre>
+
+<div class="note">
+<p>注意:由于本网站就是通过<strong>django </strong>来运维,<code><strong>\{{ </strong></code>的模版标签 在上面代码中会运行,只能通过增加 <code>\ </code>来转义,而不能直接写出“双大括号”。</p>
+</div>
+
+<p>在动态内容部分,我们的占位符(模版变量),是给我们想要视图的信息声明。变量使用“双大括号“ 或者“句柄“语法进行标记。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 你可以轻松地识别是否使用变量或模版标签(函数),因为变量具有双括号(<code>\{{ num_books }}</code>) 而标记被包含在带有百分比符号 (<code>{% extends "base_generic.html" %}</code>)的单个大括号中。</p>
+</div>
+
+<p>这里要注意的重要事情是这些变量用我们视图函数<code>render</code>中的字典—注入 <code>context</code> (下面);当渲染模版时,这些将替换为相关联的值。</p>
+
+<pre class="brush: python">return render(
+ request,
+ 'index.html',
+ context={'<strong>num_books</strong>':num_books,'<strong>num_instances</strong>':num_instances,'<strong>num_instances_available</strong>':num_instances_available,'<strong>num_authors</strong>':num_authors},
+)</pre>
+
+<h4 id="在模版中引用静态文件">在模版中引用静态文件</h4>
+
+<p>你的项目可能会使用静态资源,包括<strong>javascript</strong>,<strong>css</strong> 和图像。由于这些文件的位置可能不知道(或者可能会发生变化),则Django允许你指定你的模版相对于这些文件的位置 <code><strong>STATIC_URL</strong></code> 全局设置(默认基本网站设置的值 <code><strong>STATIC_URL</strong></code>,以“<code><strong>/static/</strong></code>”,但你可能选择在CDN和其他地方托管内容)。</p>
+
+<p>在模版中,你首先调用 <code>load</code> 指定“ <code>static</code>”去添加此模版库(如下)。静态加载后,你可以使用 <code>static</code> 模版标签,指定感兴趣的文件相对<code>URL</code></p>
+
+<pre class="brush: html"> &lt;!-- Add additional CSS in static file --&gt;
+{% load static %}
+&lt;link rel="stylesheet" href="{% static 'css/styles.css' %}"&gt;</pre>
+
+<p>你可以用同样的方式将图像添加到页面中:</p>
+
+<pre class="brush: html">{% load static %}
+&lt;img src="{% static 'catalog/images/local_library_model_uml.png' %}" alt="My image" style="width:555px;height:540px;"/&gt;
+</pre>
+
+<div class="note">
+<p><strong>主题</strong>: 上面的更改指定文件所在的位置,但Django默认不提供它们。当我们<a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">created the website skeleton</a>,我们在全局URL映射器r (<strong>/locallibrary/locallibrary/urls.py</strong>) 中开发Web服务器提供服务,你仍然需要安排它们在生产中投放。我们接下来看一看</p>
+</div>
+
+<p>更多内容—<a href="https://docs.djangoproject.com/en/1.10/howto/static-files/">Managing static files</a> (Django docs).</p>
+
+<h4 id="链接URLs">链接URLs</h4>
+
+<p>基本的模版引入<code> url</code> 模版标签</p>
+
+<pre class="brush: python">&lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+</pre>
+
+<p>此标记<code>url()</code>使用您的<strong>urls.py</strong>中调用的函数的名称 和相关视图将从该函数接收的任何参数的值,并返回可用于链接到该资源的URL。</p>
+
+<h2 id="它看起来什么样?">它看起来什么样?</h2>
+
+<p>运行 (<code>python3 manage.py runserver</code>) 和在浏览器中打开 <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>. I如果一切都正确设置,当当当当。</p>
+
+<p><img alt="Index page for LocalLibrary website" src="https://mdn.mozillademos.org/files/14045/index_page_ok.png" style="border-style: solid; border-width: 1px; display: block; height: 356px; margin: 0px auto; width: 874px;"></p>
+
+<div class="note">
+<p><strong style='background-color: #fff3d4; border: 0px; color: #333333; font-family: x-locale-heading-primary,zillaslab,Palatino,"Palatino Linotype",x-locale-heading-secondary,serif; font-size: 18px; font-style: normal; font-weight: 700; letter-spacing: normal; margin: 0px; padding: 0px; text-align: start; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal;'><font><font>注意:</font></font></strong><font><font>由于尚未定义这些网页的网址,视图和模板,因此</font><font>您将无法使用“<span> </span></font></font><strong style='background-color: #fff3d4; border: 0px; color: #333333; font-family: x-locale-heading-primary,zillaslab,Palatino,"Palatino Linotype",x-locale-heading-secondary,serif; font-size: 18px; font-style: normal; font-weight: 700; letter-spacing: normal; margin: 0px; padding: 0px; text-align: start; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal;'><font><font>所有图书</font></font></strong><font><font>和</font></font><strong style='background-color: #fff3d4; border: 0px; color: #333333; font-family: x-locale-heading-primary,zillaslab,Palatino,"Palatino Linotype",x-locale-heading-secondary,serif; font-size: 18px; font-style: normal; font-weight: 700; letter-spacing: normal; margin: 0px; padding: 0px; text-align: start; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal;'><font><font>所有作者”</font></font></strong><font><font>链接(目前我们刚刚在</font></font><code style='background-color: rgb(238, 238, 238); color: rgb(51, 51, 51); margin: 0px; padding: 2px 5px; border: 0px; font-style: normal; font-weight: normal; border-radius: 2px; font-family: consolas, "Liberation Mono", courier, monospace; word-wrap: break-word; font-size: 1rem; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; text-decoration-style: initial;'>base_generic.html</code><font><font>模板中</font><font>插入了这些链接的占位符</font><font>)</font></font></p>
+</div>
+
+<h2 id="挑战自己">挑战自己</h2>
+
+<p>以下是一些测试您熟悉模型查询,视图和模板的任务。</p>
+
+<p>   1. 在索引模板中声明一个新的标题块,并更改页面标题以匹配此特定页面。<br>
+    2. 修改视图以生成包含特定单词(不区分大小写)的类型计数和书数,然后将这些字段添加到模板。</p>
+
+<ul>
+</ul>
+
+<h2 id="概要_2">概要</h2>
+
+<p>我们现在已经为我们的网站创建了主页 - 一个HTML页面,显示数据库中的一些记录数,并且链接到我们其他尚待创建的页面。一路上,我们已经学到了很多有关url映射器,视图,使用我们的模型查询数据库的基本信息,如何从您的视图传递信息到模板,以及如何创建和扩展模板。</p>
+
+<p>在我们的下一篇文章中,我们将基于我们的知识来创建其他四个页面。</p>
+
+<h2 id="也可以看看">也可以看看</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial03/">Writing your first Django app, part 3: Views and Templates</a>  (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/http/urls/">URL 调度程序</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/http/views/">视图函数</a> (DJango docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/templates/">模版</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/static-files/">管理静态文件</a>(Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#django.shortcuts.render">Django 快捷功能</a>(Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}</p>
diff --git a/files/zh-cn/learn/server-side/django/开发环境/index.html b/files/zh-cn/learn/server-side/django/开发环境/index.html
new file mode 100644
index 0000000000..fb6041621f
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/开发环境/index.html
@@ -0,0 +1,406 @@
+---
+title: 设置Django开发环境
+slug: learn/Server-side/Django/开发环境
+tags:
+ - Python
+ - django
+translation_of: Learn/Server-side/Django/development_environment
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">现在,你知道什么是Django。<br>
+ 那么我们将向你展示如何在Windows,Linux(Ubuntu)和 Mac OSX上设置和测试Django开发环境—无论你常用哪种操作系统,本文能给你开发Django应用所需的一切。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>知道如何在你开发所用的计算机操作系统中,打开终端/命令行和安装软件包。</td>
+ </tr>
+ <tr>
+ <th scope="row">目的:</th>
+ <td>在你的计算机中运行Django(1.10)开发环境。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Django_开发环境概述">Django 开发环境概述</h2>
+
+<p>Django 使你可以轻松配置自己的电脑,以便开始开发网络应用。本节解释您可以从开发环境中获得什么,并提供一些设置和配置选项的概述。本文的其余部分介绍了在<strong>Ubuntu</strong>,<strong>Mac</strong> OSX和<strong>Windows</strong>上安装Django开发环境的 <strong>推荐方法</strong>,以及如何测试。</p>
+
+<h3 id="什么是Django开发环境">什么是Django开发环境?</h3>
+
+<p>开发环境是本地计算机上的Django安装,在将Django应用程序部署到生产环境之前,您可以使用它来开发和测试Django应用程序。</p>
+
+<p>Django 本身提供的主要工具是一组用于创建和使用Django项目的Python脚本,以及可在你电脑的web 浏览器中测试本地Django web应用(在你的计算机,而不是在外部的web 服务器)。</p>
+
+<p>还有其他外部工具, 它们构成了开发环境的一部分, 我们将不再赘述。这些包括 <a href="/en-US/docs/Learn/Common_questions/Available_text_editors">文本编辑器</a> 或编辑代码的IDE,以及像 <a href="https://git-scm.com/">Git</a> 这样的源代码控制管理工具,用于安全地管理不同版本的代码。我们假设你已经安装了一个文本编辑器。</p>
+
+<h3 id="什么是Django设置选项">什么是Django设置选项?</h3>
+
+<p>Django 在安装和配置方面非常灵活。Django可以:</p>
+
+<ul>
+ <li>安装在不同的操作系统上。</li>
+ <li>通过源代码、Python包索引(PyPi)进行安装,而大多数情况下,是通过主机的包管理应用程序安装的。</li>
+ <li>配置为使用几个数据库之一,可能还需要单独安装和配置。</li>
+ <li>在主系统的Python环境或在单独的Python虚拟环境中运行。</li>
+</ul>
+
+<p>每个选项都需要略微不同的配置和设置。以下小节解释了你的一些选择。在本文的其余部分中,我们将介绍Django在几个操作系统上的设置,并且在本教程的剩余模块中将假设你已进行该设置。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 其他可能的安装选项在官方Django文档中介绍。<a href="#furtherreading">相应文件 点击这里</a>.</p>
+</div>
+
+<h4 id="支持哪些操作系统">支持哪些操作系统?</h4>
+
+<p>Django web应用程序能运行在几乎任何可以运行Python3的计算机上:Windows,Mac OSX,Linux/Unix,Solaris,仅举几例。几乎任何计算机都具备在开发期间运行Django所需的性能。</p>
+
+<p>在本文中。我们将提供Windows,macOS 和Linux/Unix的说明。</p>
+
+<h4 id="你应该使用什么版本的Python">你应该使用什么版本的Python?</h4>
+
+<p>我们建议你使用最近发行的版本,在本文档写作的时候是Python 3.8.2。</p>
+
+<p>事实上,Python 3.5 以及更新的版本都可以用来开发,不过对Python 3.5的支持可能会在未来的版本更新中被移除。</p>
+
+<p>我们建议你使用最新版本的Python 3,除非该站点依赖于仅适用于Python 2 的第三方库。本文将介绍如何为Python 3安装环境(Python 2 的等效设置将非常相似)。</p>
+
+<ul>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>: Python 2.7 无法用于当前的 Django 发行版本(Django 1.11.x 系列是最后支持 Python 2.7 的版本)。</p>
+</div>
+
+<h4 id="我们在哪里下载Django">我们在哪里下载Django?</h4>
+
+<p>有三个地方可以下载Django:</p>
+
+<ul>
+ <li>Python包资源库 (PyPi)。并用<strong> pip </strong>工具进行安装,这是获取Django 最新稳定版本的最佳方式。</li>
+ <li>计算机软件包管理器。与操作系统捆绑在一起的Django发行版是一种常见的安装途径。请注意,打包的版本可能很老,且只能安装到系统Python 环境中(而这可能不是你想要的)。</li>
+ <li>源代码。你可以从源代码获得并安装最新版本的Django。这并不推荐给初学者,但是当你准备好开始贡献给Django项目本身的时候,它是必需的。</li>
+</ul>
+
+<p>本文介绍如何从PyPi安装Django的最新稳定版本。</p>
+
+<h4 id="哪个数据库">哪个数据库?</h4>
+
+<p>Django支持四个主要数据库(PostgreSQL,MySQL,Oracle和SQLite),还有一些社区库可以为其他流行的SQL和NOSQL数据库提供不同级别的支持。我们建议你为生产和开发选择相同的数据库(尽管Django使用其对象关系映射器(ORM)抽象了许多数据库之间的差异,但是仍然存在本可以避免的<a href="https://docs.djangoproject.com/en/1.10/ref/databases/">潜在问题</a> ).</p>
+
+<p>对于本文(和本模块的大部分),我们将使用将数据存储在文件中的SQLite数据库。SQLite旨在用作轻量级数据库,不能支持高并发。然而,这确实是只读的应用程序的绝佳选择。</p>
+
+<div class="note">
+<p><strong>注意:</strong>当你使用标准工具(django-admin)启动你的网站项目时,Django将默认使用SQLite。用来入门时,这是一个很好的选择,因为它不需要额外的配置和设置。</p>
+</div>
+
+<h4 id="安装本机系统还是Python虚拟环境中">安装本机系统还是Python虚拟环境中?</h4>
+
+<p>当你安装Python3时,将获得一个由所有Python3代码共享的全局环境。虽然你可以在该环境中安装任何你喜欢的Python包,但是每次只能安装每个包的一个特定版本。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>:安装到全局环境的Python应用程序可能会相互冲突(例如如果它们依赖于同一包的不同版本)。</p>
+</div>
+
+<p>如果你把Django安装到默认/全局环境中,那么在该计算机上将只能定位到Django的一个版本。如果你想创建新的网站(使用最新版本的Django),同时仍然维护依赖旧版本的网站,这可能是个问题。</p>
+
+<p>因此,经验丰富的Python/Django开发人员通常在独立Python虚拟环境中运行Python应用程序。这样就可以在一台计算机上实现多个不同的Django环境。Django开发团队同样建议你使用Python虚拟环境。</p>
+
+<p>本模块假设已经将Django安装到虚拟环境中,下面我们会演示如何进行。</p>
+
+<h2 id="安装_Python_3">安装 Python 3</h2>
+
+<p>为了使用Django,你需要在你的操作系统中安装Python。如果你使用Python3,那么你同样需要<a href="https://pypi.python.org/pypi">Python 包管理工具</a>  — <em>pip3</em> — 用来管理 (安装,更新和删除)被Django和其他Python应用程序使用的Python软件包/库。</p>
+
+<p>本节简要介绍了如何检查有哪些版本的Python,并根据需要安装适用于 <strong>Ubuntu Linux 16.04,macOS, and Windows 10</strong>的新版本。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 根据你的平台, 您还可以从操作系统自己的软件包管理器或其他机制安装Python / pip。对于大多数平台,您可以从<a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a>下载所需的安装文件,并使用该平台特定的方法进行安装。</p>
+</div>
+
+<h3 id="Ubuntu_18.04">Ubuntu 18.04</h3>
+
+<p>Ubuntu Linux 18.04 LTS默认包含Python 3.6.6。你可以通过在Bash终端中运行以下命令来确认这一点:</p>
+
+<pre class="notranslate"><span style="line-height: 1.5;">python3 -V
+ Python 3.6.6</span></pre>
+
+<p>然而,在默认情况下,为Python 3(包括Django)安装软件包的Python包管理工具<strong>不可用。你</strong>可以在<strong>bash</strong>终端中使用以下命令安装<strong>pip3</strong><strong>:</strong></p>
+
+<pre class="notranslate">sudo apt-get install python3-pip
+</pre>
+
+<h3 id="macOS">macOS</h3>
+
+<p>macOS 的"El Capitan" 及其他最新版本不包含Python 3。你可以通过在bash终端中运行一下命令来确认:</p>
+
+<pre class="notranslate"><span style="line-height: 1.5;">python3 -V
+ </span>-bash: python3: command not found</pre>
+
+<p>你可以轻松地从<a href="https://www.python.org/"> python.org</a>安装Python 3(以及pip3工具):</p>
+
+<ol>
+ <li>下载所需的安装程序:
+ <ol>
+ <li>点击 <a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></li>
+ <li>选择 <strong>Download Python 3.8.2</strong>  (具体的版本号可能不同)。</li>
+ </ol>
+ </li>
+ <li>使用Finder找到安装包,然后双击运行,并按照提示进行安装。</li>
+</ol>
+
+<p>之后可以通过检查Python3版本确认是否安装成功,如下所示:</p>
+
+<pre class="notranslate"><span style="line-height: 1.5;">python3 -V
+Python 3.8.2</span></pre>
+
+<p>你也可以通过列出可用的包来检查pip3是否安装了:</p>
+
+<pre class="notranslate">pip3 list</pre>
+
+<h3 id="Windows_10">Windows 10</h3>
+
+<p>windows默认不包含Python, 但你可以从<a href="https://www.python.org/"> python.org</a>轻松地安装它(以及pip3工具):</p>
+
+<ol>
+ <li>下载所需版本:
+ <ol>
+ <li>点击 <a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></li>
+ <li>选择 <strong>Download Python 3.8.2</strong>  (具体的版本号可能不同)。</li>
+ </ol>
+ </li>
+ <li>双击现在的文件并按照提示安装Python。</li>
+ <li>确保勾选了"Add Python to PATH"选项。</li>
+</ol>
+
+<p>你可以在命令提示符中输入以下内容来验证是否安装了Python:</p>
+
+<pre class="notranslate"><span style="line-height: 1.5;">python -V
+ Python 3.8.2</span>
+</pre>
+
+<p>Windows安装程序默认包含pip3 (Python包管理器)。同样在命令提示符中输入以下内容来列出已安装的包:</p>
+
+<pre class="notranslate"><span style="line-height: 1.5;">pip3 list</span>
+</pre>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong>安装包应该已把运行上述命令所需的一切设置完成。但如果你得到的消息是找不到Python,那么你可能忘记将Python添加到系统路径中了。你可以通过再次运行安装包,选择"Modify",并在下一页面中勾选 "Add Python to environment variables"来修复这个问题。</p>
+</div>
+
+<h2 id="在Python虚拟环境中使用Django">在Python虚拟环境中使用Django</h2>
+
+<p>我们使用<a href="https://virtualenvwrapper.readthedocs.io/en/latest/index.html" rel="noopener">virtualenvwrapper</a>(Linux及macOS)和 <a href="https://pypi.python.org/pypi/virtualenvwrapper-win" rel="noopener">virtualenvwrapper-win</a>(WIndows)来创建Python虚拟环境,而它们又使用了<a href="https://developer.mozilla.org/en-US/docs/Python/Virtualenv">virtualenv</a>。封装工具创建了一个一致的接口来管理各个平台上的接口。</p>
+
+<h3 id="安装虚拟环境软件">安装虚拟环境软件</h3>
+
+<h4 id="Ubuntu虚拟环境设置">Ubuntu虚拟环境设置</h4>
+
+<p>安装了Python和pip之后,你就可以安装virtualenvwrapper(包括了virtualenv)。可以在<a href="http://virtualenvwrapper.readthedocs.io/en/latest/install.html">这里</a>找到正式的安装指南,或按照以下指导操作。</p>
+
+<p>使用pip3安装该工具:<span>​​​​​​</span></p>
+
+<pre class="notranslate"><code>sudo pip3 install virtualenvwrapper</code></pre>
+
+<p>然后将以下代码行添加到shell启动文件的末尾(这是主目录中的一个隐藏文件,名字是.bashrc)。这些文件设置了虚拟环境应该存在的位置、开发项目目录的位置以及与这个包一起安装的脚本的位置。</p>
+
+<pre class="notranslate"><code>export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export VIRTUALENVWRAPPER_VIRTUALENV_ARGS=' -p /usr/bin/python3 '
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh</code></pre>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong><code>VIRTUALENVWRAPPER_PYTHON</code> 和 <code>VIRTUALENVWRAPPER_VIRTUALENV_ARGS</code>变量指向Python3的常规安装位置,<code>source /usr/local/bin/virtualenvwrapper.sh</code>指向<code>virtualenvwrapper.sh</code>脚本的一般安装位置。 如果您在测试时发现<em>virtualenv</em>无法正常工作,则要检查的一件事是Python和该脚本是否在预期的位置(然后适当更改启动文件)。</p>
+
+<p>你可以使用<code>which virtualenvwrapper.sh</code> 和 <code>which python3</code>命令为你的系统找到正确的安装位置。</p>
+</div>
+
+<p>然后通过在终端中运行以下命令重载启动文件:</p>
+
+<pre class="notranslate"><code>source ~/.bashrc</code></pre>
+
+<p>此时,你应该能看到一些脚本正在运行,如下所示:</p>
+
+<pre class="notranslate"><code>virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/premkproject
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postmkproject
+...
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/preactivate
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postactivate
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/get_env_details</code></pre>
+
+<p>然后你就可以使用 <code>mkvirtualenv</code>命令创建一个新的虚拟环境。</p>
+
+<h4 id="macOS_虚拟环境设置">macOS 虚拟环境设置</h4>
+
+<p>在macOS上设置<em>virtualenvwrapper</em> 几乎和在Ubuntu上是一样的(你同样可以按照以下指导操作,或在<a href="https://virtualenvwrapper.readthedocs.io/en/latest/install.html">这里</a>找到正式的安装指南)。</p>
+
+<p>使用<em>pip</em>安装<em>virtualenvwrapper</em>(并绑定<em>virtualenv</em>),如下所示。</p>
+
+<pre class="notranslate"><code>sudo pip3 install virtualenvwrapper</code></pre>
+
+<p>然后将以下代码行添加到shell启动文件的末尾:</p>
+
+<pre class="notranslate"><code>export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh</code></pre>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong><code>VIRTUALENVWRAPPER_PYTHON</code> 和 <code>VIRTUALENVWRAPPER_VIRTUALENV_ARGS</code>变量指向Python3的常规安装位置,<code>source /usr/local/bin/virtualenvwrapper.sh</code>指向<code>virtualenvwrapper.sh</code>脚本的一般安装位置。 如果您在测试时发现<em>virtualenv</em>无法正常工作,则要检查的一件事是Python和该脚本是否在预期的位置(然后适当更改启动文件)。</p>
+
+<p>例如,在macOS上的一个安装测试中,启动文件中必须有以下几行代码:</p>
+
+<pre class="notranslate"><code>export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/Library/Frameworks/Python.framework/Versions/3.7/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /Library/Frameworks/Python.framework/Versions/3.7/bin/virtualenvwrapper.sh</code></pre>
+
+<p>你可以使用<code>which virtualenvwrapper.sh</code> 和 <code>which python3</code>命令为你的系统找到正确的安装位置。</p>
+</div>
+
+<p>此处使用和Ubuntu相同的代码行,但是启动文件是主目录中叫做<strong>.bash_profile</strong>的隐藏文件。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong>如果找不到<strong>.bash_profile</strong>进行编辑,也可以使用nano在终端中打开它,命令看起来类似于:</p>
+
+<pre class="notranslate"><code>cd ~ # Navigate to my home directory
+ls -la #List the content of the directory. YOu should see .bash_profile
+nano .bash_profile # Open the file in the nano text editor, within the terminal
+# Scroll to the end of the file, and copy in the lines above
+# Use Ctrl+X to exit nano, Choose Y to save the file.</code></pre>
+</div>
+
+<p>然后通过在终端中运行以下命令重载启动文件:</p>
+
+<pre class="notranslate"><code>source ~/.bashrc</code></pre>
+
+<p>此时,你应该能看到一些脚本正在运行(和Ubuntu中同样的脚本)。然后你就可以使用 <code>mkvirtualenv</code>命令创建一个新的虚拟环境。</p>
+
+<h4 id="Windows_10_虚拟环境设置">Windows 10 虚拟环境设置</h4>
+
+<p>安装 <a href="https://pypi.python.org/pypi/virtualenvwrapper-win" rel="noopener">virtualenvwrapper-win</a> 甚至比设置<em>virtualenvwrapper</em> 更简单,因为你无需配置工具用来存储虚拟环境信息的位置(有一个默认值)。你需要做的只是在命令提示符中运行以下命令:</p>
+
+<pre class="notranslate"><code>pip3 install virtualenvwrapper-win</code></pre>
+
+<p>然后你就可以使用 <code>mkvirtualenv</code>命令创建一个新的虚拟环境。</p>
+
+<h3 id="创建虚拟环境">创建虚拟环境</h3>
+
+<p>一旦你成功安装了<em>virtualenvwrapper</em> 或 <em>virtualenvwrapper-win,</em>那么在所有平台中使用虚拟环境的方法是非常相似的。</p>
+
+<p>现在你可以使用 <code>mkvirtualenv</code>命令创建一个新的虚拟环境。在运行此命令时,你将看到正在设置的环境(你所看到的只略微与平台相关)。命令完成后,新的虚拟环境将被激活——你能看到提示符的开头就是括号中的环境名称(以下我们展示的是Ubuntu的,但是在Windows/macOS上,末行时相似的|)</p>
+
+<pre class="notranslate"><code>$ mkvirtualenv my_django_environment
+
+Running virtualenv with interpreter /usr/bin/python3
+...
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/t_env7/bin/get_env_details
+(my_django_environment) ubuntu@ubuntu:~$</code></pre>
+
+<p>现在你已进入虚拟环境,可以进行Django安装并开始开发。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong>从现在开始,在本文(实际上是该模块)中,请假定所有命令都在类似于我们上面设置的Python虚拟环境中运行。</p>
+</div>
+
+<h3 id="使用一个虚拟环境">使用一个虚拟环境</h3>
+
+<p>您应该知道一些其他有用的命令(在工具的文档中还有更多,但这些是您将经常使用的命令):</p>
+
+<ul>
+ <li><code>deactivate</code> —退出当前的Python虚拟环境</li>
+ <li><code>workon</code> — 列出可用的所有虚拟环境</li>
+ <li><code>workon name_of_environment</code> —激活特定的Python虚拟环境</li>
+ <li><code>rmvirtualenv name_of_environment</code> — 移除特定的虚拟环境</li>
+</ul>
+
+<div class="blockIndicator note"></div>
+
+<h2 id="安装Django">安装Django</h2>
+
+<p>一旦你创建了一个虚拟环境,并且使用<code>workon</code> 进入了它,就可以使用pip3来安装Django。</p>
+
+<pre class="notranslate">pip3 install django</pre>
+
+<p>您可以通过运行以下命令来测试Django是否安装(这只是用来测试Python是否可以找到Django模块):</p>
+
+<pre class="notranslate"># Linux/macOS
+python3 -m django --version
+ 1.10.10
+
+# Windows
+py -3 -m django --version
+ 1.10.10
+</pre>
+
+<div class="note">
+<p><strong style='background-color: #fff3d4; border: 0px; color: #333333; font-family: x-locale-heading-primary,zillaslab,Palatino,"Palatino Linotype",x-locale-heading-secondary,serif; font-size: 18px; font-style: normal; font-weight: 700; letter-spacing: normal; margin: 0px; padding: 0px; text-align: start; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal;'><font><font>注意</font></font></strong><font><font>:</font></font>如果上面的Windows命令没有显示django模块,请尝试:</p>
+
+<pre class="notranslate"><code>py -m django --version</code></pre>
+
+<p>在Windows中,Python 3脚本是通过在命令前面加上<code>py -3</code>来启动的,尽管该脚本可能会因您的特定安装而有所不同。 如果遇到命令问题,请尝试省略<code>-3</code>修饰符。 在Linux /macOS中,命令是python3。</p>
+</div>
+
+<div class="warning">
+<p>重要提示:本模块的其余部分使用Linux命令来调用Python 3(<code>python3</code>)。如果您在Windows上工作,只需将此前缀替换为: <code>py -3</code></p>
+</div>
+
+<h2 id="测试你的安装">测试你的安装</h2>
+
+<p>上面的测试工作并不是很有趣。一个更有趣的测试是创建一个框架项目并查看它的工作情况。要做到这一点,先在你的命令提示符/终端导航到你想存储你<strong>Django</strong>应用程序的位置。为您的测试站点创建一个文件夹并进入其中。</p>
+
+<pre class="notranslate">mkdir django_test
+cd django_test
+</pre>
+
+<p>然后,您可以像所展示的一样使用django-admin工具创建一个名为“<em> mytestsite </em>” 的新框架站点。创建网站后,您可以CD到此文件夹,并将在其中找到管理项目的主要脚本,名为<strong>manage.py</strong>。</p>
+
+<pre class="notranslate">django-admin startproject mytestsite
+cd mytestsite</pre>
+
+<p>我们可以在这个文件夹中使用<strong>manager.py</strong>和<code>runserver</code>命令运行<em>开发web服务器</em>,如下所示。</p>
+
+<pre class="notranslate"><code>$ python3 manage.py runserver
+Performing system checks...
+
+System check identified no issues (0 silenced).
+
+You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
+Run 'python manage.py migrate' to apply them.
+
+December 16, 2018 - 07:06:30
+Django version 2.2.12, using settings 'mytestsite.settings'
+Starting development server at http://127.0.0.1:8000/
+Quit the server with CONTROL-C.</code></pre>
+
+<div class="note">
+<p>注意:上面的命令显示了Linux /macOS命令。您可以忽略关于“15 unapplied migration(s)”的警告!</p>
+</div>
+
+<p>一旦服务器运行,您可以通过本地Web浏览器打开<code>http://127.0.0.1:8000/</code>来查看该站点。你应该看到一个如下所示的网站:</p>
+
+<p><img alt="The home page of the skeleton Django app." src="https://mdn.mozillademos.org/files/16288/Django_Skeleton_Website_Homepage_2_1.png" style="height: 714px; width: 806px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="概要">概要</h2>
+
+<p>现在,你的计算机中已经启动并运行了一个Django开发环境。</p>
+
+<p>在测试部分,您还简要地了解了如何使用d<code>jango -admin startproject</code>创建一个新的Django网站,并使用开发web服务器(<code>python3 manager .py runserver</code>)在浏览器中运行它。在下一篇文章中,我们将对此过程进行扩展,构建一个简单但完整的web应用程序。</p>
+
+<h2 id="看看瞧瞧">看看瞧瞧</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/install/">快速安装指南</a>(Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/install/">如何安装Django — 完整指南</a> (Django docs) - 包含有关如何删除Django的信息</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/windows/">如何安装Django在 Windows</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}</p>
diff --git a/files/zh-cn/learn/server-side/django/管理站点/index.html b/files/zh-cn/learn/server-side/django/管理站点/index.html
new file mode 100644
index 0000000000..d3252d84c5
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/管理站点/index.html
@@ -0,0 +1,339 @@
+---
+title: 'Django Tutorial Part 4: Django 管理员站点'
+slug: learn/Server-side/Django/管理站点
+translation_of: Learn/Server-side/Django/Admin_site
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">好了,我们已经为本地图书馆网站 <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> 创建了模型,我们接下来使用 Django 管理站点去添加 一些 “真“书数据。首先我们展示如何用管理站点注册模型,然后展示如何登录和创建一些数据。本文最后,我们介绍你可以进一步改进管理站点的建议。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>完成: <a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: 使用模型</a>。</td>
+ </tr>
+ <tr>
+ <th scope="row">目的:</th>
+ <td>
+ <p>了解关于管理站点的优点与缺点,并且可以使用它为我们模型创建一些记录。</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="综述">综述</h2>
+
+<p>Django管理应用程序可以使用您的模型自动构建可用于创建,查看,更新和删除记录的站点区域。这可以在开发过程中节省大量的时间,从而很容易测试您的模型,并了解您是否拥有正确的数据。根据网站的类型,管理应用程序也可用于管理生产中的数据。Django项目建议仅用于内部数据管理(即仅供管理员或组织内部人员使用),因为以模型为中心的方法不一定是所有用户最好的界面,并且暴露了大量不必要的细节关于模型。</p>
+
+<p><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">创建基础项目时,</a>自动完成所有将您的网站中的管理应用程序包含在内的配置文件 (有关所需实际依赖关系的信息  (如有需要请看 <a href="https://docs.djangoproject.com/en/1.10/ref/contrib/admin/">Django docs here</a>). 其结果是,你必须做你的模型添加到管理应用程序是  注册  他们。在本文末尾,我们将简要介绍如何进一步配置管理区域以更好地显示我们的模型数据。</p>
+
+<p>注册模型后,我们将展示如何创建一个新的“超级用户”,登录到该网站,并创建一些书籍,作者,书籍实例和流派。这些将有助于测试我们将在下一个教程中开始创建的视图和模板。</p>
+
+<h2 id="注册模型">注册模型</h2>
+
+<p>首先,在目录应用程序(<strong>/locallibrary/catalog/admin.py</strong>)中打开 <strong>admin.py </strong>。此时此刻它看起来像这样—注意它已经导入了django.contrib.admin:</p>
+
+<pre class="brush: python">from django.contrib import admin
+
+# Register your models here.
+</pre>
+
+<p>通过将以下文本复制到文件的底部来注册模型。该代码简单地导入模型,调用 <strong>admin.site.register </strong>来注册它们。</p>
+
+<pre class="brush: python">from .models import Author, Genre, Book, BookInstance
+
+admin.site.register(Book)
+admin.site.register(Author)
+admin.site.register(Genre)
+admin.site.register(BookInstance)
+</pre>
+
+<div class="note"><strong>注意</strong>: 如果你接受创建模型以表示书籍的自然语言的挑战(<a href="/en-US/docs/Learn/Server-side/Django/Models">see the models tutorial article</a>), 导入并注册。</div>
+
+<p>这是在网站上注册模型或多模型的简单方法,管理站点是高度可定制的,我们将进一步讨论注册模型的其他方式。</p>
+
+<h2 id="创建一个超级用户">创建一个超级用户</h2>
+
+<p>为了登录管理员站点,我们需要启动工作人员状态的用户账户。为了查看和创建记录,我们还需要该用户具有所有对象的记录。你可以创建一个“超级用户”账号,该账号具有完全访问该站点和所有必需的权限可以使用<code>manage.py </code></p>
+
+<p>调用接下来的命令,在同样的目录下,<code>manage.py 创建超级用户。你将被提示输入用户名,电子邮件地址,和强密码。</code></p>
+
+<pre class="brush: bash">python3 manage.py createsuperuser
+</pre>
+
+<p>一旦命令完成,一个新超级用户将被添加到数据库。现在重新启动开发服务器,以便我们可以测试登录名:</p>
+
+<pre class="brush: bash">python3 manage.py runserver
+</pre>
+
+<h2 id="登入并使用该网站">登入并使用该网站</h2>
+
+<p>登录网站,打开 <code>/admin</code> (e.g. <a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin</a>)<br>
+ 和进入你的新超级用户名和密码凭据(你将被重定向到 登录页面,然后在你进入你的详细信息后回到 <code>/admin</code> URL</p>
+
+<p>这部分网站展示我们所有的模型,按安装的应用程序分组。你可以点击模型名称来进入到 它所有相关详细记录的页面,你可以进一步点击这些记录进行编辑。你也可以直接点击每个模型旁边的添加链接,开始创建该类型的记录。</p>
+
+<p><img alt="Admin Site - Home page" src="https://mdn.mozillademos.org/files/13975/admin_home.png" style="display: block; height: 634px; margin: 0px auto; width: 998px;"></p>
+
+<p>点击图书右侧的添加链接来新建一本书(这将显示一个类似下面的对话框)。注意每个字段标题,使用的小部件的类型以及<strong>help_text</strong>(如果有的话)你要在模型中匹配指定的值。</p>
+
+<p>输入字段的值,你可以创建一个新的作者或类型通过 按 <code>+</code> 按钮(或者如果你已经创建选项,选择已有的值)。完成后,你可以按 <strong>保存</strong>,<strong>保存并添加另一个</strong>,或<strong>保存并继续编辑</strong>来保存记录。</p>
+
+<p><img alt="Admin Site - Book Add" src="https://mdn.mozillademos.org/files/13979/admin_book_add.png" style="border-style: solid; border-width: 1px; display: block; height: 780px; margin: 0px auto; width: 841px;"></p>
+
+<div class="note">
+<p><strong>注意</strong>: 在这里,我们希望你花费一点时间添加一些书,作者,类型(如: 幻想)到你的应用。确保每个作者和类型都包含几本不同的书籍(这会是你的列表和详细视图在文章系列中后期使用时更有趣)。</p>
+</div>
+
+<p>我们完成添加书籍,在顶部标签中,点击 <strong>Home</strong> 链接将回到主管理页面。然后点击 <strong>Books</strong> 链接显示当前书籍的列表(或其他链接之一,以查看其他型号列表)。现在你已经添加了几本书,列表可能与下面的截图类似。显示每本书的标题;这是书模型 __str__() 方法返回的值,在上一文章中提到。</p>
+
+<p><img alt="Admin Site - List of book objects" src="https://mdn.mozillademos.org/files/13935/admin_book_list.png" style="border-style: solid; border-width: 1px; display: block; height: 407px; margin: 0px auto; width: 1000px;"></p>
+
+<p>从该列表中,您可以通过选中不需要的图书旁边的复选框来删除图书,从“ 操作”下拉列表中选择“ 删除”操作  ,然后按Go按钮。您也可以通过按下ADD BOOK按钮添加新书。</p>
+
+<p>您可以通过在链接中选择其名称来编辑书籍。一本书的编辑页面如下所示,与“添加”页面几乎相同。主要的区别是页面标题(更改书)和添加  删除,历史和<code>VIEW ON SITE</code>按钮(最后一个按钮出现,因为我们定义了<code>get_absolute_url()</code>我们的模型中的  方法)。</p>
+
+<p><img alt="Admin Site - Book Edit" src="https://mdn.mozillademos.org/files/13977/admin_book_modify.png" style="border-style: solid; border-width: 1px; display: block; height: 780px; margin: 0px auto; width: 841px;"></p>
+
+<p>现在回到主页(使用主页链接的导航痕迹),然后查看作者  和类型  列表 - 您应该已经有很多创建从添加新书,但可以自由添加一些更多。</p>
+
+<p>你不会有任何书籍实例,因为这些不是从图书创建的(虽然你可以从 <strong>BookInstance</strong> - 创建一个书  - 这是ForeignKey字段的性质)。返回主页,然后按关联的添加按钮显示下面的添加书实例屏幕。请注意,全球唯一的ID,可用于单独标识库中单书的副本。</p>
+
+<p><img alt="Admin Site - BookInstance Add" src="https://mdn.mozillademos.org/files/13981/admin_bookinstance_add.png" style="border-style: solid; border-width: 1px; display: block; height: 514px; margin: 0px auto; width: 863px;"></p>
+
+<p>为你的书创建一些记录。将状态设置为可用于至少一些记录,并为其他记录贷款。如果状态 不可 用,则还设置未来到期日期。</p>
+
+<p>而已!您现在已经学会了如何 设置和使用管理站点。您还创建书的记录,BookInstance,Genre,和Author 我们就可以一次我们创造我们自己的观点和模板使用。</p>
+
+<h2 id="高级配置">高级配置</h2>
+
+<p>Django 使用注册模型的信息为创建基本管理站点做了非常好的工作:</p>
+
+<ul>
+ <li>每个模型都有一个单独的记录列表,由使用模型 __str__()<br>
+ 方法创建的字符串标识,并链接到详细视图/表单进行编辑。默认,视图最上面有一个操作菜单,可用于对记录执行批量删除操作。</li>
+ <li>进行编辑和添加记录的模型详细记录表单包含 模型的所有字段,以其声明顺序垂直布置。</li>
+</ul>
+
+<p>你可以进一步自定义界面,使它更容易使用,你可以改进的一些想法:</p>
+
+<ul>
+ <li>视图列表:
+ <ul>
+ <li>添加每个记录显示的其他字段/信息</li>
+ <li>添加过滤器以根据日期或某些其他选择值(例如图书货款状态)选择列出哪些记录。</li>
+ <li>在列表视图中的操作菜单中添加其他选项,并选择此菜单在表单上显示的位置。</li>
+ </ul>
+ </li>
+ <li>详细视图
+ <ul>
+ <li>选择要显示(或排除)的字段,以及其顺序,分组,是否可编辑,使用的小部件,方向等。</li>
+ <li>将相关字段添加到记录以允许内联编辑(例如:添加在创建作者记录时添加和编辑图书记录的功能)。</li>
+ </ul>
+ </li>
+</ul>
+
+<p>在本节中,我们将看一些改进本地图书馆界面的更改,其中包括添加更多信息Book和Author 模型列表,以及改进编辑视图的布局。我们不会改变 Language 和 Genre 模拟演示,因为它们只有一个字段,所以这样没有真正的好处。</p>
+
+<p>你可以 在<a href="https://docs.djangoproject.com/en/1.10/ref/contrib/admin/">The Django Admin site </a>中找到所以管理员网站自定义选项的完整参考。</p>
+
+<h3 id="注册_一个_ModelAdmin_类">注册 一个 ModelAdmin 类</h3>
+
+<p>在管理界面去改变一个模型的展示方式,当你定义了 <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-objects">ModelAdmin</a> 类(描述布局)和将其注册到模型中。</p>
+
+<p>让我们开始作者模型。打开 <strong>admin.py</strong> 在目录应用程序(<strong>/locallibrary/catalog/admin.py</strong>)。注释你的原始注册(前缀为#)在<strong> Author</strong> 模型</p>
+
+<pre class="brush: js"># admin.site.register(Author)</pre>
+
+<p>现在添加一个 AuthorAdmin 和注册,如下</p>
+
+<pre class="brush: python"># Define the admin class
+class AuthorAdmin(admin.ModelAdmin):
+ pass
+
+# Register the admin class with the associated model
+admin.site.register(Author, AuthorAdmin)
+</pre>
+
+<p>我们再为<strong>Book</strong> 添加 <strong>ModelAdmin</strong> 类 和 <strong>BookInstance</strong> 类。我们需要注释我们原始注册:</p>
+
+<pre class="brush: js">#admin.site.register(Book)
+#admin.site.register(BookInstance)</pre>
+
+<p>现在创建和注册新的模型;为了演示的目的,我们将使用<code>@register 装饰器来注册模型(这和 admin.site.register()</code> 语法作用一样)。</p>
+
+<pre class="brush: python"># Register the Admin classes for Book using the decorator
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ pass
+
+# Register the Admin classes for BookInstance using the decorator
+
+@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+ pass
+</pre>
+
+<p>可以看到我们现在 的 类都是空的 (“pass”),所以管理操作并不会改变,我们现在对这些类进行扩展,以定义我们针对模型的管理行为。</p>
+
+<h3 id="配置列表视图">配置列表视图</h3>
+
+<p>该 本地图书馆 目前列出的所以作者都使用从模型生成的对象名称的<code>__str__()</code> 方法。如果只是几个作者,这无关紧要。但一旦你有许多作者,这可能会重复。要区分它们,或仅仅因为你想要显示有关每个作者的更多有趣的信息,你可以使用<a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display">list_display</a> 向视图添加其他字段。</p>
+
+<p>用下面的代码替代 你 <strong>AuthorAdmin</strong> 的类。在元组中声明要显示列表中的字段名称以所需的顺序排列,如图(这些和原始模型中指定的名称相同)。</p>
+
+<pre class="brush: python">class AuthorAdmin(admin.ModelAdmin):
+ list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
+</pre>
+
+<p>重新启动站点并导航到作者列表。现在应该显示上述字段,如下所示:</p>
+
+<p><img alt="Admin Site - Improved Author List" src="https://mdn.mozillademos.org/files/14023/admin_improved_author_list.png" style="border-style: solid; border-width: 1px; display: block; height: 302px; margin: 0px auto; width: 941px;"></p>
+
+<p>对于我们的Book模型,我们将另外显示<strong>author</strong>和<strong>genre</strong>。这author是一个<strong>ForeignKey</strong>字段(一对多)的关系,所以将由<code>__str()__</code>相关记录的值表示。用<strong>BookAdmin</strong>下面的版本替换课程。</p>
+
+<pre class="brush: python">class BookAdmin(admin.ModelAdmin):
+ list_display = ('title', 'author', 'display_genre')
+</pre>
+
+<p>不幸的是,我们不能直接指定 <strong>list_display </strong>中的 <strong>genre</strong> 字段, 因为它是一个<strong>ManyToManyField</strong> (Django可以防止这种情况,因为在这样做时会有大量的数据库访问“成本”)。相反,我们将定义一个 <code>display_genre </code>函数来获取信息作为一个字符串(这是我们上面调用的函数;下面我们将定义它)。</p>
+
+<div class="note">
+<p>注意:在<strong>genre</strong>这里获取可能不是一个好主意,因为数据库操作的“成本”。我们向您展示了如何在模型中调用函数的其他原因非常有用 - 例如在列表中的每个项目旁边添加一个“ 删除”链接。</p>
+</div>
+
+<p>将以下代码添加到Book模型(<strong>models.py</strong>)中。这将从 genre字段的前三个值(如果存在)创建一个字符串,并创建一个<code>short_description</code>可以在此方法的管理站点中使用的字符串。</p>
+
+<pre class="brush: python">    def display_genre(self):
+ """
+ Creates a string for the Genre. This is required to display genre in Admin.
+ """
+ return ', '.join([ genre.name for genre in self.genre.all()[:3] ])
+ display_genre.short_description = 'Genre'
+</pre>
+
+<p>保存模型并更新管理员后,重新启动站点并转到图书列表页面; 你应该看到像下面这样的书籍清单:</p>
+
+<p><img alt="Admin Site - Improved Book List" src="https://mdn.mozillademos.org/files/14025/admin_improved_book_list.png" style="border-style: solid; border-width: 1px; display: block; height: 337px; margin: 0px auto; width: 947px;"></p>
+
+<p>该Genre模型(和Language模式,如果你定义一个)都有一个单一的领域,所以没有一点为他们创造更多的显示领域的附加模型。</p>
+
+<div class="note">
+<p>注意:值得更新BookInstance模型列表,至少显示状态和预期的返回日期。我们已经补充说,作为本文末尾的挑战!</p>
+</div>
+
+<h3 id="添加列表过滤器">添加列表过滤器</h3>
+
+<p>一旦列表中有很多项目,就可以过滤哪些项目被显示出来。这是通过在<code>list_filter</code>属性中列出字段来完成的。用<code>BookInstanceAdmin</code>下面的代码片段替换你当前的  类。</p>
+
+<pre class="brush: python">class BookInstanceAdmin(admin.ModelAdmin):
+<strong> list_filter = ('status', 'due_back')</strong>
+</pre>
+
+<p>列表视图现在将在右侧包含一个过滤器框。请注意如何选择日期和状态来过滤值:</p>
+
+<p><img alt="Admin Site - BookInstance List Filters" src="https://mdn.mozillademos.org/files/14037/admin_improved_bookinstance_list_filters.png" style="height: 528px; width: 960px;"></p>
+
+<h3 id="整理细节视图布局">整理细节视图布局</h3>
+
+<p>默认情况下,详细视图按照其在模型中声明的顺序垂直排列所有字段。您可以更改声明的顺序,哪些字段显示(或排除),区段是否用于组织信息,字段是水平还是垂直显示,甚至是管理窗体中使用的编辑窗口小部件。</p>
+
+<div class="note">
+<p>注意:LocalLibrary模型比较简单,因此我们不需要更改布局; 不管怎样,我们会做一些改变,只是为了向你展示如何。</p>
+</div>
+
+<h4 id="控制哪些字段被显示和布局">控制哪些字段被显示和布局</h4>
+
+<p>更新您的  <code>AuthorAdmin</code> 类以添加<code>fields</code>行,如下所示(粗体):</p>
+
+<pre class="brush: python">class AuthorAdmin(admin.ModelAdmin):
+ list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
+<strong> fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]</strong>
+</pre>
+
+<p>在<code>fields</code> 属性列表只是要显示在表格上那些领域,如此才能。字段默认情况下垂直显示,但如果您进一步将它们分组在元组中(如上述“日期”字段中所示),则会水平显示。</p>
+
+<p>重新启动您的应用程序并转到作者详细信息视图 - 现在应该如下所示:</p>
+
+<p><img alt="Admin Site - Improved Author Detail" src="https://mdn.mozillademos.org/files/14027/admin_improved_author_detail.png" style="border-style: solid; border-width: 1px; display: block; height: 282px; margin: 0px auto; width: 928px;"></p>
+
+<div class="note">
+<p>注意:您还可以使用<code>exclude</code>属性来声明要从表单中排除的属性列表(将显示模型中的所有其他属性)。</p>
+</div>
+
+<h4 id="剖切细节视图"><font><font>剖切细节视图</font></font></h4>
+
+<p>你可以使用 <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets">fieldsets</a> 属性添加“部分”以在详细信息表单中对相关的模型信息进行分组。</p>
+
+<p>在  <code>BookInstance</code>模型中,我们有相关的书是什么(即信息  <code>name,imprint和id</code>),并且当将可用(<code>status,due_back</code>)。我们可以通过将粗体文本添加到我们的<code>BookInstanceAdmin</code>类中来将其添加到不同的部分  。</p>
+
+<pre class="brush: python">@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+ list_filter = ('status', 'due_back')
+
+<strong> fieldsets = (
+ (None, {
+ 'fields': ('book','imprint', 'id')
+ }),
+ ('Availability', {
+ 'fields': ('status', 'due_back')
+ }),
+ )</strong></pre>
+
+<p>每个部分都有自己的标题(或者None如果你不想要一个标题)和字典中的一个相关的元组 - 描述的格式很复杂,但是如果你看上面的代码片段,那么它们很容易理解。</p>
+
+<p>重新启动并导航到书籍实例视图; 表格应如下所示:</p>
+
+<p><img alt="Admin Site - Improved BookInstance Detail with sections" src="https://mdn.mozillademos.org/files/14029/admin_improved_bookinstance_detail_sections.png" style="border-style: solid; border-width: 1px; display: block; height: 580px; margin: 0px auto; width: 947px;"></p>
+
+<h3 id="关联记录的内联编辑"><font><font>关联记录的内联编辑</font></font></h3>
+
+<p>有时,可以同时添加关联记录是有意义的。例如,将书籍信息和有关您在同一详细信息页面上的特定副本的信息同时显示可能是有意义的。</p>
+
+<p>你可以通过声明 <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.inlines">inlines</a>, 类型 <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.TabularInline">TabularInline</a> (水平布局 ) or <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.StackedInline">StackedInline</a> (垂直布局 ,就像默认布局)这样做. 您可以通过在您的以下的粗体中添加以下行,将内容中的<code>BookInstance</code>信息添加到我们的Book详细信息中<code>BookAdmin</code>:</p>
+
+<pre class="brush: python"><strong>class BooksInstanceInline(admin.TabularInline):
+    model = BookInstance</strong>
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+    list_display = ('title', 'author', 'display_genre')
+<strong>    inlines = [BooksInstanceInline]</strong>
+</pre>
+
+<p>尝试重新启动您的应用程序,然后查看图书的视图 - 在底部您应该看到与本书相关的图书实例:</p>
+
+<p><img alt="Admin Site - Book with Inlines" src="https://mdn.mozillademos.org/files/14033/admin_improved_book_detail_inlines.png" style="border-style: solid; border-width: 1px; display: block; height: 889px; margin: 0px auto; width: 937px;"></p>
+
+<p>在这种情况下,我们所做的就是声明我们的<code>tablular</code>内联类,它只是从内联模型添加所有字段。您可以为布局指定各种附加信息,包括要显示的字段,其顺序,是否只读等。(有关详细信息,请参阅  <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.TabularInline">TabularInline</a> ). </p>
+
+<div class="note">
+<p>注意:这个功能有一些痛苦的限制!在上面的屏幕截图中,我们有三个现有的书籍实例,其次是新的书籍实例的三个占位符(看起来非常相似!)。默认情况下没有备用书实例会更好,只需使用“ 添加另一个书”实例链接添加它们,或者可以<code>BookInstance</code>从这里列出作为不可读的链接。第一个选项可以通过<code>extra</code>在<code>BookInstanceInline</code>模型中将属性设置为0 来完成,自己尝试一下。</p>
+</div>
+
+<h2 id="挑战自己"><span class="highlight-span" style="background-color: #333333; border: 0px; color: #ffffff; font-weight: 400; line-height: 1.25; margin: 0px; padding: 0px 4px;"><font><font>挑战自己</font></font></span></h2>
+
+<p>我们在本节学到了很多东西,所以现在是时候尝试一些事情了。</p>
+
+<p>1. 对于  <code>BookInstance</code>列表视图,添加代码以显示书籍,状态,到期日期和ID(而不是默认<code>__str__()</code>文本)。<br>
+ 2. 添加的在线上市Book项目的Author使用,因为我们做了同样的做法详细视图<code>Book/ BookInstance。</code></p>
+
+<h2 id="概要">概要</h2>
+
+<p>而已!您现在已经了解了如何以最简单和改进的形式设置管理站点,如何创建超级用户以及如何导航管理站点以及查看,删除和更新记录。一路上,您创建了一堆书籍,BookInstances,流派和作者,一旦我们创建了自己的视图和模板,我们就可以列出和展示。</p>
+
+<ul>
+</ul>
+
+<h2 id="进阶阅读">进阶阅读</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial02/#introducing-the-django-admin">Writing your first Django app, part 2: Introducing the Django Admin</a>  (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/contrib/admin/">The Django Admin site</a> (Django Docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}</p>