Django 入门指南(一)

打印 上一主题 下一主题

主题 849|帖子 849|积分 2547

原文:Beginning Django
  协议:CC BY-NC-SA 4.0
  一、Django 框架先容

Django 框架始于 2003 年,是由美国堪萨斯州劳伦斯的《世界日报》的 Adrian Holovaty 和 Simon Willison 完成的一个项目。2005 年,Holovaty 和 Willison 发布了该框架的第一个公开版本,并以比利时-法国吉他手坦哥·雷恩哈特的名字定名。
快进到 2017 年——Django 框架如今在 Django 软件基金会(DSF)的指导下运行,框架焦点有超过 1000 个贡献者,有超过 15 个发布版本,有超过 3000 个专门设计用于 Django 框架的包。 1
Django 框架仍旧忠实于它的劈头,即模型-视图-控制器(MVC)服务器端框架,设计用于操作关系数据库。尽管如此,Django 仍旧通过第三方包紧跟大多数 web 开发趋势,与非关系数据库(NoSQL)、及时互联网通信和现代 JavaScript 实践等技能一起运行。所有这些都说明了一点,Django 框架如今是许多组织选择的 web 开发框架,包括照片共享网站 Instagram 2 和 Pinterest3;公共广播系统4;美国国家地理5;而借助这本书,你的组织!
在这一章中,你将学习 Django 框架设计原则,这是理解一样平常利用 Django 框架的关键。接下来,您将学习怎样以各种方式安装 Django:作为 tar.gz 文件,利用 pip,利用 git,以及利用 virtualenv。
一旦安装了 Django 框架,您将学习怎样启动一个 Django 项目,以及怎样用关系数据库来设置它。接下来,您将了解 Django 框架中的焦点构件——URL、模板和应用——以及它们怎样相互协作来设置内容。最后,您将学习怎样设置 Django 管理站点,这是一个基于 web 的界面,用于访问毗连到 Django 项目的关系数据库。
Django 框架设计原则

假如你在 web 开发领域工作了足够长的时间,你最终会得出如许的结论:你可以用任何 web 框架和编程语言产生相同的结果。但是,虽然您实际上可以产生相同的结果,但是差别很大的是您创建解决方案所耗费的时间:创建原型的时间、添加新功能的时间、进行测试的时间、进行调试的时间、摆设到规模的时间等等。
从这个意义上来说,Django 框架利用了一套设计原则,与许多其他 web 框架相比,它产生了最有生产力的 web 开发过程之一。注意,我并不是说 Django 是银弹(例如,最好的原型,最具伸缩性);我是说,最终,Django 框架包含了一组设计原则和权衡,使其成为构建大多数大中型 web 应用所需特性的最有用的框架之一。
如今,虽然你可能以为我有偏见——毕竟我正在写一本关于这个主题的书——我将首先列出这些设计原则,如许你可以更好地理解是什么赋予了 Django 框架这种上风。
不重复自己(干)的原则

重复可能有助于夸大一个观点,但是对于 web 开发来说,这只会导致额外的耗时工作。究竟上,web 开发的本质是跨多个交互层(例如,HTML 模板、业务逻辑方法和数据库)进行操作,这本身就是重复的。
Django 框架确实试图强迫你不要重复自己,以是让我们看看 Django 是怎样强制不要重复自己的,以及为什么这是一件好事。
假设您想要构建一个 coffeehouse 应用来发布关于市肆的信息,并为客户提供一个联系表单。你需要做的第一件事是确定市肆和联系表格需要什么样的信息。图 1-1 展示了每个实体的两个 Django 模型的实体模型。

图 1-1。
Django models for store and contact entities
请注意图 1-1 中的 Django 模型每个都有不同的字段名称和数据类型来限定值。例如,语句name = models.CharField(max_length=30)告诉 Django 市肆名称应该最多包含 30 个字符,而语句email = models.EmailField()告诉 Django 联系人实体应该包含有用的电子邮件值。假如咖啡馆像大多数 web 应用一样,您通常会为市肆和联系人实体做以下事变:


  • 创建关系数据库表来生存实体信息。
  • 创建业务逻辑以确保实体符合需求。
  • 创建 HTML 表单以答应为实体提交数据。
  • 创建答应管理用户访问数据库中实体的界面。
  • 创建 REST 服务来公开移动应用版本的实体。
完成这最后一个任务列表的关键是,您可能会在数据库定义语言(DDL)、HTML 表单、业务验证逻辑和 URL 等方面重复许多雷同的信息(例如名称、值限定),这一过程不光耗时,而且容易出错。
基于像models.CharField(max_length=30)如许的语句,您可以生成一个 HTML 表单输入,一个 DDL 语句,并自动验证信息只包含 30 个字符,这不是更容易吗?这正是 Django 的干设计原则所做的。
图 1-2 展示了与图 1-1 相同的 Django 模型,以及您可以从相同的模型中生成的各种构造,而无需重复。

图 1-2。
Django models create separate constructs based on DRY principle
正如您在图 1-2 中看到的,代表 Django 模型的实体可以或许生成向公众展示的 HTML 表单、管理实体的管理界面、实施实体值的验证逻辑,以及生成代表实体的数据库表的 DDL。
虽然如今讨论从 Django 模型生成这种布局的实际技能还为时过早,但不消说,这比在 HTML 表单、DDL、验证逻辑和其他位置跟踪同一事物(例如,姓名、电子邮件)的多个引用要简朴得多。
从这个意义上说,Django 真正帮助你在一个地方定义事物,而不必在其他地方重复它们。注意,总是有可能重复自己来获得定制的行为,但是在默认情况下,Django 在几乎所有你用它做的事变中都执行 DRY 原则。
显性比隐性好

Django 利用的编程语言 Python 有一个雷同咒语的声明,称为“Python 的禅”,被定义为该语言的 Python 增强建议(PEP)的一部分,特别是 PEP 20。PEP 20 中的一个声明是“显式比隐式更好”,而且 Django 是基于 Python 的,这个原则也被牢记在心。
显式导致 web 应用容易被更多的人理解和维护。对于最初没有编写 web 应用的人来说,添加新功能或理解 web 应用背后的逻辑可能已经够难了,但是假如您加入了具有隐式行为的 mix 构造,用户在试图弄清晰隐式地做了什么时只会面对更大的波折。Explicit 确实需要更多的输入工作,但是当您将它与您可能面对的调试或解决问题的潜伏积极相比力时,这黑白常值得的。
让我们快速看一下 Django 在一个跨不同 MVC 框架利用的通用 web 开发布局中的显式性:一个视图方法。视图方法充当 MVC 框架中的 C(controller ),负责处理惩罚传入的请求,应用业务逻辑,然后用恰当的响应路由请求。
为了更好地理解这种明确性,我将给出一个 Django 视图方法和一个等价的 Ruby on Rails 视图方法,这两个方法执行相同的逻辑,即通过给定的 id 获取存储并将响应路由到模板。以下代码片段是 Ruby on Rails 版本;请注意带有#的行,它们是注释,表示正在发生的事变。
  1. class StoresController < ApplicationController
  2.   def show
  3.     # Automatic access to params, a ruby hash with request parameters and view parameters
  4.     @store = Store.find(params[:id])
  5.     # Instance variables like @store are automatically passed on to view template
  6.     # Automatically uses template views/stores/show.html.erb
  7.   end
  8. end
复制代码
尽管非常简洁,但请注意访问数据、将数据传递给模板和分配模板的过程中的所有隐式行为。下面的代码片段是一个等价的 Django 视图方法。
  1. # Explicit request variable contains request parameters
  2. # Other view parameters must be explicitly passed to views
  3. def detail(request, store_id):
  4.     store = Store.objects.get(id=store_id)
  5.     # Instance variables must be explicitly passed on to a view template
  6.     # Explicit template must be assigned
  7.     return render(request, 'stores/detail.html', {'store': store})
复制代码
请注意,在最后这段代码中,没有推测输入参数来自那里,它们被显式声明为 view 方法中的参数。别的,值被显式传递给模板,而且模板也被显式声明,因此逻辑对新来者来说更加友好。
Ruby on Rails 视图方法的隐含性通常被称为“魔力”,乃至被许多人以为是一种特性。之以是称之为‘魔法’,是因为某些行为是在幕后提供的。然而,除非你对框架和应用了如指掌,否则很难确定为什么会发生某些事变,这使得修复或更新变得更加困难。因此,尽管“魔术”可能在开始时为您节省几分钟或几个小时的开发时间,但它最终会耗费您几个小时或几天的维护时间。
以是就像在 Python 中一样,Django 框架总是偏爱显式方法而不是隐式技能。
需要指出的是,显式不等于冗长或多余。虽然与隐式驱动的 web 框架(例如 Rails)相比,您最终肯定会在 Django 中键入更多的代码,但正如在前面的 DRY principle 部分中所描述的那样,Django 框架尽力克制在 web 应用中引入不须要的代码。
最后,显式也不意味着没有默认值。Django 框架尽可能利用合理的缺省值,只是在不明显的地方不利用缺省值。本质上,Django 框架利用默认值,但是克制利用会产生“神奇”结果的默认值。
疏松耦合架构

Django 框架是一个 MVC 框架,跨多个层运行(例如,HTML 模板、业务逻辑方法和数据库)。然而,Django 非常注意维护跨这些层运行的所有组件的疏松耦合架构。
疏松耦合意味着构成 Django 应用的各个部分之间没有严格的依靠关系。例如,在 Django 中,直接从 HTML 模板提供内容是完全有用的,不需要利用业务逻辑或创建数据库。就像在 Django 中一样,放弃利用 HTML 模板并直接从业务逻辑方法返回原始数据也是完全有用的(例如,对于 REST 服务)。
本章后面的“设置内容:理解 URL、模板和应用”一节将更详细地举例说明 Django 的疏松耦合架构是怎样工作的。
安装 Django

安装 Django 框架有多种方法。你可以从 Django 的主站点 7 下载 Django,然后像安装普通的 Python 应用一样安装它。您也可以通过操作系统(OS)包管理工具下载并安装 Django,比如 Linux 发行版上提供的apt-get。
另一个选择是通过 Python 包管理器pip下载安装 Django。另有一种方法是直接在 github 上安装 Django。8Django 安装选项列表及其优缺点如表 1-1 所示。
表 1-1。
Django installation options - Pros and Cons
| 方法 | 同意的意见 | 骗局 | | --- | --- | --- | | 利用`pip` Python 包管理器下载/安装。(推荐选项) | 答应在虚拟 Python 情况中安装。依靠关系是自动处理惩罚的。 | 最新版本可能不可用。 | | 从主站点下载为`tar.gz`文件。 | 最容易获得最新的 Django 稳定版本。 | 需要手动下载和安装。需要额外管理 Django 依靠项(假如不利用 pip)。 | | 从 Git 下载。 | 访问最新的 Django 特性。 | 可能包含 bug。需要额外管理 Django 依靠项(假如不利用 pip)。 | | 从操作系统软件包管理器下载/安装(`apt-get`)。 | 易于安装。依靠关系是自动处理惩罚的。 | 最新版本可能不可用。安装在环球 Python 情况中。 | 正如表 1-1 中所夸大的,安装 Django 的推荐选项是利用 Python pip包管理器,因为它提供了最大的灵活性。接下来,我将描述利用这种方法安装 Django 的每个步骤,更紧张的是怎样启动并运行pip。
一旦我完成了这些步骤,我还将描述从一个tar.gz文件和从 git——利用pip——安装 Django 的步骤,假如您想尝试最新的 Django 特性,这可能会很有帮助。
安装 Python(先决条件)

由于 Django 是基于 Python 构建的,以是首先需要安装 Python 来运行 Django。最新的 Django 长期版本(LTS)是 1.11 版,这也是本书的重点。Django 1.11 要求您要么拥有 Python 2.7.x 版本,要么拥有 Python 3.4 或更高版本(3.5 或 3.6)。
假如这是你第一次利用 Python,注意 Python 2 和 Python 3 有很大的不同是很紧张的。虽然 Python 3 确实是将来,但要知道,自 2008 年第一个 Python 3 版本问世以来,将来就一直在酝酿之中,Python 2 一直顽固不化,直到 2016 年 12 月 Python 2.7.13 才问世。
那么 Django 应该用 Python 2 还是 Python 3 呢?就 Django 的焦点而言,它兼容两者,因此您可以轻松地在 Python 2 和 Python 3 之间切换。当涉及到第三方 Python 包和您计划自己编写的 Django Python 代码时,事变变得有点棘手。
虽然许多第三方 Python 包已经升级到可以在 Python 3 上运行,但这个过程一直很迟钝。正如我已经指出的,Python 3 的开发已经进行了将近 10 年,以是请注意,假如您选择 Python 3 路线,您可能会碰到无法与 Python 3 兼容的第三方 Python 包。
当涉及到您自己的 Django 应用代码时,理想的选择是让您的代码兼容 Python 2 和 Python 3——就像 Django 的焦点一样——这并不难,我将在整本书中利用这种技能。侧栏包含更多关于编写 Python 2 和 Python 3 兼容代码的细节。
如今,假如你想坚持利用 Python 2,请注意 Django 1.11 将是最后一个支持 Python 2 的 Django 版本——计划支持到 2020 年 4 月左右——以是假如你最终升级到比 Django 1.11 更高的版本,你还需要将所有应用代码升级到 Python 3——这就是为什么我推荐 Python 2 和 Python 3 双重兼容技能。假如你想坚持利用 Python 3,那是将来的趋势,只是要注意,如前所述,一些第三方包可能无法与 Python 3 兼容。
Django Compatibility With Python 2 and Python 3
Django 利用 6 个 9 个 来运行 Python 2 和 Python 3 兼容的逻辑。Six 是一组实用程序,涵盖了 Python 2 和 Python 3 之间的差别,答应相同的逻辑在 Python 2 和 Python 3 中划一地运行。Django 框架的内部——很少需要查抄或修改——已经利用了这种技能。
但是,假如您计划编写与 Python 2 和 Python 3 都兼容的 Django 应用代码,那么您需要对怎样编写代码有更多的了解。Django 发布了自己的关于各种语法和技能的指南,您需要遵循这些指南来使 Django 应用代码与 Python 2 和 Python 3、 10 技能一起工作,这些技能也在整本书中利用。
假如您利用 Unix/Linux 操作系统,Python 很可能安装在您的系统上。假如您在 Unix/Linux 终端上键入which python,它会返回一个响应(例如/usr/bin/python),这表示 Python 可执行文件的位置,假如没有响应,则表示 Python 可执行文件在系统上不可用。
假如您的系统上没有 Python,而且您利用的是 Debian 或 Ubuntu Linux 发行版,那么您可以利用 OS package manager apt-get 通过键入:apt-get install python来安装 Python。假如您的 Unix/Linux 发行版不是 Debian 或 Ubuntu,而且您需要安装 Python,请查阅您的 Unix/Linux 文档以获得可用的 Python 包,或者从 http://python.org/download/ 下载 Python 源代码来进行安装。
假如你有一个运行在 Windows 操作系统或 macOS 上的系统,Python 安装程序可以从 http://python.org/download/ 下载。
无论您的系统是什么操作系统,一旦您完成 Python 安装,请确保 Python 安装正确,而且可以从系统的任何地方访问。打开一个终端并键入python,您应该会进入一个 Python 交互式会话,如清单 1-1 所示。
  1. [user@∼]$ python
  2. Python 2.7.12 (default, Nov 19 2016, 06:48:10)
  3. [GCC 5.4.0 20160609] on linux2
  4. Type "help", "copyright", "credits" or "license" for more information.
  5. Listing 1-1.Python interactive session
复制代码
假如您无法进入 Python 交互式会话,请查看 Python 安装过程,因为您将无法继续以下部分。
更新或安装 pip 软件包管理器(先决条件)

为了使 Python 包的安装和管理更容易,Python 利用了一个名为 pip 的包管理器。假如您利用的是 Python 2.7.9(或更高版本的 2.x 分支)或 Python 3.4(或更高版本的 3.x 分支),默认情况下会安装 pip。如今让我们在您的系统上升级 pip,如清单 1-2 所示,假如您的系统上没有 pip,我将扼要说明怎样获得它。
  1. [user@∼]$ pip install --upgrade pip
  2. Collecting pip
  3.   Downloading pip-9.0.1-py2.py3-none-any.whl (1.3MB)
  4. Installing collected packages: pip
  5.   Found existing installation: pip 8.1.1
  6. Successfully installed pip-9.0.1
  7. Listing 1-2.Update pip package manager
复制代码
正如您在清单 1-2 中看到的,为了更新 pip,您调用带有参数install --upgrade pip的pip可执行文件。在执行时,pip 通过提供的名称搜索一个包——在本例中是 pip 本身——下载它,并在已经安装的情况下执行升级。假如您系统上的安装输出雷同于清单 1-2 中的输出——没有任何错误——您已经成功更新了 pip。
假如您看到一个错误,如程序“pip”当前未安装或 pip 未找到,这意味着您的 Python 安装没有配备 pip。在这种情况下,您需要通过下载 https://bootstrap.pypa.io/get-pip.py 来安装 pip 可执行文件,然后利用命令python get-pip.py执行下载的文件。一旦安装了pip可执行文件,运行清单 1-2 中的 pip 更新程序。
有了系统上的 pip,您就可以进入下一步了。
安装 virtualenv(可选先决条件)

Virtualenv 对于开发 Django 应用并不紧张,但是我猛烈建议您利用它,因为它答应您在单个系统上创建虚拟 Python 情况。通过利用虚拟 Python 情况,应用可以在独立于其他 Python 应用的“沙箱”中运行。最初,virtualenv 似乎没有什么好处,但它对于将开发情况复制到生产情况以及克制不同应用之间可能出现的版本冲突等任务来说,可能会有巨大的帮助。
没有 virtualenv,您仍旧可以利用 pip 继续安装 Django 和任何其他 Python 包,但问题是所有包都安装在全局 Python 安装下。最初这看起来很方便,因为在全局 Python 安装中只需要安装一次包。但是假如你思考下面的一些问题,就没那么方便了。
假如在你的第一个项目之后发布了一个新的 Django 版本,而你想开始第二个项目,会发生什么?您是升级第一个项目以便在新的 Django 版本上运行,还是启动第二个项目,就好像新的 Django 版本不存在一样?第一个选项需要额外的工作,而第二个选项需要您在过期的 Django 版本上进行开发。通过利用虚拟 Python 情况,可以克制这个问题,因为每个项目都可以独立运行自己的 Django 版本。
假如您考虑到任何 Python 包的潜伏版本冲突,您就会明白为什么我推荐您利用 virtualenv。许多 Python 包都有特定的版本依靠关系(例如,包 A 依靠于包 B 版本 2.3 和包 C 版本 1.5)。假如您用特定的交织依靠版本更新一个新的包,假如您利用的是全局 Python 安装,那么中断 Python 安装黑白常容易的。利用 virtualenv,您可以拥有多个 Python 安装,而不会相互干扰。
既然我已经解释了 virtualenv 的好处,让我们用 pip 安装virtualenv可执行文件,如清单 1-3 所示。
  1. [user@∼]$  pip install virtualenv
  2. Downloading/unpacking virtualenv
  3.   Downloading virtualenv-15.1.0.tar.gz (1.8Mb): 1.8Mb downloaded
  4.   Running setup.py egg_info for package virtualenv
  5.   Installing collected packages: virtualenv
  6.   Running setup.py install for virtualenv
  7.   Installing virtualenv script to /usr/local/bin
  8.   Installing virtualenv-2.7 script to /usr/local/bin
  9. Successfully installed virtualenv
  10. Cleaning up...
  11. Listing 1-3.Install virtualenv
  12. with pip
复制代码
如清单 1-3 所示,pip自动下载并安装所请求的包。雷同于pip可执行文件,还安装了一个virtualenv可执行文件,可以从系统的任何地方访问。virtualenv可执行文件答应您创建虚拟 Python 情况。清单 1-4 展示了怎样用virtualenv创建一个虚拟的 Python 情况。
  1. [user@∼]$ virtualenv --python=python3 mydjangosandbox
  2. Already using interpreter /usr/bin/python3
  3. Using base prefix '/usr'
  4. New python executable in /mydjangosandbox/bin/python3
  5. Also creating executable in /mydjangosandbox/bin/python
  6. Installing setuptools, pkg_resources, pip, wheel...done.
  7. Listing 1-4.Create virtual Python environment
  8. with virtualenv
复制代码
virtualenv可执行文件接受几个参数。清单 1-4 中的任务利用了--python标记,它告诉 virtualenv 基于python3可执行文件创建一个虚拟 Python,从而创建一个 Python 3 虚拟情况。当一个操作系统上有多个 Python 版本(例如 Python 2 和 Python 3)而且需要指定创建 virtualenv 的 Python 版本时,这是一个常见的选项。可以省略--python标记;请注意,如许做 virtualenv 是用默认的操作系统python可执行文件创建的。
默认情况下,virtualenv 会创建一个原始的虚拟 Python 情况,就像您最初进行 Python 全局安装时的情况一样。在 virtualenv 参数之后,您只需要为虚拟 Python 情况的名称指定一个参数,在清单 1-4 的情况下是mydjangosandbox。在执行时,virtualenv 创建一个包含虚拟 Python 情况的目次,其内容如清单 1-5 所示。
  1. +<virtual_environment_name>
  2. |
  3. |
  4. +---+-<bin>
  5. |   |
  6. |   +-activate
  7. |   +-easy_install
  8. |   +-pip
  9. |   +-python
  10.     +-python-config
  11. |   +-wheel
  12. |
  13. +---+-<include>
  14. |
  15. +---+-<lib>
  16. |
  17. +---+-<local>
  18. |
  19. +---+-<share>
  20. Listing 1-5.Virtual Python environment directory structure
复制代码
Tip
取决于用于创建 virtualenv 的 Python 版本,bin 目次可以包含同一命令的多个别名或版本(例如,除了python、python2.7和python3;除了activate、activate.csh、activate_this.py。
Note
当地文件夹仅包含在 Python 2 virtualenv 中,并链接到虚拟目次的顶级目次以模拟 Python 安装。
如清单 1-5 所示,虚拟 Python 情况的目次布局雷同于全局 Python 安装。bin目次包含虚拟情况的可执行文件,include目次链接到全局 Python 安装头文件,lib目次是全局 Python 安装库的副本,也是安装虚拟情况的包的地方,share目任命于放置共享的 Python 包。
虚拟情况最紧张的部分是bin目次下的可执行文件。假如您利用这些可执行文件中的任何一个,比如pip、easy_install、python,或wheel,,它们将在虚拟 Python 情况的上下文中执行。例如,bin文件夹下的pip为虚拟情况安装软件包。雷同地,在bin文件夹下的python可执行文件上运行的应用只能加载安装在虚拟 Python 情况中的包。这就是我之前提到的“沙盒”行为。
尽管访问不同的虚拟 Python 情况和可执行文件是一个强大的特性,但是对于多个虚拟 Python 情况和全局 Python 安装本身的可执行文件利用不同的pip和python可执行文件,会由于长的访问路径和相对路径而变得混乱。
出于这个原因,virtualenv 有一个加载虚拟情况的机制,如许,假如您从系统上的任何地方执行pip、python,或任何其他可执行文件,就会利用来自所选虚拟情况的可执行文件(而不是默认的全局 Python 安装可执行文件)。这是通过bin目次中的activate可执行文件实现的,清单 1-6 展示了这个过程。
  1. [user@∼]$ source ./bin/activate
  2. [(mydjangosandbox)user@∼] $
  3. # NOTE: source is a Unix/Linux specific command, for other OS just execute activate
  4. Listing 1-6.
  5. Activate
  6. virtual Python environment
复制代码
请注意清单 1-6 中调用activate可执行文件后,命令提示符怎样在括号中添加虚拟情况名称。这意味着虚拟 Python 情况mydjangosandbox的bin目次下的可执行文件将优先于全局 Python 安装中的文件利用。要退出一个虚拟 Python 情况,只需键入deactivate就可以回到利用全局 Python 安装可执行文件。
正如您如今所了解的,virtualenv 透明地工作,答应您维护不同的 python 安装,每个安装都有自己的一组可执行文件,如主 Python 解释器和 pip 包管理器。您只需要在虚拟情况之间切换,以便在恰当的虚拟情况中安装和运行 Python 应用。
Note
在以后的章节中,我不会提到 virtualenv(例如mydjangosandbox),因为它与 Django 没有直接关系。虽然我建议您利用 virtualenv,但是假如您盼望继续利用全局 Python 安装python和pip可执行文件来处理惩罚任何事变,或者假如您盼望让虚拟 Python 情况拥有自己的可执行文件以使 Python 应用管理更容易,我会让您自己决定。因此,当你在书中看到 Python 可执行文件时,假设它们是全局的或者来自 virtualenv,无论你利用哪个。
安装 Django

一旦您的系统上运行了所有从前的工具,实际的 Django 安装就非常简朴了。清单 1-7 展示了怎样利用 pip 安装 Django。
  1. [user@∼]$ pip install Django==1.11
  2. Downloading/unpacking Django==1.11
  3. Collecting Django==1.11
  4.   Downloading Django-1.11-py2.py3-none-any.whl (6.9MB)
  5.     100% |███████████████████| 6.9MB 95kB/s
  6. Collecting pytz (from Django==1.11)
  7.   Downloading pytz-2017.2-py2.py3-none-any.whl (484kB)
  8.     100% |███████████████████| 491kB 735kB/s
  9. Installing collected packages: pytz, Django
  10. Successfully installed Django-1.11 pytz-2017.2
  11. Listing 1-7.Install Django with pip
复制代码
清单 1-7 中的pip install任务利用Django==1.11语法告诉 pip 下载并安装 Django 1.11 版本。利用同样的语法,您可以安装任何特定的 Django 版本。假如您不指定软件包版本,pip 会下载并安装指定软件包的最新可用版本。
有时 Django 版本可能需要几天才能通过 pip 获得,在这种情况下,您会收到一个错误。在这种情况下,您可以直接从 Django 主网站 https://www.djangoproject.com/download/ 下载该版本。一旦下载了 tar.gz 格式的发布文件,就可以利用 pip 进行安装,如清单 1-8 所示。
  1. [user@∼]$ pip install /home/Downloads/Django-1.11.tar.gz
  2. Processing /home/Downloads/Django-1.11.tar.gz
  3. Collecting pytz (from Django==1.11)
  4.   Using cached pytz-2017.2-py2.py3-none-any.whl
  5. Building wheels for collected packages: Django
  6.   Running setup.py bdist_wheel for Django ... done
  7.   Stored in directory: /home/ubuntu/.cache/pip/wheels/56/bf/24/f44162e115f4fe0cfeb4b0ae99b570fb55a741a8d090c9894d
  8. Successfully built Django
  9. Installing collected packages: pytz, Django
  10. Successfully installed Django-1.11 pytz-2017.2
  11. Listing 1-8.Install Django from local tar.gz file with pip
复制代码
请注意清单 1-8 中 pip 怎样可以或许直接从当地文件系统上的压缩文件安装 Python 包。
从 Git 安装 Django

假如您想利用 Django 中最新的功能,那么您需要从它的 Git 存储库中安装 Django。Git 存储库包含对 Django 的最新更改。尽管 Django Git 版本可能会不稳定,但这是利用最新的 Django 特性进行开发或获取尚未在公开发行版中提供的问题的 bug 修复的唯一方法。
Note
您需要安装 Git 来执行以下任务。你可以在 http://git-scm.com/ 下载几个操作系统的 Git
就像前面的 pip 安装示例一样,pip 非常灵活,可以从 Git 安装 Django。在 Git 中利用 pip 有两种选择。您可以提供远程 Django Git 存储库,在这种情况下,pip 在当地克隆存储库,并在安装后抛弃它,如清单 1-9 所示。或者您可以在当地克隆 Django Git 存储库——稍后您可以在这里进行修改——然后运行 pip 进行安装,如清单 1-10 所示。
  1. [user@∼]$ pip install git+https://github.com/django/django.git
  2. Collecting git+https://github.com/django/django.git
  3.   Cloning https://github.com/django/django.git to ./pip-31j_bcqa-build
  4. Requirement already satisfied: pytz in /python/mydjangosandbox/lib/python3.5/site-packages (from Django==2.0.dev20170408112615)
  5. Installing collected packages: Django
  6.       Successfully uninstalled Django-1.11
  7.   Running setup.py install for Django ... done
  8. Successfully installed Django-2.0.dev20170408112615
  9. Listing 1-9.Install Django from remote Git with pip
复制代码
  1. [user@∼]$ git clone https://github.com/django/django.git
  2. Cloning into django...
  3. remote: Counting objects: 388550, done.
  4. remote: Compressing objects: 100% (19/19), done.
  5. remote: Total 388550 (delta 5), reused 0 (delta 0), pack-reused 388531
  6. Receiving objects: 100% (388550/388550), 158.63 MiB | 968.00 KiB/s, done.
  7. Resolving deltas: 100% (281856/281856), done.
  8. Checking connectivity... done.
  9. # Assuming Django Git download made to /home/Downloads/django/
  10. [user@∼]$ pip install /home/Downloads/django/
  11. Processing /home/Downloads/django
  12. Collecting pytz (from Django==2.0.dev20170408112615)
  13.   Using cached pytz-2017.2-py2.py3-none-any.whl
  14. Installing collected packages: pytz, Django
  15.   Running setup.py install for Django ... done
  16. Successfully installed Django-2.0.dev20170408112615 pytz-2017.2
  17. Listing 1-10.Download Django from Git and install locally with pip
复制代码
注意在清单 1-9 中,下载远程 Git 存储库的语法是git+后跟远程 Git 位置。在本例中, https://github.com/django/django.git 表示 Django Git 存储库。在清单 1-10 中,Django Git 存储库首先被当地克隆,然后用当地 Git 存储库目次的参数执行pip。
启动 Django 项目

要启动 Django 项目,您必须利用 Django 附带的django-admin可执行文件或django-admin.py脚本。在你安装 Django 之后,这个可执行文件和脚本应该可以从你系统上的任何目次中访问(例如,安装在一个 virtualenv 的/usr/bin/、/usr/local/bin/或/bin/目次下)。请注意,可执行文件和脚本提供了相同的功能;因此,以后我将瓜代利用django-admin这一术语。
django-admin提供了各种子命令,您将在 Django 项目的一样平常工作中广泛利用这些命令。但是您将首先利用的是startproject子命令,因为它创建了 Django 项目的初始布局。startproject子命令接收一个参数来表示项目的名称,如下面的代码片段所示。
  1. #Create a project called coffeehouse
  2. django-admin startproject coffeehouse
  3. #Create a project called sportstats
  4. django-admin startproject sportstats
复制代码
Django 项目名称可以由数字、字母或下划线构成。项目名称不能以数字开头,只能以字母或下划线开头。别的,项目名称中不答应出现特别字符和空格,这主要是因为 Django 项目名称是目次和 Python 包的定名约定。
在执行django-admin startproject <project_name>时,会创建一个名为<project_name>的目次,此中包含默认的 Django 项目布局。清单 1-11 显示了 Django 项目的默认布局。
  1. +<BASE_DIR_project_name>
  2. |
  3. +----manage.py
  4. |
  5. +---+-<PROJECT_DIR_project_name>
  6.     |
  7.     +-__init__.py
  8.     +-settings.py
  9.     +-urls.py
  10.     +-wsgi.py
  11. Listing 1-11.Django project structure
复制代码
假如您查抄目次布局,您会注意到有两个目次带有<project_name>值。我将顶层 Django 项目目次称为BASE_DIR,它包括manage.py文件和基于项目名称的其他子目次。我将把第二级子目次——包括__init__.py、settings.py、urls.py和wsgi.py文件——称为PROJECT_DIR。接下来,我将描述清单 1-11 中每个文件的用途。


  • manage.py。-运行项目特定任务。正如django-admin用于执行系统范围的 Django 任务一样,manage.py用于执行项目特定的任务。
  • __init__.py。- Python 文件,答应从 Python 包所在的目次中导入 Python 包。注意__init__.py不是 Django 特有的,它是一个在几乎所有 Python 应用中利用的通用文件。
  • settings.py。-包含 Django 项目的设置设置。
  • urls.py。-包含 Django 项目的 URL 模式。
  • wsgi.py。-包含 Django 项目的 WSGI 设置属性。WSGI 是在生产情况中摆设 Django 应用的推荐方法(例如,面向公众)。开发 Django 应用不需要设置 WSGI。
Tip
给项目的BASE_DIR重新定名。在一个 Django 项目中有两个同名的嵌套目次会导致混淆,尤其是在处理惩罚 Python 包导入问题时。为了克制麻烦,我建议您将BASE_DIR重定名为不同于项目名称的名称(例如,重定名、大写或收缩名称,使其不同于PROJECT_DIR)。
Caution
不要重定名PROJECT_DIR。PROJECT_DIR名称被硬编码到一些项目文件中(例如settings.py和wsgi.py,以是不要更改它的名称。假如你需要重定名PROJECT_DIR,用一个新名字创建另一个项目更简朴。
如今您已经熟悉了默认的 Django 项目布局,让我们在浏览器中查看默认的 Django 项目。所有 Django 项目都有一个内置的 web 服务器,当项目文件发生变化时,它可以在浏览器中观察应用。放在 Django 项目的BASE_DIR中——这里是manage.py文件——运行命令python manage.py runserver,如清单 1-12 所示。
  1. [user@coffeehouse ∼]$ python manage.py runserver
  2. Performing system checks...
  3. System check identified no issues (0 silenced).
  4. You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
  5. Run 'python manage.py migrate' to apply them.
  6. May 23, 2017 - 22:41:20
  7. Django version 1.11, using settings 'coffeehouse.settings'
  8. Starting development server at http://127.0.0.1:8000/
  9. Quit the server with CONTROL-C.
  10. Listing 1-12.Start Django development web server
  11. provided by manage.py
复制代码
如清单 1-12 所示,命令python manage.py runserver在http://127.0.0.1:8000/上启动一个开发 web 服务器——这是您系统上的当地地点。临时不要担心'unapplied migration(s)'的消息,我将在接下来关于为 Django 项目创建数据库的章节中解决这个问题。接下来,假如你打开一个浏览器并指向地点http://127.0.0.1:8000/,你应该会看到 Django 项目的默认主页,如图 1-3 所示。

图 1-3。
Default home page for a Django project
有时更改 Django 开发 web 服务器的默认地点和端口很方便。这可能是因为默认端口正被另一个服务占用,或者需要将 web 服务器绑定到非当地地点,以便远程计算机上的某个人可以查看开发服务器。这很容易通过将端口或完整地点:端口字符串附加到python manage.py runserver命令来实现,如列表 1-13 中的各种示例所示。
  1. # Run the development server on the local address and port 4345 (http://127.0.0.1:4345/)
  2. python manage.py runserver 4345
  3. # Run the dev server on the 96.126.104.88 address and port 80 (http://96.126.104.88/)
  4. python manage.py runserver 96.126.104.88:80
  5. # Run the dev server on the 192.168.0.2 address and port 8888 (http://192.168.0.2:8888/)
  6. python manage.py runserver 192.168.0.2:8888
  7. Listing 1-13.Start Django development web server on different address and port
复制代码
为 Django 项目创建数据库

“开箱即用”状态下的 Django 被设置为与 SQLite 通信——一个包含在 Python 发行版中的轻量级关系数据库。以是默认情况下,Django 会自动为您的项目创建一个 SQLite 数据库。
除了 SQLite,Django 还支持其他流行的数据库,包括 PostgreSQL、MySQL 和 Oracle。毗连到数据库的 Django 设置是在 Django 项目的settting.py文件中的DATABASES变量中完成的。
假如您打开 Django 项目的settings.py文件,您会注意到DATABASES变量有一个默认的 Python 字典,其值如清单 1-14 所示。
  1. # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
  2. import os
  3. BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  4. DATABASES = {
  5.     'default': {
  6.         'ENGINE': 'django.db.backends.sqlite3',
  7.         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
  8.     }
  9. }
  10. Listing 1-14.Default Django DATABASES dictionary
复制代码
Tip
假如您想要最快的数据库设置,请利用 SQLite。
数据库设置本身可能很耗时。假如您想要最快的设置来为 Django 启用一个数据库,请保持之前的设置稳定。SQLite 不需要额外的凭据或 Python 包来创建 Django 数据库毗连。请注意,SQLite 数据库是一个平面文件,Django 基于NAME变量值创建 SQLite 数据库。在清单 1-14 的情况下,在 Django 项目的BASE_DIR下,作为一个名为db.sqlite3的平面文件。
Note
假如利用 SQLite,可以跳到本节的最后一步“测试 Django 数据库毗连并构建 Django 基表”
Django DATABASES变量定义了键值对。每个键代表一个数据库引用名,值是一个带有数据库毗连参数的 Python 字典。在清单 1-14 中,您可以观察到default数据库引用。default引用名用于表示在 Django 项目中声明的任何数据库干系操作都将针对该毗连执行。这意味着,除非另有说明,所有数据库 CRUD(创建-读取-更新-删除)操作都是针对用default键定义的数据库进行的。
在这种情况下,默认数据库的数据库毗连参数是键ENGINE和NAME,它们分别代表数据库引擎(即品牌)和数据库实例的名称。
Django 数据库毗连最紧张的参数是ENGINE值。与数据库干系联的 Django 应用逻辑是平台中立的,这意味着无论选择什么样的数据库,您总是以相同的方式编写数据库 CRUD 操作。然而,对不同数据库进行的 CRUD 操作之间存在微小的差别,这一点需要加以考虑。
Django 通过支持不同的后端或引擎来解决这个问题。因此,根据您计划用于 Django 应用的数据库品牌,ENGINE值必须是表 1-2 中所示的值之一。
表 1-2。
Django ENGINE value for different databases
| 数据库ˌ资料库 | Django `ENGINE`值 | | --- | --- | | 关系型数据库 | `django.db.backends.mysql` | | 神谕 | `django.db.backends.oracle` | | 一种数据库系统 | `django.db.backends.postgresql_psycopg2` | | 数据库 | `django.db.backends.sqlite3` | Django 数据库毗连参数NAME用于标识一个数据库实例,其值的约定可能因数据库品牌而异。例如,对于 SQLite 来说,NAME表示平面文件的位置,而对于 MySQL 来说,它表示实例的逻辑名称。
Django 数据库毗连参数的完整设置在表 1-3 中描述。
表 1-3。
Django database connection parameters based on database brand
| Django 毗连参数 | 缺省值 | 条记 | | --- | --- | --- | | `ATOMIC_REQUESTS` | `False` | 对每个查看请求强制执行(或不执行)一个事件。默认情况下设置为 False,因为为每个视图打开一个事件会有额外的开销。对性能的影响取决于应用的查询模式以及数据库处理惩罚锁定的本领。 | | `AUTOCOMMIT` | `True` | 默认情况下设置为 True,因为否则将需要显式事件来执行提交。 | | `CONN_MAX_AGE` | `0` | 数据库毗连的生存期(秒)。默以为 0,在每个请求结束时关闭数据库毗连。对于无穷定的持久毗连,请利用 None。 | | `ENGINE` | `''`(空字符串) | 要利用的数据库后端。数值选项见表 1-2 。 | | `HOST` | `''`(空字符串) | 定义数据库主机,此中空字符串表示当田主机。对于 MySQL:假如该值以正斜杠('/')开头,MySQL 将通过 Unix 套接字毗连到指定的套接字(例如," HOST": '/var/run/mysql ')。假如该值不以正斜杠开头,则该值被假定为主机。对于 PostgreSQL:默认情况下('),通过 UNIX 域套接字(pg_hba.conf 中的' local '行)毗连到数据库。假如 UNIX 域套接字不在标准位置,请利用 postgresql.conf 中 unix_socket_directory 的相同值。假如要通过 TCP 套接字进行毗连,请将 host 设置为“localhost”或“127 . 0 . 0 . 1”(pg _ HBA . conf 中的“HOST”行)。在 Windows 上,您应该始终定义主机,因为 UNIX 域套接字不可用。 | | `NAME` | `''`(空字符串) | 要利用的数据库的名称。对于 SQLite,它是数据库文件的完整路径。指定路径时,请始终利用正斜杠,纵然在 Windows 上也是如此(例如,C:/www/STORE/db.sqlite3)。 | | `OPTIONS` | `{}`(空字典) | 毗连到数据库时利用的额外参数。可用参数因数据库后端而异,请查阅后端模块自己的文档。有关后端模块的列表,请参见表 1-2 。 | | `PASSWORD` | `''`(空字符串) | 毗连到数据库时利用的密码。不消于 SQLite。 | | `PORT` | `''`(空字符串) | 毗连到数据库时利用的端口。空字符串表示默认端口。不消于 SQLite。 | | `USER` | `''`(空字符串) | 毗连到数据库时利用的用户名。不消于 SQLite。 | 安装 Python 数据库包

除了设置 Django 毗连到数据库,您还需要安装须要的 Python 包来与您的数据库品牌通信——唯一的例外是 SQLite,它包含在 Python 发行版中。
每个数据库依靠于不同的软件包,但是利用 pip 软件包管理器,安装过程非常简朴。假如您的系统上没有 pip 可执行文件,请参阅本章前面的“安装 Django”一节中的“安装 pip”小节。
表 1-4 中枚举了 Django 支持的每个数据库的 Python 包。别的,表 1-4 还包括安装每个包的 pip 命令。
表 1-4。
Python packages for different databases
| 数据库ˌ资料库 | Python 包 | pip 安装语法 | | --- | --- | --- | | 一种数据库系统 | `psycopg2` | `pip install psycopg2` | | 关系型数据库 | `mysql-python` | `pip install mysql-python` | | 神谕 | `cx_Oracle` | `pip install cx_Oracle` | | 数据库 | 包含在 Python 2.5+中 | 不适用的 | Database Development Libraries
假如您在尝试安装表 1-4 中的一个 Python 数据库包时收到错误,请确保您的系统上安装了数据库开发库。数据库开发库是构建毗连到数据库的软件所必须的。
数据库开发库与 Python 或 Django 无关,因此您需要咨询数据库供应商或操作系统文档(例如,在 Debian Linux 或 Ubuntu Linux 系统上,您可以利用以下 apt-get 任务安装 MySQL 开发库:apt-get install libmysql client-dev)。
测试 Django 数据库毗连并构建 Django 基表

一旦用数据库凭据更新了 Django settings.py文件,就可以测试它,看看 Django 应用是否可以与数据库通信。在整个 Django 项目中,有几项任务需要与数据库进行通信,但是如今测试数据库毗连最常见的任务之一是将项目的数据布局迁移到数据库中。
Django 数据库迁移过程确保与数据库干系的所有 Django 项目逻辑都反映在数据库本身中(例如,数据库具有 Django 项目所需的须要表格)。当您开始一个 Django 项目时,Django 需要进行一系列的迁移,这些迁移需要创建表来跟踪管理员和会话。这总是 Django 项目对数据库运行的第一个迁移过程。因此,为了测试 Django 数据库毗连,让我们在数据库上运行第一次迁移,以创建这组基表。
要针对数据库运行 Django 项目的迁移,在项目的BASE_DIR中利用带有migrate参数的manage.py脚本(例如,python manage.py migrate)。第一次执行这个命令时,输出应该雷同于清单 1-15 。
  1. [user@coffeehouse ∼]$ python manage.py migrate
  2. Operations to perform:
  3.   Apply all migrations: admin, auth, contenttypes, sessions
  4. Running migrations:
  5.   Applying contenttypes.0001_initial... OK
  6.   Applying auth.0001_initial... OK
  7.   Applying admin.0001_initial... OK
  8.   Applying admin.0002_logentry_remove_auto_add... OK
  9.   Applying contenttypes.0002_remove_content_type_name... OK
  10.   Applying auth.0002_alter_permission_name_max_length... OK
  11.   Applying auth.0003_alter_user_email_max_length... OK
  12.   Applying auth.0004_alter_user_username_opts... OK
  13.   Applying auth.0005_alter_user_last_login_null... OK
  14.   Applying auth.0006_require_contenttypes_0002... OK
  15.   Applying auth.0007_alter_validators_add_error_messages... OK
  16.   Applying auth.0008_alter_user_username_max_length... OK
  17.   Applying sessions.0001_initial... OK
  18. Listing 1-15.Run first Django migrate operation to create base database tables
复制代码
如清单 1-15 所示,假如数据库毗连成功,Django 会应用一系列迁移来创建数据库表,以管理项目的用户、组、权限和会话。如今,不要太担心这些 Django 迁移是怎样工作的或者它们位于那里——我将在后面提供细节——只需要知道 Django 需要这些迁移来提供一些基本的功能。
Tip
直接毗连到数据库。假如您在尝试毗连到数据库或迁移 Django 项目以创建初始数据库表集时收到错误,请尝试利用相同的 Django 参数直接毗连到数据库。
在许多情况下,Django 变量 NAME、USER、PASSWORD、HOST 或 PORT 中的输入错误会导致进程失败,或者根据乃至无法直接毗连到数据库。
设置内容:了解 URL、模板和应用

Django 项目中的内容利用三个主要的构件:URL、模板和应用。您分别创建和设置 Django 的 URL、模板和应用,尽管您将它们相互毗连以实现内容交付,这是 Django 疏松耦合架构设计原则的一部分。
URL 定义了访问内容的入口点或位置。模板定义了赋予最终内容情势的端点。应用充当 URL 和模板之间的中间件,改变或添加来自数据库或用户交互的内容。要运行静态内容,您只需要创建和设置 Django urls 和模板。要运行动态内容——从数据库或用户交互中构建——除了 URL 和模板之外,还需要创建和设置 Django 应用。
但在描述怎样创建和设置 URL、模板和应用之前,了解这些部分怎样相互共同非常紧张。图 1-4 显示了 Django 处理惩罚用户请求的工作流程,以及他们怎样处理惩罚 Django urls、模板和应用。

图 1-4。
Django workflow for urls, templates, and apps
正如您在图 1-4 中看到的,有两条独立的管道来传递静态或动态内容。更紧张的是,注意每个不同的 Django 层是怎样疏松耦合的(例如,假如不需要应用层,您可以放弃它,而 URL 层和模板层仍旧可以或许相互通信)。
创建和设置 Django Urls

Django urls 的主要入口点是启动项目时创建的urls.py文件——假如您不熟悉 Django 项目布局,请参见本章前面的清单 1-11 。假如你打开urls.py文件,你会注意到它只有一个到/admin/的活动 url,那就是 Django admin——我将在本章的下一节也是最后一节讨论 Django admin。
如今您已经熟悉了urls.py文件的语法,让我们激活一个 url 来查看 Django 项目主页上的定制内容。
Django urls 利用正则表达式来匹配传入的请求。匹配主页的正则表达式模式是^$——下一章包括一个关于在 Django urls 中利用正则表达式的专门章节。除了正则表达式模式之外,还需要在拦截到匹配模式的请求时做什么的动作(例如,发送来自特定模板的内容)。
打开urls.py文件,添加第 3 行——在django.contrib import admin下面的一行——和第 9 行——在url(r'^admin/', admin.site.urls),下面的一行——如清单 1-16 所示。
  1. from django.conf.urls import url
  2. from django.contrib import admin
  3. from django.views.generic import TemplateView
  4. ...
  5. ...
  6. urlpatterns = [
  7.     url(r'^admin/', admin.site.urls),
  8.     url(r'^$',TemplateView.as_view(template_name='homepage.html')),
  9. ]
  10. Listing 1-16.Django url for home page to template
复制代码
如清单 1-16 所示,urlpatterns是一个url()语句的 Python 列表。url方法来自django.conf.urls包。您刚刚添加的url方法定义了主页的模式——正则表达式^$——后跟动作TemplateView.as_view(template_name='homepage.html')。最后一个动作是一个帮助器方法,将请求方指向一个采用参数template_name='homepage.html'的模板。
总之,您在清单 1-16 中添加的url方法告诉 Django 对主页的请求应该返回模板homepage.html中的内容。url方法黑白常通用的,可以接受多种变化,我将在下一章扼要而详细地描述。
如今让我们测试一下主页。通过在 Django 项目的BASE_DIR上执行python manage.py runserver来启动开发 web 服务器。打开浏览器上的默认地点http://127.0.0.1:8000/。你看到了什么?带有Exception Type: TemplateDoesNotExist homepage.html的错误页面。这个错误是因为 Django 找不到为 url 定义的homepage.html模板。在下一节中,我将向您展示怎样设置和创建模板。
Caution
假如您收到错误OperationalError - no such table: django_session而不是错误TemplateDoesNotExist homepage.html,这意味着 Django 项目的数据库仍旧没有正确设置。您需要在项目的BASE_DIR中运行python manage.py migrate,如许 Django 就会创建须要的表来跟踪会话。有关更多详细信息,请参见上一节关于设置数据库的内容。
创建和设置 Django 模板

默认情况下,Django 模板被解释为 HTML。这意味着 Django 模板应该有一个标准的 HTML 文档布局和 HTML 标签(例如,<html>、<body>)。您可以利用常规的文本编辑器来创建 Django 模板,并利用.html扩展名生存文件。
让我们为已往部分的 url 创建一个模板。在文本编辑器中,创建一个名为homepage.html的文件,并将清单 1-17 的内容放入此中。将文件生存在您的系统上,在 Django 项目的PROJECT_DIR.的子目次templates中
  1. <html>
  2. <body>
  3.   <h4>Home page for Django</h4>
  4. </body>
  5. </html>
  6. Listing 1-17.Template homepage.html
复制代码
一旦有了包含 Django 模板的目次,就需要设置一个 Django 项目,如许它就可以在这个目次中找到模板。在 Django 项目的settings.py文件中,需要在TEMPLATES变量的DIRS属性中定义模板目次。DIRS属性是一个列表,因此您可以定义几个目次来定位模板,尽管我建议您只利用一个带有各种子目次的目次来进行分类。
正如我之前推荐的,你应该把 Django 模板放在子目次中——在 Django 项目的PROJECT_DIR中利用一个像templates如许明显的名字。例如,假如 Django 项目PROJECT_DIR的绝对路径是/www/STORE/coffeehouse/,那么DIRS值的推荐位置就是/www/STORE/coffeehouse/templates/。清单 1-18 展示了在settings.py中利用在settings.py顶部动态设置的PROJECT_DIR引用变量的示例DIRS定义。
  1. BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  2. PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
  3. TEMPLATES = [
  4.     {
  5.         'BACKEND': 'django.template.backends.django.DjangoTemplates',
  6.         'DIRS': ['%s/templates/' % (PROJECT_DIR),],
  7.         'APP_DIRS': True,
  8.         'OPTIONS': {
  9.             'context_processors': [
  10.                 'django.template.context_processors.debug',
  11.                 'django.template.context_processors.request',
  12.                 'django.contrib.auth.context_processors.auth',
  13.                 'django.contrib.messages.context_processors.messages',
  14.             ],
  15.         },
  16.     },
  17. ]
  18. Listing 1-18.TEMPLATES and DIRS definition in settings.py
复制代码
清单 1-18 的一个紧张特点是它没有利用硬编码的目次路径;相反,它利用动态确定的PROJECT_DIR变量。这在如今看起来可能是微不足道的,但是一旦 Django 项目的位置有改变的趋势(例如,组开发,摆设到生产),这就是一个好的实践。
最后,再次启动 Django 开发 web 服务器,并在默认地点http://127.0.0.1:8000/上打开一个浏览器。您如今应该在主页上看到模板homepage.html的内容,而不是您在上一节中看到的错误页面。
创建和设置 Django 应用

Django 应用用于对应用功能进行分组。假如你想处理惩罚来自数据库或用户交互的内容,你必须创建和设置 Django 应用。一个项目可以包含任意数量的应用。例如,假如您有一个咖啡馆项目,您可以创建一个市肆应用、另一个菜单项应用、另一个关于信息的应用,并根据需要创建其他应用。一个项目中的应用数量没有硬性规定。无论是简化代码管理还是将应用工作委托给团队,Django apps 的目的都是将应用功能分组以使工作更容易。
Django 应用通常包含在项目的子目次中。这种方法使得利用 Python 引用和定名约定更加容易。假如项目名为 coffeehouse,名为 stores 的应用的功能很容易通过 Python 包引用为coffeehouse.stores。
因为应用提供了一种组合应用功能的模块化方法,以是其他人或团体分发具有流行功能的 Django 应用是很常见的。例如,假如一个 Django 项目需要论坛功能,而不是从头开始编写一个论坛应用,您可以利用几个 Django 论坛应用中的一个。你寻找的功能越通用,你就越有可能找到第三方开发的 Django 应用。
You Already Worked With Django Apps!
您可能没有意识到这一点,但是在上一节中,当您为 Django 项目设置数据库时,您已经在调用 migrate 操作时利用了 Django apps。
默认情况下,所有 Django 项目都启用了框架提供的六个应用。这些应用分别是django.contrib.admin、django.contrib.auth、django.contrib.contenttypes、django.contrib.sessions、django.contrib.messages和django.contrib.staticfiles。当您触发迁移操作时,Django 为这些预安装的应用创建了数据库模型。
接下来,让我们创建一个小的 Django 应用。转到PROJECT_DIR——urls.py和settings.py文件所在的位置——执行命令django-admin startapp about创建一个名为 about 的应用。一个名为about的子目次被创建,此中包含该应用。默认情况下,创建应用时,其子目次包括以下内容:


  • __init__.py。- Python 文件,答应从其他目次导入应用包。注意__init__.py不是一个 Django 特定的文件,它是一个在几乎所有 Python 应用中利用的通用文件。
  • migrations。-包含应用于应用数据库定义(即模型类)的迁移的目次。
  • admin.py。-包含应用管理定义的文件-从 Django admin 访问模型类实例需要这些定义。
  • apps.py。-包含应用设置参数的文件。
  • models.py。-包含应用数据库定义(即模型类)的文件。
  • tests.py。-应用的测试定义文件。
  • views.py。-包含应用视图定义(即控制器方法)的文件。
接下来,打开views.py文件并添加清单 1-19 中的内容。
  1. from django.shortcuts import render
  2. def contact(request):
  3.     # Content from request or database extracted here
  4.     # and passed to the template for display
  5.     return render(request,'about/contact.html')
  6. Listing 1-19.
  7. Handler view method
  8. in views.py
复制代码
清单 1-19 中的contact方法——像views.py文件中的所有其他方法一样——是一个访问用户 web 请求的控制器方法。注意,联系方法的输入名为request。在这种类型的方法中,您可以利用request引用访问来自 web 请求的内容(例如,IP 地点、会话),或者访问来自数据库的信息,以便最终将这些信息传递给模板。假如您查看 contact 方法的最后一行,它以 Django helper 方法render的返回语句结束。在这种情况下,render 方法将控制权返回给about/contact.html模板。
因为清单 1-19 中的contact方法将控制权返回给模板about/contact.html,以是您还需要在您的templates目次中创建一个名为about的子目次,此中包含一个名为contact.html的模板(即在TEMPLATES变量的DIRS属性中定义的模板)。
contact方法本身什么也不做,它需要被 url 调用。清单 1-20 展示了怎样向链接到清单 1-19 中contact方法的urls.py文件添加一个 url。
  1. from django.conf.urls import url
  2. from django.contrib import admin
  3. from django.views.generic import TemplateView
  4. from coffeehouse.about import views as about_views
  5. urlpatterns = [
  6.     url(r'^admin/', admin.site.urls),
  7.     url(r'^$',TemplateView.as_view(template_name='homepage.html')),
  8.     url(r'^about/', about_views.contact),
  9. ]
  10. Listing 1-20.Django url for view method
复制代码
清单 1-20 中声明的第一件事是一个 import 语句,用于访问清单 1-19 中的contact方法。在这种情况下,因为应用被定名为about,而且它位于coffeehouse项目文件夹下,以是它显示为from coffeehouse.about,后跟import views,这使我们可以访问应用的views.py文件,此中有contact方法。
import 语句以as about_views结束,以分配一个唯一的限定符,假如您计划利用多个应用,这很紧张。例如,没有as关键字的导入语句,如from coffeehouse.about import views、from coffeehouse.items import views或from coffeehouse.stores import views可以导入冲突的视图方法引用(例如,三个名为 index 的方法),因此as限定符是一种保护步伐,以确保您不会无意中利用另一个应用中同名的方法。
清单 1-20 中的新 url 定义利用正则表达式来匹配about url 目次(例如http://127.0.0.1:8000/about/)上的请求,而不是将请求定向到模板,而是将控制权交给about_views.contact方法——此中about_views指的是上一段中描述的导入引用。
接下来,启动 Django 开发 web 服务器,并在地点http://127.0.0.1:8000/about/上打开一个浏览器。注意about url 目次上的请求怎样显示在views.py的contact方法中定义的底层about/contact.html模板。
最后,虽然您如今可以访问应用的views.py方法,但是您还需要在项目的settings.py文件中设置应用。这最后一步很紧张,如许 Django 就可以找到您稍后创建的其他应用构造(例如,数据库模型定义、静态资源、定制模板标签)。
打开 Django 项目的settings.py文件并寻找INSTALLED_APPS变量。你会看到一系列已经在INSTALLED_APPS上定义的应用。注意安装的应用是怎样属于django.contrib包的,这意味着它们是由 Django 框架本身提供的。将coffeehouse.about应用添加到列表中,如清单 1-21 的第 8 行所示。
  1. INSTALLED_APPS = [
  2.     'django.contrib.admin',
  3.     'django.contrib.auth',
  4.     'django.contrib.contenttypes',
  5.     'django.contrib.sessions',
  6.     'django.contrib.messages',
  7.     'django.contrib.staticfiles',
  8.     'coffeehouse.about',
  9. ]
  10. Listing 1-21.Add app to INSTALLED_APPS in Django settings.py
复制代码
如清单 1-21 的第 8 行所示,要将应用添加到项目中,您需要将应用包作为字符串添加到INSTALLED_APPS变量中。虽然coffeehouse.about应用实际上仍旧是空的,但是将应用添加到INSTALLED_APPS变量中是将来操作的紧张设置步骤,例如数据库操作和与应用干系的静态资源,等等。
设置 Django 管理站点

Django 管理站点提供了一个基于 web 的界面来访问毗连到 Django 项目的数据库。纵然对于有经验的技能管理员来说,直接在数据库上进行数据库 CRUD(创建-读取-更新-删除)操作也是困难和耗时的,因为需要发出原始 SQL 命令和导航数据库布局。对于非技能用户来说,直接在数据库上进行数据库 CRUD 操作可能会令人望而生畏,假如不是不可能的话。Django 管理站点解决了这个问题。
Django 管理站点可以公开链接到数据库的所有 Django 项目干系的数据布局,因此专家和新手都可以轻松地执行数据库 CRUD 操作。随着 Django 项目的增长,Django 管理站点可以成为管理与 Django 项目干系的数据库中不断增长的大量信息的紧张工具。
Django 管理站点是作为 Django 应用构建的;这意味着设置 Django 管理站点唯一需要做的事变就是像其他 Django 应用一样设置和安装应用。假如您不熟悉 Django app 这个术语,请阅读上一节“设置内容:理解 URL、模板和应用”
Django 管理站点要求您预先设置一个数据库并安装 Django 基表。因此,假如您还没有如许做,请参阅上一节“为 Django 项目设置数据库”
设置并安装 Django 管理站点应用

默认情况下,所有 Django 项目都启用了 Django 管理。假如你打开一个 Django 项目的urls.py文件,在urlpatterns变量中你会看到行url(r'^admin/', admin.site.urls)。最后一个正则表达式模式告诉 Django 在/admin url 目次(例如http://127.0.0.1:8000/admin/)上启用管理站点应用。
接下来,假如你打开项目的settings.py文件,转到INSTALLED_APPS变量,在这个变量的顶部附近,你会看到一行django.contrib.admin,表示 Django 管理站点应用已启用。
通过在 Django 的BASE_DIR上执行python manage.py runserver来启动开发 web 服务器。在 Django 管理网站http://127.0.0.1:8000/admin/上打开浏览器。你会看到如图 1-5 所示的登录屏幕。

图 1-5。
Django admin site login
接下来,让我们创建一个 Django 超等用户或管理员,通过图 1-5 中的界面访问 Django admin。要创建 Django 超等用户,你可以利用清单 1-22 中的manage.py命令。
  1. [user@coffeehouse ∼]$ python manage.py createsuperuser
  2. Username (leave blank to use 'admin'):
  3. Email address: admin@coffeehouse.com
  4. Password:
  5. Password (again):
  6. The password is too similar to the email address.
  7. This password is too short. It must contain at least 8 characters.
  8. This password is too common.
  9. Password:
  10. Password (again):
  11. Superuser created successfully.
  12. Listing 1-22.Create Django superuser for admin interface
复制代码
Caution
假如您收到错误OperationalError - no such table: auth_user,这意味着 Django 项目的数据库仍旧没有正确设置。您需要在项目的 BASE_DIR 中运行python manage.py migrate,如许 Django 就会创建须要的表来跟踪用户。更多细节请参见上一节“为 Django 项目设置数据库”。
Tip
默认情况下,Django 强制用户密码符合最低安全级别。例如,在清单 1-22 中,您可以看到在尝试利用密码 coffee 之后,Django 拒绝了这个任务,并给出了一系列错误消息,强制进行新的尝试。您可以在setttings.py的AUTH_PASSWORD_VALIDATORS变量中修改这些密码验证规则。
最后这个过程创建了一个超等用户,其信息存储在毗连到 Django 项目的数据库中,具体地说是存储在auth_user表中。如今您可能会问自己,怎样更新这个用户的名称、密码或电子邮件?虽然您可以直接进入数据库表并执行更新,但这是一条曲折的路线;更好的方法是依靠 Django admin,它可以让您非常友好地查看 Django 项目中的数据库表。
接下来,将刚刚创建的超等用户用户名和密码引入图 1-5 的界面。一旦你在管理站点上提供了超等用户的用户名和密码,你将访问如图 1-6 所示的管理站点的主页。

图 1-6。
Django admin site home page
在 Django 管理网站的主页上,如图 1-6 所示,点击“用户”链接。您将看到有权访问 Django 项目的用户列表。如今,您只能看到您在上一步中创建的超等用户。您可以更改该用户的凭据(如密码、电子邮件、用户名)或直接从 Django 管理站点屏幕添加新用户。
这种修改或添加存储在与 Django 项目干系的数据库中的记载的灵活性使得 Django 管理站点如此强大。例如,假如您开发了一个咖啡馆项目,并添加了市肆、饮料或客户等应用,Django admin 授权用户可以对这些对象进行 CRUD 操作(例如,创建市肆、更新饮料、删除客户)。从内容管理的角度来看,这黑白常强大的,特别是对于非技能用户。最紧张的是,在项目的应用上启用 Django 管理站点几乎不需要额外的开发工作。
这里先容的 Django 管理站点任务只是功能上的“冰山一角”;下一章将更详细地先容 Django 管理站点的功能。
设置并安装 Django 管理站点文档应用

Django 管理站点也有自己的文档应用。Django 管理站点文档应用不光提供关于管理站点本身操作的信息,还包括关于 Django 模板的 Django 过滤器的其他通用文档。更紧张的是,Django admin site documentation 应用会对所有已安装的项目应用的源代码进行自省,以出现关于控制器方法和模型对象的文档(即嵌入在应用models.py和views.py文件的源代码中的文档)。
要安装 Django 管理站点文档应用,您首先需要安装 docutils Python 包,利用 pip 包管理器执行以下命令:pip install docutils。一旦安装了 docutils 包,就可以像安装其他 Django 应用一样安装 Django 管理站点文档应用。
添加 url 以访问 Django 管理站点文档应用。假如打开项目的urls.py文件,在urlpatterns变量中添加下面一行:
  1. url(r'^admin/doc/', include('django.contrib.admindocs.urls'))
复制代码
请确保在url(r'^admin/'…行之前添加此内容,以便在底部保留更多通用匹配表达式,在顶部保留同一 url 路径上的更多粒度表达式(例如/admin)。最后一个正则表达式模式告诉 Django 启用/admin/doc/ url 目次下的管理站点文档应用(例如http://127.0.0.1:8000/admin/doc/)。
接下来,打开项目的settings.py文件,转到INSTALLED_APPS变量。在这个变量的最终值附近添加一行django.contrib.admindocs来启用 Django 管理站点文档应用。
随着开发 web 服务器的运行,在地点http://127.0.0.1:8000/admin/doc/上打开一个浏览器,您应该会看到如图 1-7 所示的页面。

图 1-7。
Django admin site doc home page
假如您注销了 Django 管理站点,您将需要再次登录来访问文档,因为它也需要用户认证。登录后,您将可以或许看到 Django 管理站点的文档主页——如图 1-7 所示——以及关于项目的控制器方法和模型对象的文档。
Footnotes 1
https://djangopackages.org/
2
https://engineering.instagram.com/what-powers-instagram-hundreds-of-instances-dozens-of-technologies-adf2e22da2ad#.pui97g5jk
3
https://www.quora.com/Pinterest/What-is-the-technology-stack-behind-Pinterest-1
4
http://open.pbs.org/
5
https://github.com/natgeo
6
https://www.python.org/dev/peps/pep-0020/
7
https://www.djangoproject.com/download/
8
https://github.com/django/django/
9
https://pythonhosted.org/six/
10
https://docs.djangoproject.com/en/1.11/topics/python3/
二、Django Url 和视图

在第一章中,你学习了 Django 的焦点构建模块,包括什么是视图、模型和 URL。在这一章中,您将了解更多关于 Django urls 的内容,它是 Django 应用工作流的入口点。您将学习怎样创建复杂的 url 正则表达式,怎样在视图方法和模板中利用 url 值,怎样构造和管理 URL,以及怎样定名 URL。
在 URL 之后,Django 视图代表了几乎所有 Django 工作流的下一步,视图负责查抄请求、执行业务逻辑、查询数据库和验证数据,以及生成响应。在本章中,您将学习怎样创建带有可选参数的 Django 视图,视图请求和响应的布局,怎样利用视图中间件,以及怎样创建基于类的视图。
Url 正则表达式

正则表达式在所有编程语言中都提供了一种强大的方法来确定模式。然而,强大的同时也带来了复杂性,乃至有整本书都在讨论正则表达式。 1
尽管大多数 Django URL 的复杂性不会超过许多正则表达式书中所描述的一小部分,但是理解 Django URL 中正则表达式的一些底层行为和最常见的模式是很紧张的。
优先规则:粒度 URL 优先,广义 URL 最后

Django urls 需要遵循肯定的次序和语法才能正常工作。广义 url 正则表达式应该最后声明,而且只能在更细粒度的 url 正则表达式之后声明。
这是因为 Django url 正则表达式匹配不利用短路行为,就像嵌套条件语句(例如,if/elif/elif/elif/else)一样,只要满足一个条件,其余选项就会被忽略。在 Django urls 中,假如一个传入的 url 请求有不止一个匹配的正则表达式,那么将会触发最顶层的一个操作。匹配 url 正则表达式的优先级从顶部(即第一个声明的)到底部(即最后一个声明的)给出。
你不应该低估引入两个匹配相同模式的 url 正则表达式有多容易,特别是假如你从来没有利用过正则表达式,因为语法可能很神秘。清单 2-1 展示了声明 Django urls 的正确方式,更细粒度的正则表达式在顶部,更宽泛的正则表达式在底部。
  1. from django.views.generic import TemplateVieww
  2. urlpatterns = [
  3.     url(r'^about/index/',TemplateView.as_view(template_name='index.html')),
  4.     url(r'^about/',TemplateView.as_view(template_name='about.html')),
  5. ]
  6. Listing 2-1.Correct precedence for Django url regular expressions
复制代码
基于清单 2-1 ,让我们看看假如 Django 收到对 url /about/index/的请求会发生什么。最初,Django 匹配最后一个正则表达式,即“匹配^about/”。接下来,Django 继续向上查抄正则表达式,并到达与请求 url /about/index/完全匹配的‘match^about/index/',因此触发这个动作将控制发送到index.html模板。
如今让我们浏览一个对 url /about/的请求。最初,Django 匹配最后一个正则表达式“匹配^about/”。接下来,Django 继续向上查抄正则表达式,寻找潜伏的匹配。因为没有找到匹配——因为‘match^about/index/'是一个更细粒度的正则表达式——Django 触发第一个动作,将控制发送给about.html模板,这是唯一的正则表达式匹配。
正如您所看到的,清单 2-1 产生了可以说是预期的行为。但是如今让我们颠倒 url 正则表达式的次序,如清单 2-2 所示,并分解为什么向底部声明更细粒度的正则表达式是声明 Django url 正则表达式的错误方式。
  1. from django.views.generic import TemplateVieww
  2. urlpatterns = [
  3.     url(r'^about/',TemplateView.as_view(template_name='about.html')),
  4.     url(r'^about/index/',TemplateView.as_view(template_name='index.html')),
  5. ]
  6. Listing 2-2.Wrong precedence for Django url regular expressions
复制代码
当请求 url /about/index/时,清单 2-2 中的问题出现了。最初,Django 匹配最后一个正则表达式,即“匹配^about/index/”。然而,Django 继续查抄正则表达式,并到达‘match^about/',这是对请求 url /about/index/的更广泛的匹配,但仍旧是匹配!因此 Django 触发了这个动作,并将控制权发送给了about.html模板,而不是第一次匹配时预期的index.html模板。
精确 Url 模式:放弃广泛匹配

在上一节中,我有意利用了答应广泛 url 匹配的正则表达式。根据我的经验,随着 Django 项目的增长,你最终会面对利用这种类型的 url 正则表达式的需求——但是稍后会详细解释为什么会如许。
究竟证明,利用精确的 url 正则表达式是可能的。精确的 url 正则表达式消除了由于 Django url 正则表达式的声明次序而引入的任何歧义。
让我们修改清单 2-2 中的 url 正则表达式,使它们成为精确的正则表达式,如许它们的次序就无关紧要了。清单 2-3 在清单 2-2 的基础上展示了精确的正则表达式。
  1. from django.views.generic import TemplateVieww
  2. urlpatterns = [
  3.     url(r'^about/$',TemplateView.as_view(template_name='about.html')),
  4.     url(r'^about/index/$',TemplateView.as_view(template_name='index.html')),
  5. ]
  6. Listing 2-3.Exact regular expressions, where url order doesn’t matter
复制代码
注意清单 2-3 中的正则表达式以$字符结束。这是表示行尾的正则表达式符号,这意味着正则表达式 URL 只匹配一个精确的模式。
例如,假如 Django 接收到对 url /about/index/的请求,它将只匹配清单 2-3 中的最后一个正则表达式,即“匹配^about/index/$”。然而,它不会匹配更高级的^/about/$正则表达式,因为这个正则表达式说与about/完全匹配,因为$表示模式的结束。
然而,尽管$字符对于创建更严格的 url 正则表达式很有用,但是分析它的行为也很紧张。假如您计划利用 url 搜索引擎优化(SEO)、A/B 测试技能,或者只是盼望答应多个 URL 运行相同的操作,那么利用更严格的正则表达式$最终需要做更多的工作。
例如,假如您开始利用像/about/index/、/about/email/、/about/address/如许的 URL,而且它们都利用相同的模板或视图进行处理惩罚,精确正则表达式只会使您声明的 URL 数量更大。雷同地,假如您利用 A/B 测试或 SEO,此中相同 url 的较长变体以相同的方式处理惩罚(例如,/about/landing/a/、/about/landing/b/、/about/the+coffeehouse+in+san+diego/),宽泛的 url 匹配比声明精确的 url 模式要简朴得多。
最后,无论您是否选择利用以$结尾的精确 url 正则表达式,我仍旧建议您保持将更细粒度的 url 正则表达式放在顶部而将更广泛的 url 正则表达式放在底部的做法,因为这可以克制当不止一个正则表达式匹配一个 URL 请求时出现清单 2-2 中描述的不测行为。
常见 Url 模式

尽管 url 正则表达式可以有无穷多种变化——几乎不可能描述每种可能性——但我将提供一些您更可能利用的最常见 url 模式的示例。表 2-1 显示了 Django urls 的单个正则表达式字符,表 2-2 显示了 url 模式的一系列更具体的例子。
表 2-2。
Common Django url patterns and their regular expressions, with samples
| Url 正则表达式 | 描述 | 示例 URL | | --- | --- | --- | | URL(r ' ^ $ ') | 空字符串(主页) | 匹配项:http://127.0.0.1/ | | url(r'^stores/',.....) | 有尾随字符吗 | 火柴: [`http://127.0.0.1/stores/`](http://127.0.0.1/stores/) [`http://127.0.0.1/stores/long+string+with+anything+12345`](http://127.0.0.1/stores/long+string+with+anything+12345) | | url(r'^about/contact/$',.....) | 精确,无尾随字符 | 匹配: [`http://127.0.0.1/about/contact/`](http://127.0.0.1/about/contact/) 不匹配: [`http://127.0.0.1/about/`](http://127.0.0.1/about/) | | url(r'^stores/\d+/',..…) | 数字 | 匹配: [`http://127.0.0.1/stores/2/`](http://127.0.0.1/stores/2/) [`http://127.0.0.1/stores/34/`](http://127.0.0.1/stores/34/) 不匹配: [`http://127.0.0.1/stores/downtown/`](http://127.0.0.1/stores/downtown/) | | URL(r ' ^ drinks/\ d+/', | 非数字 | 匹配: [`http://127.0.0.1/drinks/mocha/`](http://127.0.0.1/drinks/mocha/) 不匹配: [`http://127.0.0.1/drinks/324/`](http://127.0.0.1/drinks/324/) | | url(r'^drinks/mocha|espresso/',.....) | 单词选项,任何尾随字符 | 匹配:[`http://127.0.0.1/drinks/mocha/`](http://127.0.0.1/drinks/mocha/)[`http://127.0.0.1/drinks/mochaccino/`](http://127.0.0.1/drinks/mochaccino/)[`http://127.0.0.1/drinks/espresso/`](http://127.0.0.1/drinks/espresso/)不匹配: [`http://127.0.0.1/drinks/soda/`](http://127.0.0.1/drinks/soda/) | | url(r'^drinks/mocha$|espresso/$',.....) | 单词选项精确,无尾随字符 | 匹配: [`http://127.0.0.1/drinks/mocha/`](http://127.0.0.1/drinks/mocha/) 不匹配: [`http://127.0.0.1/drinks/mochaccino/`](http://127.0.0.1/drinks/mochaccino/) 匹配: [`http://127.0.0.1/drinks/espresso/`](http://127.0.0.1/drinks/espresso/) 不匹配: [`http://127.0.0.1/drinks/espressomacchiato/`](http://127.0.0.1/drinks/espressomacchiato/) | | url(r'^stores/\w+/',.....) | 单词字符(任何小写或大写字母、数字或下划线) | 匹配:[`http://127.0.0.1/stores/sandiego/`](http://127.0.0.1/stores/sandiego/)[`http://127.0.0.1/stores/LA/`](http://127.0.0.1/stores/LA/)[`http://127.0.0.1/stores/1/`](http://127.0.0.1/stores/1/)不匹配: [`http://127.0.0.1/san-diego/`](http://127.0.0.1/san-diego/) | | url(r'^stores/[-\w]+/',.....) | 单词字符或破折号 | 火柴: [`http://127.0.0.1/san-diego/`](http://127.0.0.1/san-diego/) | | url(r'^state/[A-Z]{2}/',.....) | 两个大写字母 | 匹配: [`http://127.0.0.1/CA/`](http://127.0.0.1/CA/) 不匹配: [`http://127.0.0.1/Ca/`](http://127.0.0.1/Ca/) | 表 2-1。
Regular expression syntax for Django urls: Symbol (Meaning)
| url 的开头) | url 的结尾) | \(对解释的值进行转义) | |(或) | | + (1 次或多次出现) | ?(出现 0 次或 1 次) | {n}(出现 n 次) | {n,m}(出现次数介于 n 和 m 之间) | | [](字符分组) | (?P ___)(捕获与 regexp ___ 匹配的匹配项,并将其分配给 name | 。(任何字符) | \d+(一个或多个数字)。注意 escape,没有 escape 按字面意思匹配' d+'。 | | \D+(一个或多个非数字)。注意转义,没有转义匹配' D+' | [a-zA-Z0-9_]+(一个或多个单词字符、小写或大写字母、数字或下划线) | \w+(一个或多个单词字符,相当于[a-zA-Z0-9_])。注意 escape,没有 escape 按字面意思匹配‘w+’。 | [-@\w]+(一个或多个单词字符,破折号。或者在符号处)。请注意,\w 没有转义,因为它被括在括号中(即分组)。 | Django Urls Don’T Inspect Url Query Strings
在某些 URL 上——那些由 HTTP GET 请求生成的 URL,常见于 HTML 表单或 REST 服务中——参数被添加到 URL 中,此中?后跟由&分隔的parameter_name=parameter_value(例如,/drinks/mocha/?type=cold&size=large)。这些值集被称为查询字符串,Django 出于 url 模式匹配的目的忽略了它们。
假如您需要利用这些值作为 url 参数——这是下一节探讨的主题——您可以通过请求引用在 Django 视图方法中访问这些值。另一种方法是改变 url 布局以适应正则表达式(例如,用/drinks/mocha/cold/large/取代/drinks/mocha/?type=cold&size=large)。
Url 参数、额外选项和查询字符串

您刚刚学习了怎样利用各种各样的正则表达式为您的 Django 应用创建 URL。然而,假如你转头看看清单 2-1 、 2-2 和 2-3 ,你会注意到 URL 上提供的信息被抛弃了。
有时将 url 信息作为参数传递给处理惩罚布局是有帮助的,乃至是须要的。例如,假如你有几个雷同于/drinks/mocha/、/drinks/espresso/和/drinks/latte/的 url,URL 的最后一部分代表一个饮料名称。因此,将此 url 信息传递给处理惩罚模板以在视图中显示它或以其他方式利用它(例如,查询数据库)可能是有帮助的或须要的。为了传递这些信息,url 需要将这些信息作为一个参数。
为了处理惩罚 url 参数,Django 对定名组利用 Python 的标准正则表达式语法。 2 清单 2-4 显示了一个创建名为drink_name的参数的 url。
  1. urlpatterns = [
  2.     url(r'^drinks/(?P<drink_name>\D+)/',TemplateView.as_view(template_name='drinks/index.html')),
  3. ]
  4. Listing 2-4.Django url parameter definition for access in templates
复制代码
注意清单 2-4 中的(?P<drink_name>\D+)语法。?P<>语法告诉 Django 将正则表达式的这一部分视为定名组,并将值赋给在<>之间声明的名为drink_name的参数。最后一块\D+是确定匹配值的正则表达式;在这种情况下,匹配值是一个或多个非数字字符,如表 2-1 所述。
非常紧张的一点是,您必须理解,只有当提供的值与指定的正则表达式匹配时,参数才会被捕获(例如,\D+表示非数字)。例如,对于 url 请求/drinks/mocha/,值mocha被分配给drink_name参数,但是对于像/drinks/123/如许的 url,正则表达式模式不匹配——因为123是数字——以是不采取任何行动。
假如清单 2-4 中出现 url 匹配,请求将被直接发送到模板drinks/index.html。Django 通过同名的 Django 模板上下文变量提供对以这种方式定义的所有参数的访问。因此,要访问参数,您可以直接在模板中利用参数名drink_type。例如,要输出参数drink_name的值,您可以利用标准的{{}} Django 模板语法(例如,{{drink_name}})。
除了将 url 的一部分视为参数,还可以在 url 定义中定义额外的选项,以便在 Django 模板中将它们作为上下文变量来访问。这些额外的选项在一个字典中定义,该字典被声明为 url 定义的最后一部分。
例如,请看清单 2-4 中修改后的 url Django 定义:
  1. url(r'^drinks/(?P<drink_name>\D+)', TemplateView.as_view(template_name='drinks/index.html'), {'onsale':True}),
复制代码
注意一个带有键值的字典是怎样被添加到 url 定义的末端的。以这种方式,onsale键成为一个 url 额外选项,它作为一个上下文变量被传递给底层模板。Url 额外选项可以像 url 参数一样作为模板上下文变量来访问。因此,为了输出额外的选项onsale,你可以利用{{onsale}}语法。
接下来,让我们看看清单 2-5 中展示的 url 参数的另一种变体,它将控制发送给 Django 视图方法。
  1. # Project main urls.py
  2. from coffeehouse.stores import views as stores_views
  3. urlpatterns = patterns[
  4.     url(r'^stores/(?P<store_id>\d+)/',stores_views.detail),
  5. ]
  6. Listing 2-5.Django url parameter definition for access in view methods in main urls.py file
复制代码
注意清单 2-5 中的(?P<store_id>\d+)语法与清单 2-4 中的非常相似。发生变化的是参数如今被定名为store_id,正则表达式是\d+来匹配数字。因此,举例来说,假如向 url /stores/1/发出请求,则值 1 被赋给store_id参数,假如向雷同/stores/downtown/的 url 发出请求,则正则表达式模式不匹配——因为downtown是字母而不是数字——以是不采取任何行动。
假如清单 2-5 出现 url 匹配,请求将被直接发送到 Django 视图方法coffeehouse.stores.views.detail。此中coffeehouse.stores是包名,views.py是 stores 应用中的文件,detail是视图方法的名称。清单 2-6 展示了访问store_id参数的detail视图方法。
  1. from django.shortcuts import render
  2. def detail(request,store_id):
  3.     # Access store_id with 'store_id' variable
  4.     return render(request,'stores/detail.html')
  5. Listing 2-6.Django view method
  6. in views.py to access url parameter
复制代码
注意清单 2-6 中的detail方法有两个参数。第一个参数是一个request对象,它对于所有 Django 视图方法都是相同的。第二个参数是 url 传递的参数。需要注意的是,url 参数的名称必须与方法参数的名称相匹配。在这种情况下,注意清单 2-5 中的参数名是store_id,而清单 2-6 中的方法参数也被定名为store_id。
通过 view method 参数访问 url 参数,该方法可以利用该参数执行逻辑(例如,查询数据库),然后可以将该参数传递给 Django 模板以供出现。
Caution
无论正则表达式怎样,Django url 参数总是被视为字符串。例如,\d+捕获数字,但值 1 被视为“1”(字符串),而不是 1(整数)。假如您计划在视图方法中利用 url 参数,并执行需要字符串以外的内容的操作,这一点尤其紧张。
由视图方法处理惩罚的 url 参数的另一个可用选项是使它们成为可选的,这反过来答应您对多个 URL 利用相同的视图方法。通过为视图方法参数分配默认值,可以使参数成为可选的。清单 2-7 显示了一个新的 url,它调用了同一个视图方法(coffeehouse.stores.views.detail),但是没有定义参数。
  1. from coffeehouse.stores import views as stores_views
  2. urlpatterns = patterns[
  3.     url(r'^stores/',stores_views.detail),
  4.     url(r'^stores/(?P<store_id>\d+)/',stores_views.detail),
  5. ]
  6. Listing 2-7.Django urls with optional parameters leveraging the same view method
复制代码
假如您调用 url /stores/而没有修改清单 2-6 中的detail方法,您会得到一个错误。出现错误是因为detail视图方法需要一个store_id参数,而第一个 url 没有提供这个参数。要解决这个问题,您可以在视图方法中为store_id定义一个默认值,如清单 2-8 所示。
  1. from django.shortcuts import render
  2. def detail(request,store_id='1'):
  3.     # Access store_id with 'store_id' variable
  4.     return render(request,'stores/detail.html')
  5. Listing 2-8.Django view method
  6. in views.py with default value
复制代码
注意清单 2-8 中的store_id参数怎样赋值='1'。这意味着假如调用视图方法时没有利用store_id,参数将会有一个默认值'1'。这种方法答应您利用同一个视图方法来处理惩罚带有可选参数的多个 URL。
除了在视图方法中访问 url 参数之外,还可以从 url 定义中访问额外的选项。这些额外的选项在一个字典中定义,该字典被声明为 url 定义中的最后一个参数。在视图方法声明之后,添加一个字典,此中包含您盼望在视图方法中访问的键-值对。下面的代码片段展示了清单 2-7 中 url 语句的修改版本。
  1. url(r'^stores/',stores_views.detail,{'location':'headquarters'})
复制代码
在这种情况下,location键成为一个 url 额外选项,作为参数传递给 view 方法。访问 Url 额外选项就像访问 url 参数一样,因此要访问 view 方法中的 url 额外选项,您需要修改方法签名以接受与 url 额外选项同名的参数。在这种情况下,方法签名:
  1. def detail(request,store_id='1'):
复制代码
需要更改为:
  1. def detail(request,store_id='1',location=None):
复制代码
请注意,location参数通过赋予默认值None而成为可选参数。
最后,还可以在 Django 视图方法中访问由?和&分隔的 url 参数——技能上称为查询字符串。利用request对象可以在视图方法中访问这些类型的参数。
以 url /stores/1/?hours=sunday&map=flash为例,清单 2-9 展示了怎样利用request.GET从这个由?和&分隔的 url 中提取参数。
  1. from django.shortcuts import renderdef detail(request,store_id='1',location=None):
  2.     # Access store_id param with 'store_id' variable and location param with 'location' variable    # Extract 'hours' or 'map' value appended to url as    # ?hours=sunday&map=flash    hours = request.GET.get('hours', '')    map = request.GET.get('map', '')    # 'hours' has value 'sunday' or '' if hours not in url    # 'map' has value 'flash' or '' if map not in url    return render(request,'stores/detail.html')Listing 2-9.Django view method extracting url parameters with request.GET
复制代码
清单 2-9 利用了语法request.GET.get(<parameter>, '')。假如参数出如今request.GET中,它提取值并将其赋给一个变量以备将来利用;假如参数不存在,那么参数变量被赋予一个默认的空值''——你同样可以利用None或任何其他默认值——因为这是 Python 的标准字典get()方法语法的一部分,以获得默认值。
最后一个过程被设计成从 HTTP GET 请求中提取参数;然而,Django 还支持从 HTTP POST 请求中提取参数的语法request.POST.get,这将在 Django 表单一章和本章后面的 Django 视图方法请求一节中详细描述。
Url 整合和模块化

默认情况下,Django 会在项目主目次下的urls.py文件中查找 url 定义——值得一提的是,这是因为settings.py中的ROOT_URLCONF变量。然而,一旦一个项目超过了几个 URL,在这个文件中管理它们就变得困难了。例如,看看清单 2-10 中所示的urls.py文件。
  1. from django.conf.urls import url
  2. from django.views.generic import TemplateView
  3. from coffeehouse.about import views as about_views
  4. from coffeehouse.stores import views as stores_views
  5. urlpatterns = [
  6.     url(r'^$',TemplateView.as_view(template_name='homepage.html')),
  7.     url(r'^about/',about_views.index),
  8.     url(r'^about/contact/',about_views.contact),
  9.     url(r'^stores/',stores_views.index),
  10.     url(r'^stores/(?P<store_id>\d+)/',stores_views.detail,{'location':'headquarters'}),
  11.    ]
  12. Listing 2-10.Django urls.py with no url consolidation
复制代码
正如您在清单 2-10 中看到的,有几个 URL 有多余的根- about/和stores/。将这些 URL 分开分组会很有帮助,因为如许可以将常见的 URL 生存在各自的文件中,克制了对一个大的urls.py文件进行修改的困难。
清单 2-11 显示了urls.py文件的更新版本,此中about/和stores/根放在不同的文件中。
  1. from django.conf.urls import include, url
  2. from django.views.generic import TemplateView
  3. urlpatterns = [
  4.     url(r'^$',TemplateView.as_view(template_name='homepage.html')),
  5.     url(r'^about/',include('coffeehouse.about.urls')),
  6.     url(r'^stores/',include('coffeehouse.stores.urls'),{'location':'headquarters'}),
  7.    ]
  8. Listing 2-11.Django urls.py with include to consolidate urls
复制代码
清单 2-11 利用include参数从完全独立的文件中加载 URL。在这种情况下,include('coffeehouse.about.urls')告诉 Django 从 Python 模块coffeehouse.about.urls加载 url 定义,这与 Django 基目次的分离对应于文件路径/coffeehouse/about/urls.py。在这种情况下,我继续利用urls.py文件名,并把它放在相应的 Django about app 目次下,因为它处理惩罚的是about/URL。但是,您可以利用任何您喜欢的文件名或路径来定义 url(例如,coffeehouse.allmyurl.resturls从文件路径/coffeehouse/allmyurls/resturls.py加载 URL)。
清单 2-11 中的第二个 include 语句与第一个一样,此中include('coffeehouse.stores.urls')告诉 Django 从 Python 模块coffeehouse.stores.urls加载 url 定义。但是,请注意,第二条语句附加了一个额外的字典作为 url 额外选项,这意味着 include 语句中的所有 URL 也将收到这个额外选项。
清单 2-12 展示了通过include('coffeehouse.about.urls')链接的文件/coffeehouse/about/urls.py的内容。
  1. from django.conf.urls import url
  2. from . import views
  3. urlpatterns = [
  4.     url(r'^$',views.index),
  5.     url(r'^contact/$',views.contact),
  6. ]
  7. Listing 2-12.Django /coffeehouse/about/urls.py loaded via include
复制代码
快速浏览清单 2-12 ,您可以看到其布局与主urls.py文件非常相似;然而,还是有一些渺小的区别。虽然 url 正则表达式r'^$'看起来像是匹配主页,但究竟并非如此。因为清单 2-12 中的文件通过主urls.py文件中的include链接,Django 将 url 正则表达式与父 url 正则表达式毗连起来。以是清单 2-12 中的第一个 url 实际上匹配/about/,清单 2-12 中的第二个 url 实际上匹配/about/contact/。还因为清单 2-12 中的urls.py文件放在应用的views.py文件旁边,以是导入语句利用相对路径from . import views语法。
除了利用include选项引用带有 url 定义的单独文件之外,include选项还可以接受作为 Python 列表的 url 定义。本质上,这答应你在主urls.py文件中保留所有的 url 定义,但是给它更多的模块性。清单 2-13 中说明了这种方法。
  1. from django.conf.urls import include, url
  2. from django.views.generic import TemplateView
  3. from coffeehouse.about import views as about_views
  4. from coffeehouse.stores import views as stores_views
  5. store_patterns = [
  6.     url(r'^$',stores_views.index),
  7.     url(r'^(?P<store_id>\d+)/$',stores_views.detail),
  8. ]
  9. about_patterns = [
  10.     url(r'^$',about_views.index),
  11.     url(r'^contact/$',about_views.contact),
  12. ]
  13. urlpatterns = [
  14.     url(r'^$',TemplateView.as_view(template_name='homepage.html')),
  15.     url(r'^about/',include(about_patterns)),
  16.     url(r'^stores/',include(store_patterns),{'location':'headquarters'}),
  17.    ]
  18. Listing 2-13.Django urls.py with inline include statements
复制代码
清单 2-13 中 url 模式的结果与清单 2-11 和 2-12 相同。区别在于清单 2-13 利用主urls.py文件声明多个 url 列表,而清单 2-11 和 2-12 依靠于在不同文件中声明的 url 列表。
Url 定名和名称空间

项目的内部链接或 url 引用(例如<a href='/'>Home Page</a>)通常是硬编码的,无论是在视图方法中将用户重定向到特定位置,还是在模板中提供足够的用户导航。随着项目的增长,硬编码链接会带来严重的维护问题,因为它会导致难以检测和修复的链接。Django 提供了一种定名 URL 的方法,因此很容易在视图方法和模板中引用它们。
定名 Django urls 最基本的技能是将name属性添加到urls.py中的url定义中。清单 2-14 展示了怎样定名一个项目的主页,以及怎样从一个视图方法或模板中引用这个 url。
  1. # Definition in urls.py
  2. url(r'^$',TemplateView.as_view(template_name='homepage.html'),name="homepage")
  3. # Definition in view method
  4. from django.http import HttpResponsePermanentRedirect
  5. from django.core.urlresolvers import reverse
  6. def method(request):
  7.     ....
  8.     return HttpResponsePermanentRedirect(reverse('homepage'))
  9. # Definition in template
  10. <a href="{% url 'homepage' %}">Back to home page</a>
  11. Listing 2-14.Django url using name
复制代码
清单 2-14 中的 url 定义利用了正则表达式r'^$',它被翻译成/或主页,也称为根目次。注意带有homepage值的name属性。通过给 url 分配一个name,您可以在视图方法和模板中利用这个值作为参考,这意味着将来对 url 正则表达式的任何更改,都会自动更新视图方法和模板中的所有 url 定义。
接下来在清单 2-14 中,您可以看到一个视图方法示例,它将控制重定向到reverse('homepage')。Django reverse方法试图通过给定的名称查找 url 定义——在本例中是homepage——并相应地替换它。雷同地,清单 2-14 中的链接示例<a href="{% url 'homepage' %}">Back to home page</a>利用了 Django {% url %}标签,该标签试图通过其第一个参数来查找 url 在本例中是homepage——并相应地替换它。
同样的定名和替换过程也适用于更复杂的 url 定义,比如那些带有参数的定义。清单 2-15 显示了带有参数的 url 的过程。
  1. # Definition in urls.py
  2. url(r'^drinks/(?P<drink_name>\D+)/',TemplateView.as_view(template_name='drinks/index.html'),name="drink"),
  3. # Definition in view method
  4. from django.http import HttpResponsePermanentRedirect
  5. from django.core.urlresolvers import reverse
  6. def method(request):
  7.     ....
  8.     return HttpResponsePermanentRedirect(reverse('drink', args=(drink.name,)))
  9. # Definition in template
  10. <a href="{% url 'drink' drink.name %}">Drink on sale</a>
  11. <a href="{% url 'drink' 'latte' %}">Drink on sale</a>
  12. Listing 2-15.Django url with arguments using name
复制代码
清单 2-15 中的 url 定义利用了一个更复杂的正则表达式,它带有一个参数,可以转换成情势为/drinks/latte/或/drinks/espresso/的 URL。在这种情况下,url 被赋予参数名drink_name。
因为 url 利用一个参数,reverse方法和{% url %}标记的语法略有不同。reverse方法要求 url 参数作为元组提供给args变量,而{% url %}标签要求 url 参数作为值列表提供。注意,在清单 2-15 中,参数同样可以是变量或硬编码值,只要它匹配 url 参数正则表达式类型——在本例中黑白数字。
对于有多个参数的 url 定义,利用reverse和{% url %}的方法是相同的。对于reverse方法,您传递给它一个带有所有须要参数的元组,对于{% url %}标记,您传递给它一个值列表。
Caution
鉴戒利用反向和{% url %}的无效 url 定义。Django 总是在启动时查抄所有反向和{% url %}定义是否有用。这意味着假如您在反向方法或{% url %}标记定义中出错——比如 url 名称中的输入错误或参数类型与正则表达式不匹配——应用将不会启动并抛出 HTTP 500 内部错误。
这种情况的偏差是NoReverseMatch at....Reverse for 'urlname' with arguments '()' and keyword arguments '{}' not found. X pattern(s) tried。假如您查看错误堆栈,您将可以或许指出这是在那里发生的,并纠正它。请注意,这是一个致命的错误,假如它没有被隔离到发生它的视图或页面,它将在启动时克制整个应用。
有时利用属性本身不足以对 URL 进行分类。假如您有两个或三个索引页面,会发生什么情况?或者,假如您有两个符合详细信息条件的 URL,但一个是市肆的,另一个是饮料的?
一种简朴的方法是利用复合名称(例如,drink_details、store_details)。然而,在这种情势中利用复合名称会导致难以影象的定名约定和疏松的层次布局。Django 支持的一种更简洁的方法是通过namespace属性。
属性答应用一个唯一的限定符来标识一组 URL。因为namespace属性与一组 URL 干系联,以是它与前面描述的include方法一起利用来归并 URL。
清单 2-16 展示了一系列 url 定义,它们利用了包含的名称空间属性。
  1. # Main urls.py
  2. from django.conf.urls import include, url
  3. urlpatterns = [
  4.     url(r'^$',TemplateView.as_view(template_name='homepage.html'),name="homepage"),
  5.     url(r'^about/',include('coffeehouse.about.urls',namespace="about")),
  6.     url(r'^stores/',include('coffeehouse.stores.urls',namespace="stores")),
  7. ]
  8. # About urls.py
  9. from . import views
  10. urlpatterns = [
  11.     url(r'^$',views.index,name="index"),
  12.     url(r'^contact/$',views.contact,name="contact"),
  13. ]
  14. # Stores urls.py
  15. from . import views
  16. urlpatterns =
  17.     url(r'^$',views.index,name="index"),
  18.     url(r'^(?P<store_id>\d+)/$',views.detail,name="detail"),
  19. )
  20. # Definition in view method
  21. from django.http import HttpResponsePermanentRedirect
  22. from django.core.urlresolvers import reverse
  23. def method(request):
  24.     ....
  25.     return HttpResponsePermanentRedirect(reverse('about:index'))
  26. # Definition in template
  27. <a href="{% url 'stores:index' %}">Back to stores index</a>
  28. Listing 2-16.Django urls.py
  29. with namespace attribute
复制代码
清单 [2-16 以一组典型的 Django urls.py主文件的include定义开始。注意这两个定义都利用了namespace属性。接下来,您可以看到在主urls.py文件中引用的urls.py文件,这些文件利用了前面示例中描述的name属性。注意 about 和 stores urls.py文件都有一个带name='index'的 url。
要用名称空间限定 url 名称,可以利用语法<namespace>:<name>。正如您在清单 2-16 的底部看到的,要引用 about urls.py中的索引,您利用about:index,要引用 stores urls.py文件中的索引,您利用stores:index.
namespace 属性也可以嵌套利用语法<namespace1>:<namespace2>:<namespace3>:<name>来引用 URL。清单 2-17 展示了一个嵌套名称空间属性的例子。
  1. # Main urls.py
  2. from django.conf.urls import include, url
  3. from django.views.generic import TemplateView
  4. urlpatterns = [
  5.     url(r'^$',TemplateView.as_view(template_name='homepage.html'),name="homepage"),
  6.     url(r'^stores/',include('coffeehouse.stores.urls',namespace="stores")),
  7. ]
  8. # Stores urls.py
  9. from . import views
  10. urlpatterns = [
  11.     url(r'^$',views.index,name="index"),
  12.     url(r'^(?P<store_id>\d+)/$',views.detail,name="detail"),
  13.     url(r'^(?P<store_id>\d+)/about/',include('coffeehouse.about.urls',namespace="about")),
  14. ]
  15. # About urls.py
  16. from . import views
  17. urlpatterns = [
  18.     url(r'^$',views.index,name="index"),
  19.     url(r'^contact/$',views.contact,name="contact"),
  20. ]
  21. # Definition in view method
  22. from django.http import HttpResponsePermanentRedirect
  23. from django.core.urlresolvers import reverse
  24. def method(request):
  25.     ....
  26.     return HttpResponsePermanentRedirect(reverse('stores:about:index', args=(store.id,)))
  27. # Definition in template
  28. <a href="{% url 'stores:about:index' store.id %}">See about for {{store.name}}</a>
  29. Listing 2-17.Django urls.py with nested namespace attribute
复制代码
清单 2-17 中的 url 布局与清单 2-16 的不同之处在于,它为每个市肆(例如/stores/1/about/)创建了 about url,而不是拥有一个通用的 about URL(例如/about/)。在清单 2-17 的顶部,我们利用namespace="stores"来限定 stores urls.py文件中的所有 URL。
接下来,在 stores urls.py文件中,注意另有另一个带有namespace="about"的include元素来限定 about urls.py中的所有 URL。最后,在 about urls.py文件中,有一些 URL 只利用了name属性。在清单 2-17 的最后一部分,您可以看到嵌套的名称空间是怎样与reverse方法和{% url %}标签一起利用的,它们利用一个:来分隔名称空间。
在 99%的 Django urls 中,你可以像描述的那样利用name和namespace参数。然而,当您在同一个项目中摆设同一个 Django 应用的多个实例时,namespace参数具有特别的意义。
因为 Django 应用是具有 url 定义的自包含单位,以是纵然 Django 应用利用 url 名称空间,也会出现边缘情况。假如 Django 应用利用名称空间 X,但是您想在同一个项目中摆设该应用两次或三次,会发生什么情况?假设每个应用都利用名称空间 X,那么怎样引用它们的 URL 呢?这就是术语实例名称空间和app_name属性的由来。
让我们看一个场景,这个场景利用同一个 Django 应用的多个实例来说明这个与 url 名称空间干系的边缘情况。假设您开发了一个名为 banners 的 Django 应用来显示广告。横幅应用的构建方式是,它必须在不同的 URL 上运行(例如,/coffeebanners/、/teabanners/、/foodbanners/),以简化横幅的选择。本质上,您需要在同一个项目中运行横幅应用的多个实例,每个实例位于不同的 URL 上。
那么多个 app 实例和 url 定名有什么问题呢?它与利用需要根据当前应用实例动态改变的定名 URL 有关。这个问题通过一个例子最容易理解,以是让我们跳到清单 2-18 中的例子。
  1. # Main urls.py
  2. from django.conf.urls import include, url
  3. urlpatterns = [
  4.     url(r'^$',TemplateView.as_view(template_name='homepage.html'),name="homepage"),
  5.     url(r'^coffeebanners/',include('coffeehouse.banners.urls',namespace="coffee-banners")),
  6.     url(r'^teabanners/',include('coffeehouse.banners.urls',namespace="tea-banners")),
  7.     url(r'^foodbanners/',include('coffeehouse.banners.urls',namespace="food-banners")),
  8. ]
  9. # Banners urls.py
  10. from django.conf.urls import url
  11. from . import views
  12. urlpatterns = [
  13.     url(r'^$',views.index,name="index"),
  14. ]
  15. # Definition in view method
  16. from django.http import HttpResponsePermanentRedirect
  17. from django.core.urlresolvers import reverse
  18. def method(request):
  19.     ....
  20.     return HttpResponsePermanentRedirect(reverse('coffee-banners:index'))
  21.     return HttpResponsePermanentRedirect(reverse('tea-banners:index'))
  22.     return HttpResponsePermanentRedirect(reverse('food-banners:index'))
  23. # Definition in template
  24. <a href="{% url 'coffee-banners:index' %}">Coffee banners</a>
  25. <a href="{% url 'tea-banners:index' %}">Tea banners</a>
  26. <a href="{% url 'food-banners:index' %}">Food banners</a>
  27. Listing 2-18.Django urls.py with multiple instances
  28. of the same app
复制代码
在清单 2-18 中,你可以看到我们有三个指向同一个coffeehouse.banners.urls文件的 URL,每个都有自己独特的名称空间。接下来,让我们看看清单 2-18 中的各种reverse方法和{% url %}标记示例。
清单 2-18 中的reverse方法和{% url %}标记示例都利用<namespace>:<name>语法解析为三个不同的 url 名称。以是你可以利用namespace和name有用地摆设同一个 Django 应用的多个实例。
然而,仅仅依靠namespace和name,解析的 url 名称无法动态适应不同的应用实例,这是与内部应用逻辑干系的边缘情况,必须包含内部应用逻辑以支持 Django 应用的多个实例。如今让我们来看看一个视图和模板场景,它展示了这个场景以及app_name属性怎样解决这个问题。
假设在横幅应用中,您想要将控制重定向到应用的主索引 url(例如,由于异常)。如今戴上应用设计师的帽子,你会怎样解决这个问题?作为一名应用设计师,你乃至不知道咖啡横幅、茶横幅或食品横幅名称空间,因为这些是摆设名称空间。您怎样在应用中内部集成重定向,以适应正在摆设的应用的多个实例?这就是app_name参数的用途。
清单 2-19 展示了怎样利用app_name属性来动态确定重定向到那里。
  1. # Main urls.py
  2. from django.conf.urls import include, url
  3. urlpatterns = [
  4.     url(r'^$',TemplateView.as_view(template_name='homepage.html'),name="homepage"),
  5.     url(r'^coffeebanners/',include('coffeehouse.banners.urls',namespace="coffee-banners")),
  6.     url(r'^teabanners/',include('coffeehouse.banners.urls',namespace="tea-banners")),
  7.     url(r'^foodbanners/',include('coffeehouse.banners.urls',namespace="food-banners")),
  8. ]
  9. # Banners urls.py
  10. from django.conf.urls import url
  11. from . import views
  12. app_name = 'banners_adverts'
  13. urlpatterns = [
  14.     url(r'^$',views.index,name="index"),
  15. ]
  16. # Logic inside Banners app
  17. from django.http import HttpResponsePermanentRedirect
  18. from django.core.urlresolvers import reverse
  19. def method(request):
  20.     ....
  21.     try:
  22.        ...
  23.     except:
  24.        return HttpResponsePermanentRedirect(reverse('banners_adverts:index'))
  25. Listing 2-19.Django redirect that leverages app_name to determine url
复制代码
注意横幅应用的清单 2-19 中的urls.py文件在声明urlpatterns值之前设置了app_name属性。接下来,注意清单 2-19 中的reverse方法利用了banners_adverts:index值,此中banners_adverts代表app_name。这是一个紧张的约定,因为 Django 依靠相同的语法来搜索app_name或namespace匹配。
那么你以为banners_adverts:index会解析到什么 url 呢?这完全取决于导航发生在那里,它是动态的!假如用户在咖啡横幅应用实例(即 url coffeebanners)中导航,那么 Django 将banners_adverts:index解析为咖啡横幅实例索引,假如用户在茶横幅应用实例(即 url teabanners)中导航,那么 Django 将banners_adverts:index解析为茶横幅实例索引,以此类推任何其他数量的实例。假如用户在 banners 应用实例之外导航(即没有应用实例),Django 默认将banners_adverts:index解析为urls.py中最后定义的实例,这将是 food-banners。
以这种方式并基于用户来自的请求路径实例(例如,假如用户在带有/coffeebanners/或/teabanners/的路径上),反向方法将banners_adverts:index动态解析为三个 url 应用实例之一,而不是硬编码特定的 url 名称空间,如清单 2-18 所示。
如今让我们假设 banners 应用有一个内部模板,内里有一个到应用主index url 的链接。同样,考虑到多个应用实例的可能性,您将怎样在模板中生成这个链接?依靠相同的app_name参数解决了清单 2-20 中所示的模板链接的问题。
  1. # template banners/index.html
  2. <a href="{% url 'banners_adverts:index' %}">{% url 'banners_adverts:index' %}</a>
  3. Listing 2-20.Django template link
  4. that leverages app_name to determine url
复制代码
注意清单 2-20 中的{% url %}标签指向banners_adverts:index。banners_adverts:index的解析过程与前面利用reverse方法的方法示例中概述的过程相同。
假如用户在咖啡横幅应用实例(即 url coffeebanners)中导航,那么 Django 将banners_adverts:index解析为咖啡横幅实例索引,假如用户在茶横幅应用实例(即 url teabanners)中导航,那么 Django 将banners_adverts:index解析为茶横幅实例索引,以此类推任何其他数量的实例。假如用户在横幅应用实例之外导航(即没有应用实例),Django 默认将banners_adverts:index解析为urls.py中最后定义的实例,即food-banners。
正如您所看到的,app_name属性的目的是为 Django 应用设计者提供一种内部机制,通过这种机制,可以为动态适应同一应用的多个实例的定名 URL 集成逻辑。由于这个原因,它没有被广泛用于 url 定名,而且在大多数情况下可以被放弃,只利用namespace和name属性。
查看方法请求

到如今为止,您已经利用了 Django 视图方法及其输入——request对象和参数——以及它们的输出,包括生成直接响应或依靠模板生成响应。然而,如今是时间更深入地了解视图方法请求中的可用内容以及生成视图方法响应的各种替换方法了。
到如今为止,您毫无疑问地放在视图方法中的request引用是django.http.request.HttpRequest类的一个实例。 3 这个request对象包含由视图方法之前存在的实体设置的信息:用户的 web 浏览器、运行应用的 web 服务器或应用上设置的 Django 中间件类。
下面的列表显示了一些在request参考中最常见的属性和方法:


  • request.method。-包含用于请求的 HTTP 方法(例如,GET、POST)。
  • request.GET或request.POST。-包含分别作为 GET 或 POST 请求的一部分添加的参数。参数被括为一个django.http.request.QueryDict 4 个 实例。

    • request.POST.get('name',default=None)。-获取 POST 请求中的name参数的值,假如该参数不存在,则获取None。注意default可以用自定义值覆盖。
    • request.GET.getlist('drink',default=None)。-获取 GET 请求中的drink参数的值列表,或者假如参数不存在,则获取空列表None。注意default可以用自定义值覆盖。

  • request.META。-包含由浏览器或 web 服务器作为请求的一部分添加的 HTTP 标头。参数包含在一个标准的 Python 字典中,此中键是 HTTP 头名称——大写和下划线(例如,Content-Length作为键CONTENT_LENGTH)。

    • request.META['REMOTE_ADDR']。-获取用户的远程 IP 地点。

  • request.user。-包含链接到请求的 Django 用户的信息(如用户名、电子邮件)。注意user指的是django.contrib.auth包中的用户,通过 Django 中间件设置,这将在本章后面描述。
正如您可以从这个简短的列表中证明的那样,request引用包含许多可操作的信息来满足业务逻辑(例如,您可以基于来自用户'的 IP 地点的地理位置信息来响应某些内容)。在django.http.request.HttpRequest和django.http.request.QueryDict属性和方法之间有超过 50 个request选项可用,所有这些都在书中干系的部分进行了解释——但是你可以在上一页的脚注链接中查看request选项的完整范围。
一旦您完成了从request引用中提取信息并对其进行干系的业务逻辑处理惩罚(例如,查询数据库,从第三方 REST 服务中获取数据),您就需要在一个视图方法中设置数据,以将其作为响应的一部分发送出去。
要在 Django 视图方法中设置数据,首先需要在方法体中声明或提取数据。您可以声明字符串、数字、列表、元组、字典或任何其他 Python 数据布局。
一旦在视图方法中声明或提取了数据,就可以创建一个字典来使数据在 Django 模板上可访问。字典键代表模板的引用名,而值是数据布局本身。清单 2-21 展示了一个视图方法,它声明了多个数据布局并将它们传递给 Django 模板。
  1. from django.shortcuts import renderdef detail(request,store_id='1',location=None):
  2.     # Create fixed data structures to pass to template    # data could equally come from database queries    # web services or social APIs    STORE_NAME = 'Downtown'    store_address = {'street':'Main #385','city':'San Diego','state':'CA'}    store_amenities = ['WiFi','A/C']    store_menu = ((0,''),(1,'Drinks'),(2,'Food'))    values_for_template = {'store_name':STORE_NAME, 'store_address':store_address, 'store_amenities':store_amenities, 'store_menu':store_menu}    return render(request,'stores/detail.html', values_for_template)Listing 2-21.Set up dictionary in Django view method for access in template
复制代码
注意清单 2-21 中的render方法是怎样包含values_for_template字典的。在前面的例子中,render方法只包含了request对象和一个处理惩罚请求的模板。在清单 2-21 中,字典作为最后一个render参数被传递。通过将一个字典指定为最后一个参数,该字典对模板可用——在本例中是stores/detail.html。
Tip
假如您计划在多个模板上访问相同的数据,而不是在多个视图上声明它,您可以利用上下文处理惩罚器来声明它一次,并使它在所有项目模板上都可以访问。关于 Django 模板的下一章将讨论这个主题。
清单 2-21 中的字典包含键和值,它们是在方法体中声明的数据布局。字典键成为访问 Django 模板中的值的引用。
Output View Method Dictionary in Django Templates
虽然下一章将深入讨论 Django 模板,但是下面的代码片段显示了怎样利用{{}}语法输出清单 2-21 中的字典值。
  1. <h4>{{store_name}} store</h4>
  2. <p>{{store_address.street}}</p>
  3. <p>{{store_address.city}},{{store_address.state}}</p>
  4. <hr/>
  5. <p>We offer: {{store_amenities.0}} and {{store_amenities.1}}</p>
  6. <p>Menu includes : {{store_menu.1.1}} and {{store_menu.2.1}}</p>
复制代码
第一个声明{{store_name}}利用独立键显示Downtown值。其他访问声明利用点(.)符号,因为值本身是复合数据布局。
store_address键包含一个字典,因此要访问内部字典值,可以利用由点(.)分隔的内部字典键。store_address.street显示街道值,store_address.city显示城市值,store_address.state显示州值。
store _ 市容键包含一个利用雷同点(.)符号来访问内部值。然而,因为 Python 列表没有键,以是利用列表索引号。store _ facilities . 0 显示列表 store _ facilities 和 store _ facilities 中的第一项。1 显示列表 store _ facilities 中的第二项。
store_menu key包含一个元组,由于缺少键,它也需要一个数字。{{store_menu.1.1}}显示store_menu的第二元组值的第二元组值,{{store_menu.2.1}}显示store_menu的第三元组的第二元组值。
查看方法响应

到如今为止,生成视图方法响应的render()方法实际上是一种快捷方式。你可以在清单 2-21 的顶部看到,render()方法是django.shortcuts包的一部分。
这意味着除了生成视图响应的render()方法之外,另有其他方法,尽管render()方法是最常用的技能。对于初学者来说,有三种雷同的方法来生成带有模板支持的数据的视图方法响应,如清单 2-22 所示。
  1. # Option 1)from django.shortcuts import renderdef detail(request,store_id='1',location=None):
  2.     ...    return render(request,'stores/detail.html', values_for_template)# Option 2)from django.template.response import TemplateResponsedef detail(request,store_id='1',location=None):
  3.     ...    return TemplateResponse(request, 'stores/detail.html', values_for_template)# Option 3)from django.http import HttpResponsefrom django.template import loader, Contextdef detail(request,store_id='1',location=None):
  4.      ...     response = HttpResponse()     t = loader.get_template('stores/detail.html')     c = Context(values_for_template)     return response.write(t.render(c))Listing 2-22.Django view method response alternatives
复制代码
清单 2-22 中的第一个选项是django.shortcuts.render()方法,它显示了生成响应的三个参数必须的)request引用、(必须的)模板路由和(可选的)字典——也称为上下文——以及要传递给模板的数据。
清单 2-22 : content_type中没有显示render()方法的别的三个(可选)参数,这些参数为响应设置 HTTP Content-Type头,默以为settings.py中的DEFAULT_CONTENT_TYPE参数,后者本身默以为text/html;status为默以为200的响应设置 HTTP Status代码;和using来指定模板引擎——或者是jinja2或者是django——来生成响应。下一节关于render()方法的 HTTP 处理惩罚描述了怎样利用content_type & status,而第 3 和 4 章讨论了 Django 和 Jinja 模板引擎。
清单 2-22 中的第二个选项是django.template.response.TemplateResponse()类,它在输入方面与render()方法几乎相同。这两种变体的区别在于,一旦一个视图方法完成后,TemplateResponse()可以改变响应(例如,通过中间件),而render()方法被以为是视图方法完成后生命周期中的最后一步。当您预见到需要在多个视图方法完成工作后修改它们的视图方法响应时,您应该利用TemplateResponse(),这种技能将在本章关于视图方法中间件的后续章节中讨论。
清单 2-22 : content_type中没有显示的TemplateResponse()类另有四个(可选)参数,默以为text/html;status默以为200;charset设置来自 HTTP Content-Type头或settings.py中的DEFAULT_CHARSET的响应编码,其本身默以为utf-8;和using来指示模板引擎——或者jinja2或者django——来生成响应。
清单 2-22 中的第三个选项代表了最长的,但也是最灵活的响应创建过程。这个过程首先创建一个原始的HTTPResponse实例,然后用django.template.loader.get_template()方法加载一个模板,创建一个Context()类将值加载到模板中,最后将一个出现的模板及其上下文写到HTTPResponse实例中。虽然这是三个选项中最长的一个,但是当视图方法响应需要高级选项时,这是首选。即将到来的关于内联和流式内容的内置响应快捷方式的部分,有更多关于HTTPResponse响应类型的细节。
HTTP 状态和内容类型标头的响应选项

浏览器在请求中设置 HTTP 头,告诉应用在处理惩罚时考虑某些特性。雷同地,应用在响应中设置 HTTP 头,告诉浏览器考虑发送内容的某些特性。Django 等应用设置的最紧张的 HTTP 头是Status和Content-Type。
HTTP Status头是一个三位数的代码,表示给定请求的响应状态。Status值的例子有200,它是成功的 HTTP 请求的标准响应,以及404,它用于指示无法找到请求的资源。HTTP Content-Type报头是一个 MIME(多用途互联网邮件扩展)类型的字符串,用于指示响应中的内容类型。Content-Type值的例子有text/html,它是 HTML 内容响应的标准,以及image/gif,它用于指示响应是 GIF 图像。
默认情况下,除非有错误,所有用django.shortcuts.render()、TemplateResponse()类或HttpResponse()类创建响应的 Django 视图方法——如清单 2-22 所示——创建一个响应,将 HTTP Status值设置为200,将 HTTP Content-Type设置为text/html。虽然这些默认值是最常见的,但是假如您想要发送不同类型的响应(例如,错误或非 HTML 内容),就有须要修改这些值。
覆盖清单 2-22 中三个选项中任意一个的 HTTP Status和Content-Type头值就像提供额外的参数status和/或content_type一样简朴。清单 2-23 展示了这个过程的各种例子。
  1. from django.shortcuts import render
  2. # No method body(s) and only render() example provided for simplicity
  3. # Returns content type text/plain, with default HTTP 200
  4. return render(request,'stores/menu.csv', values_for_template, content_type='text/plain')
  5. # Returns HTTP 404, wtih default text/html
  6. # NOTE: Django has a built-in shortcut & template 404 response, described in the next section
  7. return render(request,'custom/notfound.html',status=404)
  8. # Returns HTTP 500, wtih default text/html
  9. # NOTE: Django has a built-in shortcut & template 500 response, described in the next section
  10. return render(request,'custom/internalerror.html',status=500)
  11. # Returns content type application/json, with default HTTP 200
  12. # NOTE: Django has a built-in shortcut JSON response, described in the next section
  13. return render(request,'stores/menu.json', values_for_template, content_type='application/json')
  14. Listing 2-23.HTTP Content-type and HTTP Status for Django view method responses
复制代码
清单 2-23 中的第一个例子旨在返回一个包含纯文本内容的响应。注意render方法的content_type参数。清单 2-23 中的第二个和第三个例子将 HTTP Status代码设置为404和500。因为 HTTP Status 404代码用于未找到的资源,以是render方法为此利用了一个特别的模板。雷同地,因为 HTTP Status 500代码用于指示错误,render方法也为此利用了一个特别的模板。
Tip
Django 有内置的快捷方式和模板来处理惩罚 HTTP Status代码404和500,以及 JSON 快捷方式响应,所有这些都将在下一节中描述,您可以利用它们来取代清单 2-23 中的示例。
清单 2-23 中的第四个也是最后一个例子旨在返回一个带有 JavaScript 对象符号(JSON)内容的响应。HTTP Content-Type application/json是通过异步 JavaScript (AJAX)消费 JavaScript 数据的浏览器发出的请求的常见要求。
常见 HTTP 状态的内置响应快捷方式和模板:404(未找到)、500(内部服务器错误)、400(错误请求)和 403(禁止)

虽然 Django 在找不到页面时会自动触发 HTTP 404 Status (Not Found)响应,而且在视图中出现未处理惩罚的异常时也会触发 HTTP 500 Status(内部服务器错误)响应,但是它有内置的快捷方式和模板,当您知道最终用户应该获得它们时,这些快捷方式和模板就应该在 Django 视图中明确利用。表 2-3 说明了触发特定 HTTP 状态响应的不同快捷方式。
表 2-3。
Django shortcut exceptions to trigger HTTP statuses
| HTTP 状态代码 | Python 代码示例 | | --- | --- | | 404(未找到) | 从 django.http 导入 Http404 引发 Http404 | | 500(内部服务器错误) | 引发异常 | | 400(错误请求) | 从 django.core.exceptions 导入可疑操作提出可疑操作 | | 403(禁止) | 从 django.core.exceptions 导入权限拒绝提升权限拒绝 | *Django automatically handles not found pages raising HTTP 404 and unhandled exceptions raising HTTP 500
正如你在表 2-3 的例子中看到的,快捷方式的语法很简朴。例如,您可以在 Django 视图中进行评估,如if article_id < 100:或if unpayed_subscription:,并基于结果从表 2-3 中抛出异常,以便最终用户获得正确的 HTTP 状态响应。
那么当表 2-3 中的异常被触发时,除了 HTTP 状态之外,响应中发送的实际内容是什么呢?HTTP 400(错误请求)和 HTTP 403(禁止请求)的默认设置是一个单行 HTML 页面,分别显示“Bad Request (400)和“403 Forbidden”。对于 HTTP 404(未找到)和 HTTP 500(内部服务器错误),取决于settings.py中的DEBUG值。
假如 Django 项目在settings.py中有DEBUG=True,HTTP 404(未找到)生成一个带有可用 URL 的页面——如图 2-1 所示——HTTP500(内部服务器错误)生成一个带有详细错误的页面——如图 2-2 所示。假如 Django 项目在settings.py中有DEBUG=False,HTTP 404(未找到)会生成一个单行 HTML 页面,显示为“Not Found. The requested URL <url_location> was not found on this server.”,HTTP 500(内部服务器错误)会生成一个单行 HTML 页面,显示为“A server error occurred. Please contact the administrator"。

图 2-2。
HTTP 500 for Django project when DEBUG=True

图 2-1。
HTTP 404 for Django project when DEBUG=True
还可以用定制模板覆盖所有从前的 HTTP 代码的默认响应页面。要利用定制的响应页面,您需要用所需的 HTTP 代码和.html扩展名创建一个模板。例如,对于 HTTP 403,您将创建403.html模板,对于 HTTP 500,您将创建500.html模板。所有这些定制的 HTTP 响应模板都需要放在TEMPLATES变量的DIRS列表中定义的文件夹中,以便 Django 在利用默认的 HTTP 响应模板之前找到它们。
Caution
自定义 404.html 和 500.html 页面仅在 DEBUG=False 时有用。
假如DEBUG=True,无论在正确的位置是否有404.html或500.html模板,Django 都利用默认的响应行为,分别如图 2-1 和图 2-2 所示。您需要设置DEBUG=False以使自定义404.html和500.html模板工作。
在某些情况下,利用定制的 HTTP 响应模板可能还不够。例如,假如您想将上下文数据添加到处理惩罚 HTTP 响应的自定义模板中,您需要自定义内置的 Django HTTP view 方法本身,因为没有其他方法可以将数据传递到这种类型的模板中。要定制内置的 Django HTTP 视图方法,您需要在项目的urls.py文件中声明特别的处理惩罚程序。清单 2-24 展示了带有 Django 内置 HTTP Status视图方法的定制处理惩罚程序的urls.py文件。
  1. # Overrides the default 400 handler django.views.defaults.bad_request
  2. handler400 = 'coffeehouse.utils.views.bad_request'
  3. # Overrides the default 403 handler django.views.defaults.permission_denied
  4. handler403 = 'coffeehouse.utils.views.permission_denied'
  5. # Overrides the default 404 handler django.views.defaults.page_not_found
  6. handler404 = 'coffeehouse.utils.views.page_not_found'
  7. # Overrides the default 500 handler django.views.defaults.server_error
  8. handler500 = 'coffeehouse.utils.views.server_error'
  9. urlpatterns = [....
  10. ]
  11. Listing 2-24.Override built-in Django HTTP Status
  12. view methods in urls.py
复制代码
Caution
假如 DEBUG=True,handler404 和 handler500 处理惩罚程序将无法工作,Django 将继续利用内置的 Django HTTP 视图方法。要使 handler404 和 handler500 处理惩罚程序工作,需要设置 DEBUG=False。
正如您在清单 2-24 中看到的,在标准urlpatterns变量的正上方urls.py中有一系列变量。清单 2-24 中的每个变量代表一个 HTTP Status处理惩罚程序,其值对应于一个定制的 Django 视图来处理惩罚请求。例如,handler400表示所有 HTTP 400请求都应该由 Django 视图方法coffeehouse.utils.views.bad_request处理惩罚,而不是默认的django.views.defaults.bad_request。对于利用handler403的 HTTP 403请求、利用handler404的 HTTP 404请求和利用handler500的 HTTP 500 请求,采取相同的方法。
就定制 Django 视图方法的实际布局而言,它们与任何其他 Django 视图方法都是相同的。清单 2-26 显示了清单 2-25 中利用的定制视图方法的布局。
  1. from django.shortcuts import render
  2. def page_not_found(request):
  3.     # Dict to pass to template, data could come from DB query
  4.     values_for_template = {}
  5.     return render(request,'404.html',values_for_template,status=404)
  6. def server_error(request):
  7.     # Dict to pass to template, data could come from DB query
  8.     values_for_template = {}
  9.     return render(request,'500.html',values_for_template,status=500)
  10. def bad_request(request):
  11.     # Dict to pass to template, data could come from DB query
  12.     values_for_template = {}
  13.     return render(request,'400.html',values_for_template,status=400)
  14. def permission_denied(request):
  15.     # Dict to pass to template, data could come from DB query
  16.     values_for_template = {}
  17.     return render(request,'403.html',values_for_template,status=403)
  18. Listing 2-25.Custom views to override built-in Django HTTP view methods
复制代码
正如您在清单 2-26 中看到的,定制的 HTTP 视图方法利用了与前面的视图方法示例相同的来自django.shortcut s 的render方法。这些方法指向一个由 HTTP Status代码定名的模板,利用一个可以在模板上访问的定制数据字典,并利用status参数来指示 HTTP 状态代码。
内联和流式内容的内置响应快捷方式

所有先前的视图响应示例都是基于通过模板布局化的内容来工作的。然而,有时利用模板输出响应是不须要的(例如,一行响应说“这里没有什么可看的”)。
其他时间,响应利用模板是没有意义的,例如 HTTP 301(永久重定向)或 HTTP 302(重定向),此中响应只需要一个重定向 url。表 2-4 说明了触发 HTTP 重定向的不同快捷方式。
表 2-4。
Django shortcuts for HTTP redirects
| HTTP 状态代码 | Python 代码示例 | | --- | --- | | 301(永久重定向) | 从 django.http 导入 HttpResponsePermanentRedirect 返回 HttpResponsePermanentRedirect("/") | | 302(重定向) | 从 django.http 导入 httpresponserdirect 返回 httpresponserdirect("/") | 表 2-4 中的两个示例都重定向到应用的主页(即"/")。但是,您也可以将重定向设置为任何应用 url,乃至是不同域上的完整 url(例如, http://maps.google.com/ )。
除了响应重定向快捷方式,Django 还提供了一系列响应快捷方式,您可以在此中添加内联响应。表 2-5 说明了具有内嵌内容响应的 HTTP 状态代码的各种其他快捷方式。
表 2-5。
Django shortcuts for inline and streaming content responses
| 目的或 HTTP 状态代码 | Python 代码示例 | | --- | --- | | 304(未修改) | 从 django.http 导入 HttpResponseNotModified 返回 HttpResponseNotModified()* | | 400(错误请求) | 从 django.http 导入 HttpResponseBadRequest 返回 HttpResponseBadRequest(" 请求看起来不对劲

“) |
| 404(未找到) | 从 django.http 导入 HttpResponseNotFound 返回 HttpResponseNotFound(”
Ups,我们找不到那个页面

") |
| 403(禁止) | 从 django.http 导入 HttpResponseForbidden 返回 HttpResponseForbidden(“这里什么都看不到”,content_type="text/plain “) |
| 405(不答应的方法) | 从 django.http 导入 HttpResponseNotAllowed 返回 HttpResponseNotAllowed(”
方法不答应利用

") |
| 410(走了) | 从 django.http 导入 HttpResponseGone 返回 HttpResponseGone(“不再在这里”,content_type="text/plain “) |
| 500(内部服务器错误) | 从 django.http 导入 httpresponseserverror 返回 httpresponseserverror(”
Ups,这是我们的失误,抱歉!

)) |
| 将数据序列化为 JSON 的内联响应(默以为 HTTP 200 和内容类型 application/json) | 从 django.http 导入 JSON response data _ dict = { ’ name ‘:’ Downtown ‘,’ address’:‘Main #385 ‘,’ city’:‘San Diego ‘,’ state’:‘CA’}返回 JsonResponse(data_dict) |
| 流数据内联响应(默以为 HTTP 200 和流内容,它是字符串的迭代器) | 从 django.http 导入 StreamingHttpResponse 返回 streaming httpresponse(large _ data _ structure) |
| 流二进制文件的内联响应(默以为 HTTP 200 和流内容) | 从 django.http 导入文件响应返回文件响应(open('Report.pdf ‘,’ rb ')) |
| 带有任何 HTTP 状态代码的内联响应(默以为 HTTP 200) | 从 django.http 导入 HttpResponse 返回 HttpResponse("
Django 内联响应

") |


  • The HTTP 304 status code indicates a “Not Modified” response, so you can’t send content in the response, it should always be empty.
正如您在表 2-5 的示例中所看到的,有多种快捷方式可以生成带有内嵌内容的不同 HTTP 状态响应,而且完全不需要利用模板。别的,你可以看到表 2-5 中的快捷键也可以接受content_type参数,假如内容是 HTML 以外的东西(即content_type=text/html)。
由于非 HTML 响应在 web 应用中变得非常普遍,您可以看到表 2-5 还显示了三个 Django 内置的响应快捷方式来输出非 HTML 内容。JsonResponse类用于将内联响应转换成 JavaScript 对象符号(JSON)。因为这个响应将有用负载转换为 JSON 数据布局,以是它会自动将内容类型设置为application/json。StreamingHttpResponse类的设计目的是在不需要将整个有用负载放在内存中的情况下传输响应,这种情况有助于大型有用负载响应。FileResponse类是StreamingHttpResponse的子类,旨在传输二进制数据(如 PDF 或图像文件)。
这将我们带到表 2-5 中的最后一个条目,即HttpResponse类。究竟证明,表 2-5 中的所有快捷方式都是HttpResponse类的定制子类,我最初在清单 2-22 中将其描述为创建视图响应的最灵活的技能之一。
HttpResponse方法有助于为没有直接快捷方法的 HTTP 状态代码创建响应(例如,HTTP408[请求超时],HTTP429[太多请求]),或者包含性地利用模板来生成内联响应,如清单 2-26 所示。
  1. from django.http import HttpResponse
  2. from django.utils import timezone
  3. from django.template import loader, Context
  4. response = HttpResponse(content_type='text/csv')
  5. response['Content-Disposition'] = 'attachment; filename=Users_%s.csv' % str(timezone.now().today())
  6. t = loader.get_template('dashboard/users_csvexport.html')
  7. c = Context({'users': sorted_users,})
  8. response.write(t.render(c))
  9. return response
  10. Listing 2-26.HttpResponse with template and custom CSV file download
复制代码
清单 2-26 中的HTTPResponse对象是用text/csv内容类型生成的,用来通知请求方(例如浏览器)它即将接收 CSV 内容。接下来,Content-Disposition报头还告诉请求方(例如浏览器)尝试下载名为Users_%s.csv的内容,此中用当前服务器日期替换了%s。
接下来,利用loader模块,我们利用get_template方法加载模板users_csvexport.html,该模板将具有雷同 CSV 的布局,带有数据占位符。然后我们创建一个Context对象来生存将添补模板的数据,在本例中,它只是一个名为 u sers的变量。接下来,我们用context对象调用模板的render方法,以便用数据添补模板的数据占位符。最后,通过write方法将渲染后的模板写入response对象,并返回response对象。
HttpResponse类在属性和方法之间提供了 20 多个选项, 5 以及content_type和status参数。
视图方法中间件

在大多数情况下,在视图方法中,请求和响应中的数据是以逐段的方式添加、删除或更新的。然而,有时将这些更改应用于所有请求和响应会很方便。
例如,假如您想在所有视图方法上访问某些数据,利用中间件类使这些数据可以跨所有请求访问会更容易。就像您想对所有响应进行安全查抄一样,利用中间件类更容易在全局范围内完成。
因为中间件是一个相当抽象的概念,以是在我描述 Django 中间件类的布局之前,我将带您浏览各种内置的 Django 中间件类,如许您就可以更好地理解中间件在那里是好的设计选择。
内置中间件类

Django 配备了一系列中间件类,此中一些在所有 Django 项目中默认启用。假如你打开 Django 项目的settings.py文件,你会注意到MIDDLEWARE变量,其默认内容如清单 2-27 所示。
  1. MIDDLEWARE = [
  2.     'django.middleware.security.SecurityMiddleware',
  3.     'django.contrib.sessions.middleware.SessionMiddleware',
  4.     'django.middleware.common.CommonMiddleware',
  5.     'django.middleware.csrf.CsrfViewMiddleware',
  6.     'django.contrib.auth.middleware.AuthenticationMiddleware',
  7.     'django.contrib.messages.middleware.MessageMiddleware',
  8.     'django.middleware.clickjacking.XFrameOptionsMiddleware',
  9. ]
  10. Listing 2-27.Default Django middleware classes in MIDDLEWARE
复制代码
正如您在清单 2-27 中看到的,Django 项目在开箱即用状态下启用了七个中间件类,因此所有请求和响应都被设置为通过这七个类运行。假如您计划利用 Django 的主要特性,我建议您不要删除这些默认的中间件类。但是,假如您愿意,可以将MIDDLEWARE变量留空;请注意如许做可能会破坏 Django 的某些功能。
为了让您更好地理解清单 2-27 中的 Django 中间件类的功能,并帮助您做出是否禁用它们的更明智的决定,表 2-6 描述了每个中间件类的功能。
表 2-6。
Django default middleware classes and functionality
| 中间件类 | 功能 | | --- | --- | | django . middleware . security . security 中间件 | 提供安全性增强,例如:基于 SECURE_SSL_REDIRECT 和 SECURE_SSL_HOST 设置的 SSL 重定向。通过各种设置实现严格的运输安全。 | | django . contrib . sessions . middleware . session middleware | 启用会话支持。 | | django . middleware . common . common 中间件 | 提供一组通用的功能,例如:禁止访问 DISALLOWED_USER_AGENTS 设置中的用户代理,该设置可以是已编译的正则表达式对象的列表。根据 APPEND_SLASH 和 PREPEND_WWW 设置执行 url 重写,以便规范化 URL。为非流式响应设置 HTTP Content-Length 标头。 | | django . middleware . csrf . csrfview middleware | 通过在发布表单中添加隐藏的表单域并查抄请求的正确值,来防止跨站点请求伪造。 | | django . contraib . auth . middleware . authenticationmiddleware | 将表示当前登任命户的用户属性添加到每个传入的 HttpRequest 对象中。注意:这个中间件类依靠于中间件 django . contrib . sessions . middleware . session middleware 的功能,必须出如今它的后面。 | | django . IB . messages . middleware . message middleware .讯息中介软体 | 启用基于 cookie 和基于会话的消息支持。注意:这个中间件类依靠于中间件 django . contrib . sessions . middleware . session middleware 的功能,必须出如今它的后面。 | | django . middleware . click packing . xframoptions middleware . django .中介软体 | 通过 X-Frame-Options 标题提供点击劫持保护。关于什么是点击劫持的更多细节请参见: [`http://en.wikipedia.org/wiki/Clickjacking`](http://en.wikipedia.org/wiki/Clickjacking) 。 | 正如您在表 2-6 中所看到的,尽管各种默认中间件类的目的差别很大,但它们的功能适用于需要在项目中所有请求或响应中应用的特性。
表 2-6 中中间件类的另一个紧张因素是一些依靠于另一些。例如,AuthenticationMiddleware类的设计是基于如许的假设,即它可以访问由SessionMiddleware类提供的功能。这种依靠性很紧张,因为它使得中间件类定义次序干系(例如,在MIDDLEWARE中,某些中间件类需要在其他类之前定义),这个主题我将在下一节中详细阐述。
除了表 2-6 中给出的默认中间件类,Django 还提供了其他中间件类。表 2-7 展示了你可以在你的项目中利用的 Django 中间件类的剩余集合,这对你没有须要从头开始编写中间件类很有帮助。
表 2-7。
Other Django middleware classes and functionality
| 中间件类 | 功能 | | --- | --- | | django . middleware . cache .updatecache 中间件 | 响应阶段缓存中间件,假如响应可缓存,则更新缓存。注意:UpdateCacheMiddleware 必须是中间件中的第一部分,以便在响应阶段最后调用它。 | | django . middleware . cache . fetchfromcachemiddleware | 从缓存中获取页面的请求阶段缓存中间件。注意:FetchFromCacheMiddleware 必须是中间件中的最后一部分,如许它将在请求阶段最后被调用。 | | django . middleware . common . broken link mail middleware | 向司剃头送断开链接通知电子邮件。 | | django,中间件,exceptionmiddleware | Django 利用这个中间件,不管您是否将它包含在中间件中;但是,假如您自己的中间件需要将它处理惩罚的异常转换成恰当的响应,您可能盼望创建子类。 | | django . middleware . gzip . gzip middleware | 为理解 GZip 压缩的浏览器压缩内容。注意:GZipMiddleware 应该放在任何其他需要读取或写入响应体的中间件之前,以便压缩发生在之后。只有当请求方在 HTTP Accept-Encoding 头上发送 gzip,而且内容大于 200 个字节,而且响应没有设置 HTTP Content-Encoding 头时,这个中间件才会进行压缩。 | | django.middleware.http | 处理惩罚条件 GET 操作。假如响应没有 HTTP ETag 头,则会添加一个。假如响应有一个 ETag 或 Last-Modified 头,而请求有 If-None-Match 或 If-Modified-Since,则响应被替换为 HttpNotModified。 | | Django,中间件,当地,当地 | 解析请求并决定在当前线程上下文中安装什么翻译对象。这答应页面被动态地翻译成用户想要的语言。 | | django . contrib . sites . middleware . currentsitemiddleware | 将表示当前站点的站点属性添加到每个传入的 HttpRequest 对象中。 | | django . contraib . auth . middleware . persistent treomeusermiddleware | 添加 REMOTE_USER -在请求中可用。META -通过外部来源(如 web 服务器)进行 Django 认证。 | | django . contraib . auth . middleware . remote user middleware | 答应 web 服务器提供的身份验证。假如 request.user 没有通过身份验证,这个中间件会尝试对 REMOTE_USER 请求头中传递的用户名进行身份验证。假如身份验证成功,用户将自动登录,以便将用户保留在会话中。 | | django . contraib . flatpages . middleware . flatpagefallback middleware . django . flatpages 回退中间件 | 每当 Django 应用引发 404 错误时,这个中间件都会查抄 flatpages 数据库中所请求的 url,这是最后的手段。 | | django . contrib . redirects . middleware . redirectfallback middleware | 每次 Django 应用引发 404 错误时,这个中间件都会查抄重定向数据库中请求的 url,这是最后一招。 | 如今您已经了解了 Django 的内置中间件类以及它们的用途,让我们来看看中间件类的布局及其执行过程。
中间件布局和执行过程

Django 中间件类有两个必须的方法和三个可选的方法,它们在视图请求/响应生命周期的不同点执行。清单 2-28 展示了一个示例中间件类及其各个部分。
  1. class CoffeehouseMiddleware(object):
  2.     def __init__(self, get_response):
  3.         self.get_response = get_response
  4.         # One-time configuration and initialization on start-up
  5.     def __call__(self, request):
  6.         # Logic executed on a request before the view (and other middleware) is called.
  7.         # get_response call triggers next phase
  8.         response = self.get_response(request)
  9.         # Logic executed on response after the view is called.
  10.         # Return response to finish middleware sequence
  11.         return response
  12.     def process_view(self, request, view_func, view_args, view_kwargs):
  13.         # Logic executed before a call to view
  14.         # Gives access to the view itself & arguments
  15.     def process_exception(self,request, exception):
  16.         # Logic executed if an exception/error occurs in the view
  17.     def process_template_response(self,request, response):
  18.         # Logic executed after the view is called,
  19.         # ONLY IF view response is TemplateResponse, see listing 2-22
  20. Listing 2-28.Django middleware class structure
复制代码
为了让视图方法执行清单 2-28 中的 Django 中间件类,必须将中间件类添加到settings.py中的MIDDLEWARE变量中。例如,假如清单 2-28 中的CoffeehouseMiddleware类存储在coffeehouse/utils/项目文件夹下名为middleware.py的文件/模块中,您可以将coffeehouse.utils.middleware.CoffeeMiddleware语句添加到settings.py中的MIDDLEWARE值列表中。
接下来,我将描述清单 2-28 中显示的所有 Django 中间件类中所需的两个方法:


  • __init__。-在所有 Python 类中用于引导对象实例。Django 中间件类中的__init__方法只在支持 Django 应用的 web 服务器启动时被调用一次。Django 中间件中的__init__方法必须声明一个get_response输入,它表示对先前中间件类响应的引用。get_response输入被分配给一个实例变量——也称为get_response——稍后用于中间件类的主处理惩罚逻辑。当我扩展 Django 中间件的执行过程时,get_response引用的目的将会变得更加清晰。
  • __call__。-在所有 Python 类中利用,将对象实例作为函数调用。每个应用请求都会调用 Django 中间件类中的__call__方法。正如您在清单 2-28 中看到的,__call__方法声明了一个request输入,它表示视图方法利用的同一个HttpRequest对象。__call__方法分为三个阶段:

    • 在视图方法调用之前。-一旦触发了__call__方法,您就有时机在将request引用传递给视图方法之前对其进行修改。假如你想在request中添加或修改一些东西,在它被移交给一个视图方法之前,这就是要做的阶段。
    • 触发视图方法调用。-在您修改(或不修改)原始的request之后,您必须将控制权移交给 view 方法,以便它可以或许运行。当您将request传递给在__init__方法中设置的self.get_response参考时,该阶段被触发。这个阶段实际上是在说,“我已经完成了对request的修改,继续把它交给视图方法,如许它就可以运行了。”
    • Post 视图方法调用。-一旦查看方法完成,结果将被分配给__call__中的response引用。在这个阶段,您有时机在视图方法完成后执行逻辑。您可以通过简朴地从视图方法返回response引用(即return response)来退出这个阶段。

这是这两个必须方法执行的每个 Django 中间件类背后的焦点逻辑。如今让我们看看清单 2-28 中给出的三个可选中间件类方法:


  • process_view。-所需的中间件方法——__init__和__call__——缺乏任何关于他们正在利用的视图方法的知识。在视图方法被触发之前,process_view方法答应您访问视图方法及其参数。假如存在,process_view中间件方法在__call__之后和调用self.get_response(request)之前被调用,这触发了视图方法。
  • process_exception。-假如视图方法的逻辑中出现错误,就会调用process_exception中间件方法,让您有时机执行错误后清理逻辑。
  • process_template_response。-在调用 self.get_response(request)而且视图方法完成后,可能需要更改响应本身以对其执行附加逻辑(例如,修改上下文或模板)。假如存在的话,process_template_response中间件方法会在视图方法完成后被调用,让您有时机修改响应。
Warning
只有当视图方法返回 TemplateResponse 时,才会触发 process_template_response 中间件方法。假如视图方法利用 render()生成响应,则不会触发 process_template_response。更多细节请参见查看方法响应的清单 2-22 。
总之,单个中间件类的执行过程如下:

  • __init__方法被触发(在服务器启动时)。
  • __call__触发的方法(针对每个请求)。
  • 假如声明,process_view()方法被触发。
  • 查看方法从__call__中的self.get_response(request)语句开始。
  • 假如声明,process_exception()方法在视图中发生异常时触发。
  • 查看方法完成。
  • 假如声明,当视图返回TemplateResponse时process_template_response()被触发。
尽管理解单个中间件类的执行过程很紧张,但更紧张的方面是理解多个中间件类的执行过程。正如我在本节开始时提到的,Django 项目支持清单 2-27 中所示的七个中间件类,因此多个中间件类的执行更像是常态而不是例外。
Django 中间件类是背靠背执行的,但是 view 方法代表了它们执行次序中的一个迁移变化点。清单 2-27 中默认中间件类的执行次序如下:
  1. Server start-up
  2. __init__ on django.middleware.security.SecurityMiddleware called
  3. __init__ on django.contrib.sessions.middleware.SessionMiddleware called
  4. __init__ on django.middleware.common.CommonMiddleware called
  5. __init__ on django.middleware.csrf.CsrfViewMiddleware called
  6. __init__ on django.contrib.auth.middleware.AuthenticationMiddleware called
  7. __init__ on django.contrib.messages.middleware.MessageMiddleware called
  8. __init__ on django.middleware.clickjacking.XframeOptionsMiddleware called
  9. request for index() view method
  10. __call__ on django.middleware.security.SecurityMiddleware called
  11. process_view on django.middleware.security.SecurityMiddleware called (if declared)
  12. __call__ on django.contrib.sessions.middleware.SessionMiddleware called
  13. process_view on django.contrib.sessions.middleware.SessionMiddleware called (if declared)
  14. __call__ on django.middleware.common.CommonMiddleware called
  15. process_view on django.middleware.common.CommonMiddleware called (if declared)
  16. __call__ on django.middleware.csrf.CsrfViewMiddleware called
  17. process_view on django.middleware.csrf.CsrfViewMiddleware called (if declared)
  18. __call__ on django.contrib.auth.middleware.AuthenticationMiddleware called
  19. process_view on django.contrib.auth.middleware.AuthenticationMiddleware called (if declared)
  20. __call__ on django.contrib.messages.middleware.MessageMiddleware called
  21. process_view on django.contrib.messages.middleware.MessageMiddleware called (if declared)
  22. __call__ on django.middleware.clickjacking.XframeOptionsMiddleware called
  23. process_view on django.middleware.clickjacking.XframeOptionsMiddleware called (if declared)
  24. start index() view method logic
  25. if an exception occurs in index() view
  26. process_exception on django.middleware.clickjacking.XframeOptionsMiddleware called (if declared)
  27. process_exception on django.contrib.messages.middleware.MessageMiddleware called (if declared)
  28. process_exception on django.contrib.auth.middleware.AuthenticationMiddleware called(if declared)
  29. process_exception on django.middleware.csrf.CsrfViewMiddleware called (if declared)
  30. process_exception on django.middleware.common.CommonMiddleware called (if declared)
  31. process_exception on django.contrib.sessions.middleware.SessionMiddleware called (if declared)
  32. process_exception on django.middleware.security.SecurityMiddleware called (if declared)
  33. if index() view returns TemplateResponse
  34. process_template_response on django.middleware.clickjacking.XframeOptionsMiddleware called (if declared)
  35. process_template_response on django.contrib.messages.middleware.MessageMiddleware called (if declared)
  36. process_template_response on django.contrib.auth.middleware.AuthenticationMiddleware called(if declared)
  37. process_template_response on django.middleware.csrf.CsrfViewMiddleware called (if declared)
  38. process_template_response on django.middleware.common.CommonMiddleware called (if declared)
  39. process_template_response on django.contrib.sessions.middleware.SessionMiddleware called (if declared)
  40. process_template_response on django.middleware.security.SecurityMiddleware called (if declared)
复制代码
请注意,在进入视图方法的执行之前,中间件类的执行次序遵循声明的次序(即,先声明的先运行,最后声明的最后运行)。但是,一旦执行了视图方法,中间件的执行次序就会颠倒(即,最后声明的先运行,首先声明的最后运行)。
这种行为雷同于拔塞钻,要到达中央(视图方法),您需要向一个方向移动(1 到 7),要向外移动,您需要向相反的方向移动(7 到 1)。因此中间件方法process_exception和process_template_response以__init__、__call__和process_view的相反次序执行。
清单 2-27 中默认 Django 中间件类的执行过程如图 2-3 所示。

图 2-3。
Django middleware execution process
查看方法中的中间件 Flash 消息

当用户执行一个操作(例如,提交一个表单)时,通常会利用快速消息,而且有须要告诉他们该操作是成功的还是有某种错误。其他时间,快速消息用作网页上的一次性通知,告诉用户某些事件(例如,网站维护或特别折扣)。图 2-4 显示了一组示例简讯。

图 2-4。
Web page flash messages Django Flash Messages Require a Django App, Middleware, and a Template Context Processor
默认情况下,所有 Django 项目都支持 flash 消息。但是,假如您调整了项目的 settings.py 文件,您可能会无意中禁用了 flash 消息。
为了使 Django flash 消息工作,您必须确保在 settings.py 中设置以下值:变量 INSTALLED_APPS 具有 django.contrib.messages 值,变量 MIDDLEWARE 具有 Django . contrib . messages . messages . message MIDDLEWARE 值,而且模板变量的选项中的 context_processors 列表具有 Django . contrib . messages . context _ processors . messages 值。
正如您在图 2-4 中看到的,可以有不同类型的 flash 消息,在技能上称为级别。Django 遵循标准的 Syslog 标准严重性级别,并支持表 2-8 中描述的五个内置消息级别。
表 2-8。
Django built-in flash messages
| 水平常数 | 标签 | 代价 | 目的 | | --- | --- | --- | --- | | 调试 | 调试 | Ten | 在生产摆设中将被忽略(或删除)的开发干系消息。 | | 信息 | 信息 | Twenty | 用户的信息性消息。 | | 成功ˌ成就 | 成功 | Twenty-five | 操作成功,例如,“联系信息已成功发送” | | 警告 | 警告 | Thirty | 故障没有发生,但可能即将发生。 | | 错误 | 错误 | Forty | 操作不成功或发生了其他故障。 | 添加即时消息

Django flash 消息是基于每个请求进行管理的,并被添加到视图方法中,因为这是确定 flash 消息是否被授权的最佳位置。要添加消息,您可以利用django.contrib.messages包。
用django.contrib.messages包添加 flash 消息有两种技能:一种是通用的add_message()方法,另一种是表 2-8 中描述的不同级别的快捷方式方法。清单 2-29 展示了不同的技能。
  1. from django.contrib import messages
  2. # Generic add_message method
  3. messages.add_message(request, messages.DEBUG, 'The following SQL statements were executed: %s' % sqlqueries) # Debug messages ignored by default
  4. messages.add_message(request, messages.INFO, 'All items on this page have free shipping.')
  5. messages.add_message(request, messages.SUCCESS, 'Email sent successfully.')
  6. messages.add_message(request, messages.WARNING, 'You will need to change your password in one week.')
  7. messages.add_message(request, messages.ERROR, 'We could not process your request at this time.')
  8. # Shortcut level methods
  9. messages.debug(request, 'The following SQL statements were executed: %s' % sqlqueries) # Debug messages ignored by default
  10. messages.info(request, 'All items on this page have free shipping.')
  11. messages.success(request, 'Email sent successfully.')
  12. messages.warning(request, 'You will need to change your password in one week.')
  13. messages.error(request, 'We could not process your request at this time.')
  14. Listing 2-29.Techniques to add Django flash messages
复制代码
清单 2-29 中的第一组样本利用add_message()方法,而第二组样本利用快捷方式级别方法。清单 2-29 中的两组样本产生相同的结果。
假如你仔细观察清单 2-29 ,你会注意到两个DEBUG级消息都有行尾注释# Ignored by default。Django 消息框架默认处理惩罚所有高于INFO级别的消息,这意味着DEBUG消息——如表 2-8 所述,是一个较低级别的消息阈值——被忽略,纵然它们可能被定义。
您可以更改默认的 Django 消息级别阈值,以包含所有消息级别,或者包含性地低落默认的INFO阈值。默认的消息级别阈值可以用两种方法之一来改变:用清单 2-30 中所示的MESSAGE_LEVEL变量在settings.py中全局地(即对于整个项目)改变,或者用清单 2-31 中所示的django.contrib.messages包的set_level方法在每个请求的基础上改变。
  1. # Reduce threshold to DEBUG level in settings.py
  2. from django.contrib.messages import constants as message_constants
  3. MESSAGE_LEVEL = message_constants.DEBUG
  4. # Increase threshold to WARNING level in setting.py
  5. from django.contrib.messages import constants as message_constants
  6. MESSAGE_LEVEL = message_constants.WARNING
  7. Listing 2-30.Set default Django message level globally in settings.py
复制代码
  1. # Reduce threshold to DEBUG level per request
  2. from django.contrib import messages
  3. messages.set_level(request, messages.DEBUG)
  4. # Increase threshold to WARNING level per request
  5. from django.contrib import messages
  6. messages.set_level(request, messages.WARNING)
  7. Listing 2-31.Set default Django message level on a per request basis
复制代码
清单 2-30 中的第一个MESSAGE_LEVEL定义将默认消息级别更改为DEBUG,这意味着所有消息级别定义都会得到处理惩罚,因为DEBUG是最低的阈值。清单 2-30 中的第二个MESSAGE_LEVEL定义将默认消息级别更改为WARNING,这意味着处理惩罚高于WARNING(含)的消息级别(即WARNING和ERROR)。
清单 2-31 中的第一个set_level定义将默认的请求消息级别更改为DEBUG,这意味着所有的消息级别定义都会得到处理惩罚,因为DEBUG是最低的阈值。清单 2-31 中的第二个set_level定义将默认消息级别更改为WARNING,这意味着处理惩罚高于WARNING(含)的消息级别(即WARNING和ERROR)。
假如您同时定义了两个默认的消息级别机制,默认的请求消息级别优先于默认的全局消息级别定义(例如,假如您定义了messages.set_level(request, messages.WARNING),则处理惩罚高于WARNING(包含)的消息级别,纵然全局MESSAGE_LEVEL变量被设置为MESSAGE_LEVEL = message_constants.DEBUG以包含所有消息。
除了设置 flash 消息和了解忽略来自某个级别的消息的内置阈值机制之外,您还必须熟悉到清单 2-29 中的消息定义假设 Django 消息框架的先决条件在settings.py中声明——如本节开始的侧栏中所述。
因为您最终可能会将 Django 项目发布给第三方,而且无法控制最终的摆设settings.py文件,以是 Django messages 框架提供了在须要的先决条件没有在settings.py.中声明的情况下静默忽略消息定义的本领。为了在没有声明先决条件的情况下静默忽略消息定义,您可以向任何一种添加消息的技能添加fail_silently=True属性,如清单 2-32 所示。
  1. from django.contrib import messages
  2. # Generic add_message method, with fail_silently=True
  3. messages.add_message(request, messages.INFO, 'All items on this page have free shipping.',fail_silently=True)
  4. # Shortcut level method, with fail_silently=True
  5. messages.info(request, 'All items on this page have free shipping.',fail_silently=True)
  6. Listing 2-32.Use of the fail_silently=True attribute to ignore errors in case Django messages framework not installed
复制代码
如今您已经知道了怎样添加消息以及添加消息时需要记住的紧张方面,让我们来看看怎样访问消息。
访问即时消息

您最常访问 Django flash 消息的地方是显示给最终用户的 Django 模板。作为一种快捷方式,感谢上下文处理惩罚器django.contrib.messages.context_processors.messages Django flash 消息通过messages变量在所有模板上可用。但是在我们开始实际的模板示例之前,让我们快速看一下 Django flash 消息的布局。
当您利用上一节描述的技能之一添加 Django flash 消息时,Django 会创建一个storage.base.Message类的实例。表 2-9 描述了storage.base.Message级的布局。
表 2-9。
Django storage.base.Message structure
| 属性 | 描述 | 例子 | | --- | --- | --- | | 消息 | 消息的实际文本。 | 此页面上的所有项目都有免费送货。 | | 水平 | 描述消息类型的整数(见表 2-8 中的值列)。 | Twenty | | 标签 | 由空格分隔的所有消息标记(extra_tags 和 level_tag)组合而成的字符串。 | 信息 | | 额外标签 | 包含此消息的自定义标记的字符串,用空格分隔。 | 默认情况下为空。 | | 级别标签 | 级别的字符串表示情势。 | 信息 | 正如您在表 2-9 中看到的,您可以利用几个属性在 Django 模板中显示。清单 2-33 显示了样板模板代码,您可以用它来显示请求中设置的所有 flash 消息。
  1. {% if messages %}
  2. <ul class="messages">
  3.     {% for msg in messages %}
  4.     <li>
  5.         <div class="alert alert-{{msg.level_tag}}" role="alert">
  6.         {{msg.message}}
  7.         </div>
  8.     </li>
  9.     {% endfor %}
  10. </ul>
  11. {% endif %}
  12. Listing 2-33.
  13. Boilerplate code
  14. to use in Django template to display Django flash messages
复制代码
清单 2-33 从查抄messages变量是否存在开始——它包含所有的 flash 消息——假如存在,那么一个 HTML 列表从<ul>开始。接下来,对messages中的所有元素进行循环,此中每个元素对应于一个storage.base.Message实例。对于这些元素中的每一个,都创建了一个列表和部分标签——<li>和<div>——将level_tag属性作为 CSS 类输出,将消息属性作为<div>内容输出。
您可以根据需要修改清单 2-33 中的样板代码,例如,添加条件并输出特定的消息级别,或者利用其他storage.base.Message属性,等等。
Note
清单 2-33 中的 HTML 代码利用 CSS class = " alert alert-{ { msg . level_tag } } ",该代码根据 level _ tag 属性出现为 class="alert alert-info "或 class="alert alert-success "。这些 CSS 类是 CSS 引导框架的一部分。通过这种方式,你可以快速格式化 flash 消息,看起来如图 2-2 所示。
虽然您通常会在 Django 模板中访问 Django flash 消息,但这并不意味着您不能在其他地方访问它们,比如视图方法。您还可以通过django.contrib.messages包的get_messages()方法访问请求中的 Django flash 消息。清单 2-34 展示了利用get_messages()方法的代码片段。
  1. from django.contrib import messages
  2. the_req_messages = messages.get_messages(request)
  3. for msg in the_req_messages:
  4.     do_something_with_the_flash_message(msg)
  5. Listing 2-34.Use of get_messages() method
  6. to access Django flash messages
复制代码
在清单 2-34 中,get_messages()方法接收request作为输入,并将结果赋给the_req_messages变量。接下来,对the_req_messages中的所有元素进行循环,此中每个元素都对应于一个storage.base.Message实例。对于这些元素中的每一个,都会调用方法do_something_with_the_flash_message来处理惩罚每个 flash 消息。
访问 Django flash 消息时需要理解的一个紧张方面是消息本身的一连时间。Django flash 消息被标记为在主 messages 实例上发生迭代时被清除,并在处理惩罚响应时被清除。
对于 Django 模板中的访问,这意味着假如你不能在 Django 模板中像清单 2-33 中那样进行迭代,而且请求中有 flash 消息,这可能会导致陈旧或幻影消息出如今其他地方,直到进行迭代并处理惩罚响应。对于 Django 视图方法中的访问(即利用get_messages()),这没有影响,因为纵然您可以对主消息实例进行迭代——因此,将消息标记为待清除——在 Django 视图方法中不会处理惩罚响应,因此消息永久不会被清除,只是被标记为待清除。
基于类的视图

在第一章和本章的开始——在清单 2-1——你看到了怎样定义一个 Django url 并使它在不需要视图方法的情况下利用 Django 模板操作。这可能是由于django.views.generic.TemplateView类,它被称为基于类的视图。
与利用 Django HttpRequest输入参数并输出 Django HttpResponse的标准 Python 方法支持的 Django 视图方法不同,基于类的视图通过成熟的 Python 类提供其功能。这反过来又答应 Django 视图按照面向对象编程(OOP)原则(例如封装、多态和继续)进行操作,从而进步可重用性并收缩实现时间。
尽管基于 Django 类的视图代表了一种更强大的创建 Django 视图的方法,但它们只是到如今为止您所利用的视图方法的一种替换方法。假如您想在 Django 请求上快速执行业务逻辑,您可以继续利用视图方法,但是对于要求更高的视图需求(例如表单处理惩罚、样板模型查询),基于类的视图可以节省您大量的时间。
内置的基于类的视图

由django.views.generic.TemplateView基于类的视图提供的功能确实节省了时间。虽然可以设置一个 url 来执行一个空视图方法,然后将控制发送给一个模板,但是TemplateView类答应这个过程在一行中完成。
除了TemplateView基于类的视图之外,Django 还提供了许多其他内置的基于类的视图,利用雷同 OOP 的原则来收缩普通 Django 视图操作的创建过程。表 2-10 展示了 Django 的内置视图类。
表 2-10。
Built-in classes for views
| 班级 | 描述 | | --- | --- | | django.views.generic.View | 所有基于类的视图的父类,提供焦点功能。 | | django . views . generic . template view | 答应 url 返回模板的内容,而不需要视图。 | | django . views . generic . redirect view | 答应 url 执行重定向,而不需要视图。 | | django . views . generic . archive index view django . views . generic . yeararchiveview django . views . generic . monthararchiveview django . views . generic . weekarchiveview django . views . generic . dayarchiveview django . views . generic . todayarchiveview django . views . generic . date detail view | 答应视图返回基于日期的对象结果,而无需显式执行 Django 模型查询。 | | django . views . generic . create view django . views . generic . detail view django . views . generic . update view django . views . generic . delete view django . views . generic . listview django . views . generic . formview | 答应视图执行创建-读取-更新-删除(CRUD)操作,而不需要显式执行 Django 模型查询。 | 在本章接下来也是最后一节,我将解释表 2-10 上半部分的类,如许你可以更好地理解 Django 基于类的视图的布局和执行过程。表 2-10 下半部分中涉及 Django 模型的基于类的视图在关于 Django 模型的单独章节中描述。
基于类的视图布局和执行

要创建基于类的视图,您需要创建一个从表 2-10 中的一个类继续而来的类。清单 2-35 显示了利用这种继续技能的基于类的视图,以及执行基于类的视图的相应 url 定义。
  1. # views.py
  2. from django.views.generic import TemplateView
  3. class AboutIndex(TemplateView):
  4.       template_name = 'index.html'
  5.       def get_context_data(self, **kwargs):
  6.          # **kwargs contains keyword context initialization values (if any)
  7.          # Call base implementation to get a context
  8.          context = super(AboutIndex, self).get_context_data(**kwargs)
  9.          # Add context data to pass to template
  10.          context['aboutdata'] = 'Custom data'
  11.          return context
  12. #urls.py
  13. from coffeehouse.about.views import AboutIndex
  14. urlpatterns = [
  15.     url(r'^about/index/',AboutIndex.as_view(),{'onsale':True}),
  16. ]
  17. Listing 2-35.Class-based view inherited from TemplateView
  18. with url definition
复制代码
我选择首先创建一个继续自TemplateView的视图,因为它很简朴,而且您已经知道了这个类的用途。清单 2-35 中的例子和本章中清单 2-1 中的第一个例子产生了几乎相同的结果。
不同之处在于,清单 2-1 直接声明了一个TemplateView类实例作为 url 的一部分(例如TemplateView.as_view(template_name='index.html')),而清单 2-35 声明了一个名为AboutIndex的TemplateView子类的实例。比力这两种方法,您可以对基于类的视图的 OOP 行为有一个初步的感觉。
清单 2-35 的第一部分声明了基于AboutIndex类的视图,它从TemplateView类继续了它的行为。注意这个类声明了template_name属性和get_context_data()方法。
AboutIndex类中的template_name值充当基于类的视图的默认模板。但是在 OOP 方式中,相同的值可以通过在实例创建时提供一个值来覆盖(例如,AboutIndex.as_view(template_name='other.html')利用other.html模板)。
AboutIndex类中的get_context_data方法答应您向类视图模板添加上下文数据。请注意,get_context_data方法的签名利用**kwargs来访问上下文初始化值(例如,在 url 或父类视图中声明的值),并根据标准 OOP Python 实践利用 Python super()方法调用父类的get_context_data方法。接下来,get_context_data方法利用aboutdata键添加额外的上下文数据,并返回修改后的context引用。
在清单 2-35 的第二部分中,您可以看到基于AboutIndex类的视图是怎样首先导入到一个urls.py文件中,然后毗连到一个 url 定义。注意基于类的视图是怎样利用as_view()方法在url定义上声明的。别的,请注意 url 定义怎样声明 url 额外选项{'onsale':True},该选项作为上下文数据传递给基于类的视图(即在get_context_data方法的**kwargs中)。
Tip
所有基于类的视图都利用 as_view()方法集成到 url 定义中。
如今您已经对 Django 基于类的视图有了基本的了解,清单 2-36 展示了另一个基于类的视图,它有不同的实现细节。
  1. # views.py
  2. from django.views.generic import View
  3. from django.http import HttpResponse
  4. from django.shortcuts import render
  5. class ContactPage(View):
  6.     mytemplate = 'contact.html'
  7.     unsupported = 'Unsupported operation'
  8.     def get(self, request):
  9.         return render(request, self.mytemplate)
  10.     def post(self, request):
  11.         return HttpResponse(self.unsupported)
  12. #urls.py
  13. from coffeehouse.contact.views import ContactPage
  14. urlpatterns = [
  15.     url(r'^contact/$',ContactPage.as_view()),
  16. ]
  17. Listing 2-36.Class-based view inherited from View with multiple HTTP handling
复制代码
清单 2-36 中的第一个区别是基于类的视图继续了通用类django.views.generic.View的行为。如表 2-10 所示,View类为所有基于类的视图提供焦点功能。以是究竟上,清单 2-35 中利用的TemplateView类是View的子类,这意味着利用TemplateView的基于类的视图可以访问利用View的基于类的视图的相同功能。
您选择一个类而不是另一个类来实现基于类的视图的原因根植于 OOP 多态性原则。例如,在 OOP 中你可以有一个饮料→咖啡→拿铁的类层次布局,此中饮料类提供了可用于饮料、咖啡和拿铁实例的通用功能;Coffee 类提供了更多适用于 Coffee 和后续实例的特定功能;Latte 类提供了仅适用于 Latte 实例的最具体的功能。
因此,假如您事先知道您需要一个基于类的视图来放弃对模板的控制,而不应用复杂的业务逻辑或定制的请求和响应处理惩罚,那么与更通用的View类相比,TemplateView类提供了最快的解决方案。扩展同样的原则,一旦你开始利用 Django 模型和视图,你会发现表 2-10 中一些更专业的基于类的视图也比创建一个从通用View类继续的基于类的视图提供更快的解决方案。如今你知道了为什么你会选择一个基于View类的视图而不是一个更专业的类,让我们来分解清单 2-36 中的功能。
注意基于类的视图ContactPage声明了两个属性:mytemplate和unsupported。这些是通用的类属性,我利用了mytemplate名称来说明与清单 2-35 和TemplateView基于类的视图中利用的template_name属性没有关系。从TemplateView派生的基于类的视图需要一个template_name值,并自动利用这个模板来生成响应。然而,从View类派生的基于类的视图并不期望特定的模板,而是期望您实现怎样生成响应,这就是清单 2-36 中的get和post方法发挥作用的地方。
get方法用于处理惩罚视图上的 HTTP GET 请求,而post方法用于视图上的 HTTP POST 请求。这提供了一种更加模块化的方法来处理惩罚不同的 HTTP 操作,而标准视图方法需要显式地查抄请求并创建条件来处理惩罚不同的 HTTP 操作。如今,不要担心 HTTP GET 和 HTTP POST 视图处理惩罚;Django 表格中对此进行了更详细的探讨,此中主题更具干系性。
接下来,注意到get和post方法都声明了一个request输入,它表示一个 Django HttpRequest实例,就像标准视图方法一样。在这两种情况下,方法都会立即返回响应,但是在生成响应之前,可以查抄请求值或执行任何业务逻辑,就像在标准视图方法中一样。
get方法利用django.shortcuts.render方法生成响应,而post方法利用HttpResponse类生成响应,这两种方法都是在标准视图方法中用于生成响应的相同技能。清单 2-36 中唯一微小的区别是render方法和HttpResponse类都利用实例属性(例如self.mytemplate、self.unsupported)来生成响应,但除此之外,您可以自由地返回一个 Django HttpResponse,此中包含本章已经解释过的任何变体(例如,清单 2-22 响应替换,表 2-5 快捷响应)。
最后,清单 2-36 中的最后一部分展示了怎样将ContactPage基于类的视图导入到urls.py文件中,然后利用as_view()方法毗连到一个 url。
为告终束对基于类的视图和本章的讨论,我们来看一下django.views.generic.RedirectView类。雷同于TemplateView基于类的视图答应您快速生成响应而不需要视图方法,而RedirectView基于类的视图答应您快速生成 HTTP 重定向——就像表 2-4 中描述的那样——不需要视图方法。
RedirectView类支持以下列表中描述的四个属性:


  • permanent。-默以为False执行表 2-4 中描述的HttpResponseRedirect类支持的非永久重定向。假如设置为True,则利用表 2-4 中描述的HttpResponsePermanentRedirect类进行永久重定向。
  • url。-默以为None。定义执行重定向的 url 值。
  • pattern_name。-默以为None。定义一个 url 名称,通过reverse方法生成一个重定向 url。注意reverse方法在本章前面的 url 定名和名称空间部分已经解释过了。
  • query_string。-默以为False将查询字符串附加到重定向 url。假如提供,重定向 url 的query_string值。
至此,我们结束了对 Django 视图和 URL 的探索。在接下来的两章中,您将了解 Django 模板和 Jinja 模板。
Footnotes 1
http://www.apress.com/la/book/9781590594414
2
https://docs.python.org/3/howto/regex.html#non-capturing-and-named-groups
3
https://docs.djangoproject.com/en/1.11/_modules/django/http/request/#HttpRequest
4
https://docs.djangoproject.com/en/1.11/_modules/django/http/request/#QueryDict
5
https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpResponse

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

道家人

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表