提交 5424af6f 编写于 作者: W wizardforcel

8.28

上级 518aeae9
此差异已折叠。
<!--
来源:http://django-chinese-docs.readthedocs.org/
来源:http://django-chinese-docs.readthedocs.org/
-->
# 快速安装指南 #
# 快速安装指南 #
来源:[django-chinese-docs.readthedocs.org](http://django-chinese-docs.readthedocs.org/)
来源:[django-chinese-docs.readthedocs.org](http://django-chinese-docs.readthedocs.org/)
在你开始使用 Django 之前,你需要先安装它。我们有一个 *完整安装指南* 它涵盖了所有的安装步骤和可能遇到的问题;本指南将会给你一个最简单、简洁的安装指引。
在你开始使用 Django 之前,你需要先安装它。我们有一个 *完整安装指南* 它涵盖了所有的安装步骤和可能遇到的问题;本指南将会给你一个最简单、简洁的安装指引。
## 安装 Python ##
## 安装 Python ##
作为一个 Web 框架,Django 需要使用 Python 。它适用 2.6.5 到 2.7 的所有 Python 版本。它还具有 3.2 和 3.3 版本的实验性支持。所有这些 Python 版本都包含一个轻量级的数据库名叫 SQLite 。因此你现在还不需要建立一个数据库。
作为一个 Web 框架,Django 需要使用 Python 。它适用 2.6.5 到 2.7 的所有 Python 版本。它还具有 3.2 和 3.3 版本的实验性支持。所有这些 Python 版本都包含一个轻量级的数据库名叫 SQLite 。因此你现在还不需要建立一个数据库。
在 http://www.python.org 获取 Python 。如果你使用 Linux 或者 Mac OS X,那很可能已经安装了 Python 。
在 http://www.python.org 获取 Python 。如果你使用 Linux 或者 Mac OS X,那很可能已经安装了 Python 。
> **在 Jython 使用 Django**
> **在 Jython 使用 Django**
>
> 如果你使用 *Jython* (一个在 Java 平台上实现的 Python ),你需要遵循一些额外的步骤。查看 *在 Jyton 上运行 Python* 获取详细信息。
> 如果你使用 *Jython* (一个在 Java 平台上实现的 Python ),你需要遵循一些额外的步骤。查看 *在 Jyton 上运行 Python* 获取详细信息。
>
在你的终端命令行(shell)下输入 python 来验证是否已经安装 Python ; 你将看到如下类似的提示信息:
在你的终端命令行(shell)下输入 python 来验证是否已经安装 Python ; 你将看到如下类似的提示信息:
```
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
......@@ -28,29 +28,29 @@ Type "help", "copyright", "credits" or "license" for more information.
>>>
```
## 建立一个数据库 ##
## 建立一个数据库 ##
若你需要一个“大”数据库引擎,例如:PostgreSQL ,MySQL ,或 Oracle ,那此步骤是需要的。 想要安装这样一个数据库,请参考 数据库安装信息.
若你需要一个“大”数据库引擎,例如:PostgreSQL ,MySQL ,或 Oracle ,那此步骤是需要的。 想要安装这样一个数据库,请参考 数据库安装信息.
## 删除旧版本的 Django ##
## 删除旧版本的 Django ##
如果你是从旧版本的 Django 升级安装,你将需要 *在安装新版本之前先卸载旧版本的 Django*.
如果你是从旧版本的 Django 升级安装,你将需要 *在安装新版本之前先卸载旧版本的 Django*.
## 安装 Django ##
## 安装 Django ##
你可以使用下面这简单的三个方式来安装 Django:
你可以使用下面这简单的三个方式来安装 Django:
+ 安装 *你的操作系统所提供供的发行包* 。对于操作系统提供了 Django 安装包的人来说,这是最快捷的安装方法。
+ *安装官方正式发布的版本* 。这是对于想要安装一个稳定版本而不介意运行一个稍旧版本的 Django 的人来说是最好的方式。
+ *安装最新的开发版本* 。这对于那些想要尝试最新最棒的特性而不担心运行崭新代码的用户来说是最好的。
+ 安装 *你的操作系统所提供供的发行包* 。对于操作系统提供了 Django 安装包的人来说,这是最快捷的安装方法。
+ *安装官方正式发布的版本* 。这是对于想要安装一个稳定版本而不介意运行一个稍旧版本的 Django 的人来说是最好的方式。
+ *安装最新的开发版本* 。这对于那些想要尝试最新最棒的特性而不担心运行崭新代码的用户来说是最好的。
> **总是参考你所使用的对应版本的 Django 文档!**
> **总是参考你所使用的对应版本的 Django 文档!**
>
> 如果采用了前两种方式进行安装,你需要注意在文档中标明**在开发版中新增**的标记。这个标记表明这个特性仅适用开发版的 Django ,而他们可能不在官方版本发布。
> 如果采用了前两种方式进行安装,你需要注意在文档中标明**在开发版中新增**的标记。这个标记表明这个特性仅适用开发版的 Django ,而他们可能不在官方版本发布。
## 验证安装 ##
## 验证安装 ##
为了验证 Django 被成功的安装到 Python 中,在你的终端命令行 (shell) 下输入 python 。 然后在 Python 提示符下,尝试导入 Django:
为了验证 Django 被成功的安装到 Python 中,在你的终端命令行 (shell) 下输入 python 。 然后在 Python 提示符下,尝试导入 Django:
```
>>> import django
......@@ -58,8 +58,8 @@ Type "help", "copyright", "credits" or "license" for more information.
1.5
```
你可能已安装了其他版本的 Django 。
你可能已安装了其他版本的 Django 。
## 安装完成! ##
## 安装完成! ##
安装完成 – 现在你可以 *学习入门教程*.
\ No newline at end of file
安装完成 – 现在你可以 *学习入门教程*.
\ No newline at end of file
此差异已折叠。
此差异已折叠。
<!--
来源:http://django-chinese-docs.readthedocs.org/
来源:http://django-chinese-docs.readthedocs.org/
-->
# 编写你的第一个 Django 程序 第4部分 #
# 编写你的第一个 Django 程序 第4部分 #
本教程上接 教程 第3部分 。我们将 继续开发 Web-poll 应用并且关注在处理简单的窗体和优化我们的代码。
本教程上接 教程 第3部分 。我们将 继续开发 Web-poll 应用并且关注在处理简单的窗体和优化我们的代码。
## 编写一个简单的窗体 ##
## 编写一个简单的窗体 ##
让我们把在上一篇教程中编写的 poll 的 detail 模板更新下,在模板中包含 HTML 的 <form> 组件:
让我们把在上一篇教程中编写的 poll 的 detail 模板更新下,在模板中包含 HTML 的 <form> 组件:
```
<h1>{{ poll.question }}</h1>
......@@ -24,20 +24,20 @@
<input type="submit" value="Vote" />
</form>
```
简单的总结下:
简单的总结下:
+ 上面的模板中为每个投票选项设置了一个单选按钮。每个单选按钮的 value 是投票选项对应的 ID 。每个单选按钮的 name 都是 ``“choice”``。这意味着,当有人选择了一个单选按钮并提交了表单,将会发送 的 POST 数据是 ``choice=3``。这是 HTML 表单中的基本概念。
+ 我们将 form 的 action 设置为 {% url 'polls:vote' poll.id %}``,以及设置了 ``method="post" 。使用 method="post" ( 而不是 method="get") 是非常重要的,因为这种提交表单的方式会改变服务器端的数据。 当你创建一个表单为了修改服务器端的数据时,请使用 method="post" 。这不是 Django 特定的技巧;这是优秀的 Web 开发实践。
+ forloop.counter 表示 for 标签在循环中已经循环过的次数
+ 由于我们要创建一个POST form ( 具有修改数据的功能 ),我们需要担心跨站点请求伪造 ( Cross Site Request Forgeries )。 值得庆幸的是,你不必太担心这一点,因为 Django 自带了一个非常容易使用的系统来防御它。 总之,所有的 POST form 针对内部的 URLs 时都应该使用 {% csrf_token %} 模板标签。
+ 上面的模板中为每个投票选项设置了一个单选按钮。每个单选按钮的 value 是投票选项对应的 ID 。每个单选按钮的 name 都是 ``“choice”``。这意味着,当有人选择了一个单选按钮并提交了表单,将会发送 的 POST 数据是 ``choice=3``。这是 HTML 表单中的基本概念。
+ 我们将 form 的 action 设置为 {% url 'polls:vote' poll.id %}``,以及设置了 ``method="post" 。使用 method="post" ( 而不是 method="get") 是非常重要的,因为这种提交表单的方式会改变服务器端的数据。 当你创建一个表单为了修改服务器端的数据时,请使用 method="post" 。这不是 Django 特定的技巧;这是优秀的 Web 开发实践。
+ forloop.counter 表示 for 标签在循环中已经循环过的次数
+ 由于我们要创建一个POST form ( 具有修改数据的功能 ),我们需要担心跨站点请求伪造 ( Cross Site Request Forgeries )。 值得庆幸的是,你不必太担心这一点,因为 Django 自带了一个非常容易使用的系统来防御它。 总之,所有的 POST form 针对内部的 URLs 时都应该使用 {% csrf_token %} 模板标签。
现在,让我们来创建一个 Django 视图来处理提交的数据。 记得吗?在 教程 第3部分 中,我们为 polls 应用创建了一个 URLconf 配置中包含有这一行代码:
现在,让我们来创建一个 Django 视图来处理提交的数据。 记得吗?在 教程 第3部分 中,我们为 polls 应用创建了一个 URLconf 配置中包含有这一行代码:
```
url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
```
我们还创建了一个虚拟实现的 vote() 函数。让我们创建一个真实版本吧。在 polls/views.py 中添加如下代码:
我们还创建了一个虚拟实现的 vote() 函数。让我们创建一个真实版本吧。在 polls/views.py 中添加如下代码:
```
from django.shortcuts import get_object_or_404, render
......@@ -64,29 +64,29 @@ def vote(request, poll_id):
return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
```
在这代码中有些内容还未在本教程中提到过:
在这代码中有些内容还未在本教程中提到过:
request.POST 是一个类似字典的对象,可以让你 通过关键字名称来获取提交的数据。在本例中, request.POST['choice'] 返回了所选择的投票项目的 ID ,以字符串的形式。 request.POST 的值永远是字符串形式的。
request.POST 是一个类似字典的对象,可以让你 通过关键字名称来获取提交的数据。在本例中, request.POST['choice'] 返回了所选择的投票项目的 ID ,以字符串的形式。 request.POST 的值永远是字符串形式的。
请注意 Django 也同样的提供了通过 request.GET 获取 GET 数据的方法 – 但是在代码中我们明确的使用了 request.POST 方法,以确保数据是通过 POST 方法来修改的。
请注意 Django 也同样的提供了通过 request.GET 获取 GET 数据的方法 – 但是在代码中我们明确的使用了 request.POST 方法,以确保数据是通过 POST 方法来修改的。
如果 choice 未在 POST 数据中提供 request.POST['choice'] 将抛出 KeyError 当未给定 choice 对象时上面的代码若检测到抛出的是 KeyError 异常就会向 poll 显示一条错误信息。
如果 choice 未在 POST 数据中提供 request.POST['choice'] 将抛出 KeyError 当未给定 choice 对象时上面的代码若检测到抛出的是 KeyError 异常就会向 poll 显示一条错误信息。
在增加了投票选项的统计数后,代码返回一个 HttpResponseRedirect 对象而不是常见的 HttpResponse 对象。 HttpResponseRedirect 对象需要一个参数:用户将被重定向的 URL (请继续看下去在这情况下我们是如何构造 URL ) 。
在增加了投票选项的统计数后,代码返回一个 HttpResponseRedirect 对象而不是常见的 HttpResponse 对象。 HttpResponseRedirect 对象需要一个参数:用户将被重定向的 URL (请继续看下去在这情况下我们是如何构造 URL ) 。
就像上面用 Python 作的注释那样,当成功的处理了 POST 数据后你应该总是返回一个 HttpResponseRedirect 对象。 这个技巧不是特定于 Django 的;它是优秀的 Web 开发实践。
就像上面用 Python 作的注释那样,当成功的处理了 POST 数据后你应该总是返回一个 HttpResponseRedirect 对象。 这个技巧不是特定于 Django 的;它是优秀的 Web 开发实践。
在本例中,我们在 HttpResponseRedirect 的构造方法中使用了 reverse() 函数。 此函数有助于避免在视图中硬编码 URL 的功能。它指定了我们想要的跳转的视图函数名以及视图函数中 URL 模式相应的可变参数。在本例中,我们使用了教程 第3部分中的 URLconf 配置, reverse() 将会返回类似如下所示的字符串
在本例中,我们在 HttpResponseRedirect 的构造方法中使用了 reverse() 函数。 此函数有助于避免在视图中硬编码 URL 的功能。它指定了我们想要的跳转的视图函数名以及视图函数中 URL 模式相应的可变参数。在本例中,我们使用了教程 第3部分中的 URLconf 配置, reverse() 将会返回类似如下所示的字符串
```
'/polls/3/results/'
```
... 在此 3 就是 p.id 的值。该重定向 URL 会调用 'results' 视图并显示最终页面。
... 在此 3 就是 p.id 的值。该重定向 URL 会调用 'results' 视图并显示最终页面。
正如在教程 第3部分提到的,``request`` 是一个 HttpRequest 对象。想了解 HttpRequest 对象更多的内容,请参阅 request 和 response 文档 。
正如在教程 第3部分提到的,``request`` 是一个 HttpRequest 对象。想了解 HttpRequest 对象更多的内容,请参阅 request 和 response 文档 。
当有人投票后,``vote()`` 视图会重定向到投票结果页。让我们来编写这个视图
当有人投票后,``vote()`` 视图会重定向到投票结果页。让我们来编写这个视图
```
def results(request, poll_id):
......@@ -94,9 +94,9 @@ def results(request, poll_id):
return render(request, 'polls/results.html', {'poll': poll})
```
这几乎和 教程 第3部分 中的 detail() 视图完全一样。 唯一的区别就是模板名称。 稍后我们会解决这个冗余问题。
这几乎和 教程 第3部分 中的 detail() 视图完全一样。 唯一的区别就是模板名称。 稍后我们会解决这个冗余问题。
现在,创建一个 polls/results.html 模板:
现在,创建一个 polls/results.html 模板:
```
<h1>{{ poll.question }}</h1>
......@@ -110,33 +110,33 @@ def results(request, poll_id):
<a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
```
现在,在浏览器中访问 /polls/1/ 并完成投票。每次投票后你将会看到结果页数据都有更新。 如果你没有选择投票选项就提交了,将会看到错误的信息。
现在,在浏览器中访问 /polls/1/ 并完成投票。每次投票后你将会看到结果页数据都有更新。 如果你没有选择投票选项就提交了,将会看到错误的信息。
## 使用通用视图:优化代码 ##
## 使用通用视图:优化代码 ##
detail() ( 在 教程 第3部分 中) 和 results() 视图 都很简单 – 并且还有上面所提到的冗余问题。``index()`` 用于显示 polls 列表的 index() 视图 (也在教程 第3部分中),也是存在类似的问题。
detail() ( 在 教程 第3部分 中) 和 results() 视图 都很简单 – 并且还有上面所提到的冗余问题。``index()`` 用于显示 polls 列表的 index() 视图 (也在教程 第3部分中),也是存在类似的问题。
这些视图代表了基本的 Web 开发中一种常见的问题: 根据 URL 中的参数从数据库中获取数据,加载模板并返回渲染后的内容。由于这类现象很 常见,因此 Django 提供了一种快捷方式,被称之为“通用视图”系统。
这些视图代表了基本的 Web 开发中一种常见的问题: 根据 URL 中的参数从数据库中获取数据,加载模板并返回渲染后的内容。由于这类现象很 常见,因此 Django 提供了一种快捷方式,被称之为“通用视图”系统。
通用视图抽象了常见的模式,以至于你不需要编写 Python 代码来编写一个应用。
通用视图抽象了常见的模式,以至于你不需要编写 Python 代码来编写一个应用。
让我们把 poll 应用修改成使用通用视图系统的应用,这样我们就能删除删除一些我们自己的代码了。 我们将采取以下步骤来进行修改:
让我们把 poll 应用修改成使用通用视图系统的应用,这样我们就能删除删除一些我们自己的代码了。 我们将采取以下步骤来进行修改:
+ 修改 URLconf 。
+ 删除一些旧的,不必要的视图。
+ 修正 URL 处理到对应的新视图。
+ 修改 URLconf 。
+ 删除一些旧的,不必要的视图。
+ 修正 URL 处理到对应的新视图。
请继续阅读了解详细的信息。
请继续阅读了解详细的信息。
> 为什么要重构代码?
> 为什么要重构代码?
>
> 通常情况下,当你编写一个 Django 应用时,你会评估下通用视图是否适合解决你的问题, 如果适合你就应该从一开始就使用它,而不是进行到一半才重构你的代码。 但是本教程直到现在都故意集中介绍“硬编码”视图,是为了专注于核心概念上。
> 通常情况下,当你编写一个 Django 应用时,你会评估下通用视图是否适合解决你的问题, 如果适合你就应该从一开始就使用它,而不是进行到一半才重构你的代码。 但是本教程直到现在都故意集中介绍“硬编码”视图,是为了专注于核心概念上。
>
> 就像你在使用计算器前需要知道基本的数学知识一样。
> 就像你在使用计算器前需要知道基本的数学知识一样。
## 修改 URLconf ##
## 修改 URLconf ##
首先,打开 polls/urls.py 的 URLconf 配置文件并修改成如下所示样子
首先,打开 polls/urls.py 的 URLconf 配置文件并修改成如下所示样子
```
from django.conf.urls import patterns, url
......@@ -164,23 +164,23 @@ urlpatterns = patterns('',
)
```
## 修改 views ##
## 修改 views ##
在这我们将使用两个通用视图: ListView 和 DetailView 。这两个视图分别用于显示两种抽象概念 “显示一系列对象的列表” 和 “显示一个特定类型的对象的详细信息页”。
在这我们将使用两个通用视图: ListView 和 DetailView 。这两个视图分别用于显示两种抽象概念 “显示一系列对象的列表” 和 “显示一个特定类型的对象的详细信息页”。
+ 每个视图都需要知道使用哪个模型数据。因此需要提供将要使用的 model 参数。
+ DetailView 通用视图期望从 URL 中捕获名为 "pk" 的主键值,因此我们将 poll_id 改为 pk 。
+ 每个视图都需要知道使用哪个模型数据。因此需要提供将要使用的 model 参数。
+ DetailView 通用视图期望从 URL 中捕获名为 "pk" 的主键值,因此我们将 poll_id 改为 pk 。
默认情况下, DetailView 通用视图使用名为 <应用名>/<模型名>_detail.html 的模板。在我们的例子中,将使用名为 "polls/poll_detail.html" 的模板。 template_name 参数是告诉 Django 使用指定的模板名,而不是使用自动生成的默认模板名。 我们也指定了 results 列表视图的 template_name – 这确保了 results 视图和 detail 视图渲染时会有不同的外观,虽然它们有一个 DetailView 隐藏在幕后。
默认情况下, DetailView 通用视图使用名为 <应用名>/<模型名>_detail.html 的模板。在我们的例子中,将使用名为 "polls/poll_detail.html" 的模板。 template_name 参数是告诉 Django 使用指定的模板名,而不是使用自动生成的默认模板名。 我们也指定了 results 列表视图的 template_name – 这确保了 results 视图和 detail 视图渲染时会有不同的外观,虽然它们有一个 DetailView 隐藏在幕后。
同样的,~django.views.generic.list.ListView 通用视图使用的默认模板名为 <应用名>/<模型名>_list.html ;我们指定了 template_name 参数告诉 ListView 使用已经存在的 "polls/index.html" 模板。
同样的,~django.views.generic.list.ListView 通用视图使用的默认模板名为 <应用名>/<模型名>_list.html ;我们指定了 template_name 参数告诉 ListView 使用已经存在的 "polls/index.html" 模板。
在之前的教程中,模板提供的上下文中包含了 poll 和 latest_poll_list 上下文变量。在 DetailView 中 poll 变量是自动提供的 – 因为我们使用了一个 Django 模型 (Poll) ,Django 能够为上下文变量确定适合的名称。 另外 ListView 自动生成的上下文变量名是 poll_list 。若要覆盖此变量我们需要提供 context_object_name 选项, 我们想要使用 latest_poll_list 来替代它。作为一种替代方式,你可以改变你的模板来 匹配新的默认的上下文变量 – 但它是一个非常容易地告诉 Django 使用你想要的变量的方式。
在之前的教程中,模板提供的上下文中包含了 poll 和 latest_poll_list 上下文变量。在 DetailView 中 poll 变量是自动提供的 – 因为我们使用了一个 Django 模型 (Poll) ,Django 能够为上下文变量确定适合的名称。 另外 ListView 自动生成的上下文变量名是 poll_list 。若要覆盖此变量我们需要提供 context_object_name 选项, 我们想要使用 latest_poll_list 来替代它。作为一种替代方式,你可以改变你的模板来 匹配新的默认的上下文变量 – 但它是一个非常容易地告诉 Django 使用你想要的变量的方式。
现在你可以在 polls/views.py 中删除 index() , detail() 和 results() 视图了。 我们不需要它们了 – 它们已替换为通用视图了。你也可以删除不再需要的 HttpResponse 导入包了。
现在你可以在 polls/views.py 中删除 index() , detail() 和 results() 视图了。 我们不需要它们了 – 它们已替换为通用视图了。你也可以删除不再需要的 HttpResponse 导入包了。
运行服务器,并且使用下基于通用视图的新投票应用。
运行服务器,并且使用下基于通用视图的新投票应用。
有关通用视图的完整详细信息,请参阅 通用视图文档.
有关通用视图的完整详细信息,请参阅 通用视图文档.
当你熟悉了窗体和通用视图后,请阅读 教程 第5部分 来学习测试我们的投票应用。
\ No newline at end of file
当你熟悉了窗体和通用视图后,请阅读 教程 第5部分 来学习测试我们的投票应用。
\ No newline at end of file
此差异已折叠。
<!--
来源:http://python.usyiyi.cn/django/index.html
来源:http://python.usyiyi.cn/django/index.html
-->
# 高级教程:如何编写可重用的应用 #
# 高级教程:如何编写可重用的应用 #
本高级教程上接教程 6。我们将把我们的网页投票转换成一个独立的Python包,这样你可以在其它项目中重用或者分享给其它人。
本高级教程上接教程 6。我们将把我们的网页投票转换成一个独立的Python包,这样你可以在其它项目中重用或者分享给其它人。
如果你最近没有完成教程1–6,我们建议你阅读它们使得你的示例项目与下面描述的相匹配。
如果你最近没有完成教程1–6,我们建议你阅读它们使得你的示例项目与下面描述的相匹配。
## 可重用很重要 ##
## 可重用很重要 ##
设计、构建、测试和维护一个网页应用有许多工作要做。许多Python 和 Django 项目都有常见的共同问题。如果我们可以节省一些这些重复的工作会不会很棒?
设计、构建、测试和维护一个网页应用有许多工作要做。许多Python 和 Django 项目都有常见的共同问题。如果我们可以节省一些这些重复的工作会不会很棒?
可重用性是Python 中一种生活的态度。Python包索引 (PyPI) 具有广泛的包,你可以在你自己的Python程序中使用。调查一下Django Packages中已经存在的可重用的应用,你可以结合它们到你的项目。Django 自身也只是一个Python 包。这意味着你可以获取已经存在的Python包和Django应用并将它们融合到你自己的网页项目。你只需要编写你项目的独特的部分。
可重用性是Python 中一种生活的态度。Python包索引 (PyPI) 具有广泛的包,你可以在你自己的Python程序中使用。调查一下Django Packages中已经存在的可重用的应用,你可以结合它们到你的项目。Django 自身也只是一个Python 包。这意味着你可以获取已经存在的Python包和Django应用并将它们融合到你自己的网页项目。你只需要编写你项目的独特的部分。
比如说,你正在开始一个新的项目,需要一个像我们正在编写的投票应用。你如何让该应用可重用?幸运的是,你已经在正确的道路上。在教程 3中,我们看到我们可以如何使用include将投票应用从项目级别的URLconf 解耦。在本教程中,我们将更进一步,让你的应用在新的项目中容易地使用并随时可以发布给其它人安装和使用。
比如说,你正在开始一个新的项目,需要一个像我们正在编写的投票应用。你如何让该应用可重用?幸运的是,你已经在正确的道路上。在教程 3中,我们看到我们可以如何使用include将投票应用从项目级别的URLconf 解耦。在本教程中,我们将更进一步,让你的应用在新的项目中容易地使用并随时可以发布给其它人安装和使用。
> 包?应用?
> 包?应用?
>
> Python 包 提供的方式是分组相关的Python 代码以容易地重用。一个包包含一个或多个Python代码(也叫做“模块”)。
> Python 包 提供的方式是分组相关的Python 代码以容易地重用。一个包包含一个或多个Python代码(也叫做“模块”)。
>
> 包可以通过import foo.bar 或from foo import bar 导入。如果一个目录(例如polls)想要形成一个包,它必须包含一个特殊的文件__init__.py,即使这个文件为空。
> 包可以通过import foo.bar 或from foo import bar 导入。如果一个目录(例如polls)想要形成一个包,它必须包含一个特殊的文件__init__.py,即使这个文件为空。
>
> 一个Django 应用 只是一个Python包,它特意用于Django项目中。一个应用可以使用常见的Django 约定,例如具有models、tests、urls和views 子模块。
> 一个Django 应用 只是一个Python包,它特意用于Django项目中。一个应用可以使用常见的Django 约定,例如具有models、tests、urls和views 子模块。
>
> 后面我们使用打包这个词来描述将一个Python包变得让其他人易于安装的过程。我们知道,这可能有点绕人。
> 后面我们使用打包这个词来描述将一个Python包变得让其他人易于安装的过程。我们知道,这可能有点绕人。
## 你的项目和你的可重用的应用 ##
## 你的项目和你的可重用的应用 ##
经过前面的教程之后,我们的项目应该看上去像这样:
经过前面的教程之后,我们的项目应该看上去像这样:
```
mysite/
......@@ -63,29 +63,29 @@ mysite/
base_site.html
```
你在教程 2中创建了mysite/templates ,在教程 3中创建了polls/templates。 现在你可能更加清晰为什么我们为项目和应用选择单独的模板目录:属于投票应用的部分全部在polls中。它使得该应用自包含且更加容易丢到一个新的项目中。
你在教程 2中创建了mysite/templates ,在教程 3中创建了polls/templates。 现在你可能更加清晰为什么我们为项目和应用选择单独的模板目录:属于投票应用的部分全部在polls中。它使得该应用自包含且更加容易丢到一个新的项目中。
现在可以拷贝polls目录到一个新的Django项目并立即使用。然后它还不能充分准备好到可以立即发布。由于这点,我们需要打包这个应用来让它对其他人易于安装。
现在可以拷贝polls目录到一个新的Django项目并立即使用。然后它还不能充分准备好到可以立即发布。由于这点,我们需要打包这个应用来让它对其他人易于安装。
## 安装一些前提条件 ##
## 安装一些前提条件 ##
Python 打包的目前状态因为有多种工具而混乱不堪。对于本教程,我们打算使用setuptools来构建我们的包。它是推荐的打包工具(已经与distribute 分支合并)。我们还将使用pip来安装和卸载它。现在你应该安装这两个包。如果你需要帮助,你可以参考如何使用pip安装Django。你可以使用同样的方法安装setuptools。
Python 打包的目前状态因为有多种工具而混乱不堪。对于本教程,我们打算使用setuptools来构建我们的包。它是推荐的打包工具(已经与distribute 分支合并)。我们还将使用pip来安装和卸载它。现在你应该安装这两个包。如果你需要帮助,你可以参考如何使用pip安装Django。你可以使用同样的方法安装setuptools。
## 打包你的应用 ##
## 打包你的应用 ##
Python packaging refers to preparing your app in a specific format that can be easily installed and used. Django 自己是以非常相似的方式打包起来的。对于一个像polls这样的小应用,这个过程不是太难。
Python packaging refers to preparing your app in a specific format that can be easily installed and used. Django 自己是以非常相似的方式打包起来的。对于一个像polls这样的小应用,这个过程不是太难。
首先,在你的Django项目之外,为polls创建一个父目录。称这个目录为django-polls。
首先,在你的Django项目之外,为polls创建一个父目录。称这个目录为django-polls。
> 为你的应用选择一个名字
> 为你的应用选择一个名字
>
> 让为你的包选择一个名字时,检查一下PyPI中的资源以避免与已经存在的包有名字冲突。当创建一个要发布的包时,在你的模块名字前面加上django-通常很有用。 这有助于其他正在查找Django应用的人区分你的应用是专门用于Django的。
> 让为你的包选择一个名字时,检查一下PyPI中的资源以避免与已经存在的包有名字冲突。当创建一个要发布的包时,在你的模块名字前面加上django-通常很有用。 这有助于其他正在查找Django应用的人区分你的应用是专门用于Django的。
>
> 应用的标签(应用的包的点分路径的最后部分)在INSTALLED_APPS中必须唯一。避免使用与Django的contrib 包 中任何一个使用相同的标签,例如auth、admin和messages。
> 应用的标签(应用的包的点分路径的最后部分)在INSTALLED_APPS中必须唯一。避免使用与Django的contrib 包 中任何一个使用相同的标签,例如auth、admin和messages。
将polls 目录移动到django-polls目录。
将polls 目录移动到django-polls目录。
创建一个包含一些内容的文件django-polls/README.rst:
创建一个包含一些内容的文件django-polls/README.rst:
```
django-polls/README.rst
......@@ -120,9 +120,9 @@ Quick start
5. Visit http://127.0.0.1:8000/polls/ to participate in the poll.
```
创建一个django-polls/LICENSE文件。选择License超出本教程的范围,但值得一说的是公开发布的代码如果没有License是毫无用处的。Django和许多与Django兼容的应用以BSD License 发布;然而,你可以随便挑选自己的License。只需要知道你的License的选则将影响谁能够使用你的代码。
创建一个django-polls/LICENSE文件。选择License超出本教程的范围,但值得一说的是公开发布的代码如果没有License是毫无用处的。Django和许多与Django兼容的应用以BSD License 发布;然而,你可以随便挑选自己的License。只需要知道你的License的选则将影响谁能够使用你的代码。
下一步我们将创建一个setup.py 文件,它提供如何构建和安装该应用的详细信息。该文件完整的解释超出本教程的范围,setuptools 文档 有很好的解释。创建一个文件django-polls/setup.py,其内容如下:
下一步我们将创建一个setup.py 文件,它提供如何构建和安装该应用的详细信息。该文件完整的解释超出本教程的范围,setuptools 文档 有很好的解释。创建一个文件django-polls/setup.py,其内容如下:
```
django-polls/setup.py
......@@ -163,7 +163,7 @@ setup(
)
```
默认只有Python模块和包会包含进包中。如果需要包含额外的文件,我们需要创建一个MANIFEST.in文件。上一步提到的setuptools 文档对这个文件有更详细的讨论。如果要包含模板、README.rst和我们的LICENSE 文件,创建一个文件django-polls/MANIFEST.in,其内容如下:
默认只有Python模块和包会包含进包中。如果需要包含额外的文件,我们需要创建一个MANIFEST.in文件。上一步提到的setuptools 文档对这个文件有更详细的讨论。如果要包含模板、README.rst和我们的LICENSE 文件,创建一个文件django-polls/MANIFEST.in,其内容如下:
```
django-polls/MANIFEST.in
......@@ -173,55 +173,55 @@ recursive-include polls/static *
recursive-include polls/templates *
```
将详细的文档包含进你的应用中,它是可选的,但建议你这样做。创建一个空的目录django-polls/docs用于将来存放文档。向django-polls/MANIFEST.in添加另外一行:
将详细的文档包含进你的应用中,它是可选的,但建议你这样做。创建一个空的目录django-polls/docs用于将来存放文档。向django-polls/MANIFEST.in添加另外一行:
```
recursive-include docs *
```
注意docs不会包含进你的包中除非你添加一些文件到它下面。许多Django应用还通过类似readthedocs.org这样的站点提供它们的在线文档.
注意docs不会包含进你的包中除非你添加一些文件到它下面。许多Django应用还通过类似readthedocs.org这样的站点提供它们的在线文档.
试着通过python setup.py sdist 构建你的包(从django-polls的内部运行)。这创建一个dist目录并构建一个新包django-polls-0.1.tar.gz。
试着通过python setup.py sdist 构建你的包(从django-polls的内部运行)。这创建一个dist目录并构建一个新包django-polls-0.1.tar.gz。
更多关于打包的信息,参见Python 的 打包和分发项目的教程。
更多关于打包的信息,参见Python 的 打包和分发项目的教程。
## 使用你自己的包 ##
## 使用你自己的包 ##
因为,我们将polls 目录移到项目的目录之外,它不再工作了。我们将通过安装我们的新的django-polls包来修复它。
因为,我们将polls 目录移到项目的目录之外,它不再工作了。我们将通过安装我们的新的django-polls包来修复它。
> 安装成某个用户的库
> 安装成某个用户的库
>
> 以下的步骤将安装django-polls 成某个用户的库。根据用户安装相比系统范围的安装具有许多优点,例如用于没有管理员权限的系统上以及防止你的包影响系统的服务和机器上的其它用户。
> 以下的步骤将安装django-polls 成某个用户的库。根据用户安装相比系统范围的安装具有许多优点,例如用于没有管理员权限的系统上以及防止你的包影响系统的服务和机器上的其它用户。
>
> 注意根据用户的安装仍然可以影响以该用户身份运行的系统工具,所以virtualenv 是更健壮的解决办法(见下文)。
> 注意根据用户的安装仍然可以影响以该用户身份运行的系统工具,所以virtualenv 是更健壮的解决办法(见下文)。
安装这个包,使用pip(你已经安装好它了,对吧?):
安装这个包,使用pip(你已经安装好它了,对吧?):
```
pip install --user django-polls/dist/django-polls-0.1.tar.gz
```
如果幸运,你的Django 项目现在应该可以再次正确工作。请重新运行服务器以证实这点。
如果幸运,你的Django 项目现在应该可以再次正确工作。请重新运行服务器以证实这点。
若要卸载这个包,使用pip:
若要卸载这个包,使用pip:
```
pip uninstall django-polls
```
## 发布你的应用: ##
## 发布你的应用: ##
既然我们已经打包并测试过django-polls,是时候与世界共享它了!要不是它仅仅是个例子,你现在可以:
既然我们已经打包并测试过django-polls,是时候与世界共享它了!要不是它仅仅是个例子,你现在可以:
+ 将这个包用邮件发送给朋友。
+ 上传这个包到你的网站上。
+ 上传这个包到一个公开的仓库,例如Python 包索引 (PyPI)。packaging.python.org has a good tutorial for doing this.
+ 将这个包用邮件发送给朋友。
+ 上传这个包到你的网站上。
+ 上传这个包到一个公开的仓库,例如Python 包索引 (PyPI)。packaging.python.org has a good tutorial for doing this.
## 使用 virtualenv 安装Python 包 ##
## 使用 virtualenv 安装Python 包 ##
前面,我们将poll 安装成一个用户的库。它有一些缺点:
前面,我们将poll 安装成一个用户的库。它有一些缺点:
+ 修改这个用户的库可能影响你的系统上的其它Python 软件。
+ 你将不可以运行这个包的多个版本(或者具有相同名字的其它包)。
+ 修改这个用户的库可能影响你的系统上的其它Python 软件。
+ 你将不可以运行这个包的多个版本(或者具有相同名字的其它包)。
特别是一旦你维护几个Django项目,这些情况就会出现。如果确实出现,最好的解决办法是使用virtualenv。这个工具允许你维护多个分离的Python环境,每个都具有它自己的库和包的命名空间。
\ No newline at end of file
特别是一旦你维护几个Django项目,这些情况就会出现。如果确实出现,最好的解决办法是使用virtualenv。这个工具允许你维护多个分离的Python环境,每个都具有它自己的库和包的命名空间。
\ No newline at end of file
<!--
来源:http://python.usyiyi.cn/django/index.html
来源:http://python.usyiyi.cn/django/index.html
-->
# 为Django编写首个补丁 #
# 为Django编写首个补丁 #
## 介绍 ##
## 介绍 ##
有兴趣为社区做出点贡献吗?也许你会在Django中发现你想要修复的漏洞,或者你希望为它添加一个小特征。
有兴趣为社区做出点贡献吗?也许你会在Django中发现你想要修复的漏洞,或者你希望为它添加一个小特征。
为Django作贡献这件事本身就是使你的顾虑得到解决的最好方式。一开始这可能会使你怯步,但事实上是很简单的。整个过程中我们会一步一步为你解说,所以你可以通过例子学习。
为Django作贡献这件事本身就是使你的顾虑得到解决的最好方式。一开始这可能会使你怯步,但事实上是很简单的。整个过程中我们会一步一步为你解说,所以你可以通过例子学习。
## Who’s this tutorial for? ##
## Who’s this tutorial for? ##
使用教程前,我们希望你至少对于Django的运行方式有基础的了解。这意味着你可以自如地在写你自己的Django app时使用教程。 除此之外,你应该对于Python本身有很好的了解。如果您并不太了解, 我们为您推荐Dive Into Python,对于初次使用Python的程序员来说这是一本很棒(而且免费)的在线电子书。
使用教程前,我们希望你至少对于Django的运行方式有基础的了解。这意味着你可以自如地在写你自己的Django app时使用教程。 除此之外,你应该对于Python本身有很好的了解。如果您并不太了解, 我们为您推荐Dive Into Python,对于初次使用Python的程序员来说这是一本很棒(而且免费)的在线电子书。
对于版本控制系统及Trac不熟悉的人来说,这份教程及其中的链接所包含的信息足以满足你们开始学习的需求。然而,如果你希望定期为Django贡献,你可能会希望阅读更多关于这些不同工具的信息。
对于版本控制系统及Trac不熟悉的人来说,这份教程及其中的链接所包含的信息足以满足你们开始学习的需求。然而,如果你希望定期为Django贡献,你可能会希望阅读更多关于这些不同工具的信息。
当然对于其中的大部分内容,Django会尽可能做出解释以帮助广大的读者。
当然对于其中的大部分内容,Django会尽可能做出解释以帮助广大的读者。
> 何处获得帮助:
> 何处获得帮助:
>
> 如果你在使用本教程时遇到困难,你可以发送信息给django开发者 或者登陆 #django-dev on irc.freenode.net 向其他Django使用者需求帮助。
> 如果你在使用本教程时遇到困难,你可以发送信息给django开发者 或者登陆 #django-dev on irc.freenode.net 向其他Django使用者需求帮助。
## 教程包含的内容 ##
## 教程包含的内容 ##
一开始我们会帮助你为Django编写补丁,在教程结束时,你将具备对于工具和所包含过程的基本了解。准确来说,我们的教程将包含以下几点:
一开始我们会帮助你为Django编写补丁,在教程结束时,你将具备对于工具和所包含过程的基本了解。准确来说,我们的教程将包含以下几点:
+ 安装Git。
+ 如何下载Django的开发复本
+ 运行Django的测试组件
+ 为你的补丁编写一个测试
+ 为你的补丁编码。
+ 测试你的补丁。
+ 为你所做的改变写一个补丁文件。
+ 去哪里寻找更多的信息。
+ 安装Git。
+ 如何下载Django的开发复本
+ 运行Django的测试组件
+ 为你的补丁编写一个测试
+ 为你的补丁编码。
+ 测试你的补丁。
+ 为你所做的改变写一个补丁文件。
+ 去哪里寻找更多的信息。
一旦你完成了这份教程,你可以浏览剩下的Django’s documentation on contributing. 它包含了大量信息。任何想成为Django的正式贡献者必须去阅读它。如果你有问题,它也许会给你答案
一旦你完成了这份教程,你可以浏览剩下的Django’s documentation on contributing. 它包含了大量信息。任何想成为Django的正式贡献者必须去阅读它。如果你有问题,它也许会给你答案
## 安装Git ##
## 安装Git ##
使用教程前,你需要安装好Git,下载Django的最新开发版本并且为你作出的改变生成补丁文件
使用教程前,你需要安装好Git,下载Django的最新开发版本并且为你作出的改变生成补丁文件
为了确认你是否已经安装了Git, 输入 git 进入命令行。如果信息提示命令无法找到, 你就需要下载并安装Git, 详情阅读 Git’s download page.
\ No newline at end of file
为了确认你是否已经安装了Git, 输入 git 进入命令行。如果信息提示命令无法找到, 你就需要下载并安装Git, 详情阅读 Git’s download page.
\ No newline at end of file
此差异已折叠。
<!--
译者:Github@wizardforcel
译者:Github@wizardforcel
-->
# 关联对象参考 #
# 关联对象参考 #
**class RelatedManager**
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。它存在于下面两种情况:
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。它存在于下面两种情况:
ForeignKey关系的“另一边”。像这样:
ForeignKey关系的“另一边”。像这样:
```
from django.db import models
......@@ -21,9 +21,9 @@ class Article(models.Model):
reporter = models.ForeignKey(Reporter)
```
在上面的例子中,管理器reporter.article_set拥有下面的方法。
在上面的例子中,管理器reporter.article_set拥有下面的方法。
ManyToManyField关系的两边:
ManyToManyField关系的两边:
```
class Topping(models.Model):
......@@ -34,13 +34,13 @@ class Pizza(models.Model):
toppings = models.ManyToManyField(Topping)
```
这个例子中,topping.pizza_set 和pizza.toppings都拥有下面的方法。
这个例子中,topping.pizza_set 和pizza.toppings都拥有下面的方法。
**add(obj1[, obj2, ...])**
把指定的模型对象添加到关联对象集中。
把指定的模型对象添加到关联对象集中。
例如:
例如:
```
>>> b = Blog.objects.get(id=1)
......@@ -48,11 +48,11 @@ class Pizza(models.Model):
>>> b.entry_set.add(e) # Associates Entry e with Blog b.
```
在上面的例子中,对于ForeignKey关系,e.save()由关联管理器调用,执行更新操作。然而,在多对多关系中使用add()并不会调用任何 save()方法,而是由QuerySet.bulk_create()创建关系。如果你需要在关系被创建时执行一些自定义的逻辑,请监听m2m_changed信号。
在上面的例子中,对于ForeignKey关系,e.save()由关联管理器调用,执行更新操作。然而,在多对多关系中使用add()并不会调用任何 save()方法,而是由QuerySet.bulk_create()创建关系。如果你需要在关系被创建时执行一些自定义的逻辑,请监听m2m_changed信号。
**create(\*\*kwargs)**
创建一个新的对象,保存对象,并将它添加到关联对象集之中。返回新创建的对象:
创建一个新的对象,保存对象,并将它添加到关联对象集之中。返回新创建的对象:
```
>>> b = Blog.objects.get(id=1)
......@@ -65,7 +65,7 @@ class Pizza(models.Model):
# No need to call e.save() at this point -- it's already been saved.
```
这完全等价于(不过更加简洁于):
这完全等价于(不过更加简洁于):
```
>>> b = Blog.objects.get(id=1)
......@@ -78,11 +78,11 @@ class Pizza(models.Model):
>>> e.save(force_insert=True)
```
要注意我们并不需要指定模型中用于定义关系的关键词参数。在上面的例子中,我们并没有传入blog参数给create()。Django会明白新的 Entry对象blog 应该添加到b中。
要注意我们并不需要指定模型中用于定义关系的关键词参数。在上面的例子中,我们并没有传入blog参数给create()。Django会明白新的 Entry对象blog 应该添加到b中。
**remove(obj1[, obj2, ...])**
从关联对象集中移除执行的模型对象:
从关联对象集中移除执行的模型对象:
```
>>> b = Blog.objects.get(id=1)
......@@ -90,38 +90,38 @@ class Pizza(models.Model):
>>> b.entry_set.remove(e) # Disassociates Entry e from Blog b.
```
和add()相似,上面的例子中,e.save()可会执行更新操作。但是,多对多关系上的remove(),会使用QuerySet.delete()删除关系,意思是并不会有任何模型调用save()方法:如果你想在一个关系被删除时执行自定义的代码,请监听m2m_changed信号。
和add()相似,上面的例子中,e.save()可会执行更新操作。但是,多对多关系上的remove(),会使用QuerySet.delete()删除关系,意思是并不会有任何模型调用save()方法:如果你想在一个关系被删除时执行自定义的代码,请监听m2m_changed信号。
对于ForeignKey对象,这个方法仅在null=True时存在。如果关联的字段不能设置为None (NULL),则这个对象在添加到另一个关联之前不能移除关联。在上面的例子中,从b.entry_set()移除e等价于让e.blog = None,由于blog的ForeignKey没有设置null=True,这个操作是无效的。
对于ForeignKey对象,这个方法仅在null=True时存在。如果关联的字段不能设置为None (NULL),则这个对象在添加到另一个关联之前不能移除关联。在上面的例子中,从b.entry_set()移除e等价于让e.blog = None,由于blog的ForeignKey没有设置null=True,这个操作是无效的。
对于ForeignKey对象,该方法接受一个bulk参数来控制它如果执行操作。如果为True(默认值),QuerySet.update()会被使用。而如果bulk=False,会在每个单独的模型实例上调用save()方法。这会触发pre_save和post_save,它们会消耗一定的性能。
对于ForeignKey对象,该方法接受一个bulk参数来控制它如果执行操作。如果为True(默认值),QuerySet.update()会被使用。而如果bulk=False,会在每个单独的模型实例上调用save()方法。这会触发pre_save和post_save,它们会消耗一定的性能。
**clear()**
从关联对象集中移除一切对象。
从关联对象集中移除一切对象。
```
>>> b = Blog.objects.get(id=1)
>>> b.entry_set.clear()
```
注意这样不会删除对象 —— 只会删除他们之间的关联。
注意这样不会删除对象 —— 只会删除他们之间的关联。
就像 remove() 方法一样,clear()只能在 null=True的ForeignKey上被调用,也可以接受bulk关键词参数。
就像 remove() 方法一样,clear()只能在 null=True的ForeignKey上被调用,也可以接受bulk关键词参数。
> 注意
> 注意
>
> 注意对于所有类型的关联字段,add()、create()、remove()和clear()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
> 注意对于所有类型的关联字段,add()、create()、remove()和clear()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
>
> 同样,如果你再多对多关系中使用了中间模型,一些关联管理的方法会被禁用。
> 同样,如果你再多对多关系中使用了中间模型,一些关联管理的方法会被禁用。
## 直接赋值 ##
## 直接赋值 ##
通过赋值一个新的可迭代的对象,关联对象集可以被整体替换掉。
通过赋值一个新的可迭代的对象,关联对象集可以被整体替换掉。
```
>>> new_list = [obj1, obj2, obj3]
>>> e.related_set = new_list
```
如果外键关系满足null=True,关联管理器会在添加new_list中的内容之前,首先调用clear()方法来解除关联集中一切已存在对象的关联。否则, new_list中的对象会在已存在的关联的基础上被添加。
\ No newline at end of file
如果外键关系满足null=True,关联管理器会在添加new_list中的内容之前,首先调用clear()方法来解除关联集中一切已存在对象的关联。否则, new_list中的对象会在已存在的关联的基础上被添加。
\ No newline at end of file
<!--
译者:Github@wizardforcel
译者:Github@wizardforcel
-->
# 编写数据库迁移 #
# 编写数据库迁移 #
这一节介绍你可能遇到的在不同情况下如何分析和编写数据库迁移. 有关迁移的入门资料,请查看 the topic guide.
这一节介绍你可能遇到的在不同情况下如何分析和编写数据库迁移. 有关迁移的入门资料,请查看 the topic guide.
## 数据迁移和多数据库 ##
## 数据迁移和多数据库 ##
在使用多个数据库时,需要解决是否针对某个特定数据库运行迁移。例如,你可能 只 想在某个特定数据库上运行迁移。
在使用多个数据库时,需要解决是否针对某个特定数据库运行迁移。例如,你可能 只 想在某个特定数据库上运行迁移。
为此你可以在RunPython中通过查看schema_editor.connection.alias 属性来检查数据库连接别名:
为此你可以在RunPython中通过查看schema_editor.connection.alias 属性来检查数据库连接别名:
```
from django.db import migrations
......@@ -32,10 +32,10 @@ class Migration(migrations.Migration):
```
```
Django 1.8 中新增。
Django 1.8 中新增。
```
你也可以提供一个提示作为 **hints参数传递到数据库路由的allow_migrate() 方法:
你也可以提供一个提示作为 **hints参数传递到数据库路由的allow_migrate() 方法:
```
myapp/dbrouters.py
......@@ -47,7 +47,7 @@ class MyRouter(object):
return True
```
然后,要在你的迁移中利用,执行以下操作:
然后,要在你的迁移中利用,执行以下操作:
```
from django.db import migrations
......@@ -66,19 +66,19 @@ class Migration(migrations.Migration):
]
```
如果你的RunPython或者RunSQL操作只对一个模型有影响,最佳实践是将model_name作为提示传递,使其尽可能对路由可见。这对可复用的和第三方应用极其重要。
如果你的RunPython或者RunSQL操作只对一个模型有影响,最佳实践是将model_name作为提示传递,使其尽可能对路由可见。这对可复用的和第三方应用极其重要。
## 添加唯一字段的迁移 ##
## 添加唯一字段的迁移 ##
如果你应用了一个“朴素”的迁移,向表中一个已存在的行中添加了一个唯一的非空字段,会产生错误,因为位于已存在行中的值只会生成一次。所以需要移除唯一性的约束。
如果你应用了一个“朴素”的迁移,向表中一个已存在的行中添加了一个唯一的非空字段,会产生错误,因为位于已存在行中的值只会生成一次。所以需要移除唯一性的约束。
所以,应该执行下面的步骤。在这个例子中,我们会以默认值添加一个非空的UUIDField字段。你可以根据你的需要修改各个字段。
所以,应该执行下面的步骤。在这个例子中,我们会以默认值添加一个非空的UUIDField字段。你可以根据你的需要修改各个字段。
+ 把default=...和unique=True参数添加到你模型的字段中。在这个例子中,我们默认使用uuid.uuid4。
+ 运行 makemigrations 命令。
+ 编辑创建的迁移文件。
+ 把default=...和unique=True参数添加到你模型的字段中。在这个例子中,我们默认使用uuid.uuid4。
+ 运行 makemigrations 命令。
+ 编辑创建的迁移文件。
生成的迁移类看上去像这样:
生成的迁移类看上去像这样:
```
class Migration(migrations.Migration):
......@@ -96,13 +96,13 @@ class Migration(migrations.Migration):
]
```
你需要做三处更改:
你需要做三处更改:
+ 从已生成的迁移类中复制,添加第二个AddField操作,并改为AlterField。
+ 在第一个AddField操作中,把unique=True改为 null=True,这会创建一个中间的null字段。
+ 在两个操作之间,添加一个RunPython或RunSQL操作为每个已存在的行生成一个唯一值(例如UUID)。
+ 从已生成的迁移类中复制,添加第二个AddField操作,并改为AlterField。
+ 在第一个AddField操作中,把unique=True改为 null=True,这会创建一个中间的null字段。
+ 在两个操作之间,添加一个RunPython或RunSQL操作为每个已存在的行生成一个唯一值(例如UUID)。
最终的迁移类应该看起来是这样:
最终的迁移类应该看起来是这样:
```
# -*- coding: utf-8 -*-
......@@ -139,6 +139,6 @@ class Migration(migrations.Migration):
]
```
现在你可以像平常一样使用migrate命令应用迁移。
现在你可以像平常一样使用migrate命令应用迁移。
注意如果你在这个迁移运行时让对象被创建,就会产生竞争条件(race condition)。在AddField之后, RunPython之前创建的对象会覆写他们原始的uuid。
\ No newline at end of file
注意如果你在这个迁移运行时让对象被创建,就会产生竞争条件(race condition)。在AddField之后, RunPython之前创建的对象会覆写他们原始的uuid。
\ No newline at end of file
<!--
译者:Github@wizardforcel
译者:Github@wizardforcel
-->
# 将遗留数据库整合到Django #
# 将遗留数据库整合到Django #
虽然Django最适合用来开发新的应用,但也可以将它整合到遗留的数据库中。Django包含了很多工具,尽可能自动化解决这类问题。
虽然Django最适合用来开发新的应用,但也可以将它整合到遗留的数据库中。Django包含了很多工具,尽可能自动化解决这类问题。
这篇文章假设你了解Django的基础部分,它们在教程中提及。
这篇文章假设你了解Django的基础部分,它们在教程中提及。
一旦你的Django环境建立好之后,你可以按照这个大致的流程,整合你的现有数据库。
一旦你的Django环境建立好之后,你可以按照这个大致的流程,整合你的现有数据库。
## 向Django提供你的数据库参数 ##
## 向Django提供你的数据库参数 ##
你需要告诉Django你的数据库连接参数,以及数据库的名称。请修改DATABASES设置,为'默认' 连接的以下键赋值:
你需要告诉Django你的数据库连接参数,以及数据库的名称。请修改DATABASES设置,为'默认' 连接的以下键赋值:
+ NAME
+ ENGINE
......@@ -21,25 +21,25 @@
+ HOST
+ PORT
## 自动生成模型 ##
## 自动生成模型 ##
Django自带叫做inspectdb的工具,可以按照现有的数据库创建模型。你可以运行以下命令,并查看输出:
Django自带叫做inspectdb的工具,可以按照现有的数据库创建模型。你可以运行以下命令,并查看输出:
```
$ python manage.py inspectdb
```
通过重定向Unix标准输出流来保存文件:
通过重定向Unix标准输出流来保存文件:
```
$ python manage.py inspectdb > models.py
```
这个特性是一个快捷方式,并不是一个确定的模型生成器。详见inspectdb文档 。
这个特性是一个快捷方式,并不是一个确定的模型生成器。详见inspectdb文档 。
一旦你创建好了你的模型,把文件命名为models.py,然后把它放到你应用的Python包中。然后把应用添加到你的INSTALLED_APPS 设置中。
一旦你创建好了你的模型,把文件命名为models.py,然后把它放到你应用的Python包中。然后把应用添加到你的INSTALLED_APPS 设置中。
默认情况下,inspectdb创建未被管理的模型。这就是说,模型的Meta类中的managed = False告诉Django不要管理每个表的创建、修改和删除:
默认情况下,inspectdb创建未被管理的模型。这就是说,模型的Meta类中的managed = False告诉Django不要管理每个表的创建、修改和删除:
```
class Person(models.Model):
......@@ -50,16 +50,16 @@ class Person(models.Model):
db_table = 'CENSUS_PERSONS'
```
如果你希望Django管理表的生命周期,你需要把managed选项改为 True(或者简单地把它移除,因为True是默认值)。
如果你希望Django管理表的生命周期,你需要把managed选项改为 True(或者简单地把它移除,因为True是默认值)。
## 安装Django核心表 ##
## 安装Django核心表 ##
接下来,运行migrate命令来安装所有所需的额外的数据库记录,比如后台权限和内容类型:
接下来,运行migrate命令来安装所有所需的额外的数据库记录,比如后台权限和内容类型:
```
$ python manage.py migrate
```
## 测试和调整 ##
## 测试和调整 ##
上面就是所有基本的步骤了 —— 到目前为止你会想要调整Django自动生成的模型,直到他们按照你想要的方式工作。尝试通过Django数据库API访问你的数据,并且尝试使用Django后台页面编辑对象,以及相应地编辑模型文件。
\ No newline at end of file
上面就是所有基本的步骤了 —— 到目前为止你会想要调整Django自动生成的模型,直到他们按照你想要的方式工作。尝试通过Django数据库API访问你的数据,并且尝试使用Django后台页面编辑对象,以及相应地编辑模型文件。
\ No newline at end of file
<!--
译者:Github@wizardforcel
译者:Github@wizardforcel
-->
# 为模型提供初始数据 #
# 为模型提供初始数据 #
当你首次建立一个应用的时候,为你的数据库预先安装一些硬编码的数据,是很有用处的。 有几种方法可以让Django自动创建这些数据:你可以通过fixtures提供初始数据,或者提供一个包含初始数据的sql文件。
当你首次建立一个应用的时候,为你的数据库预先安装一些硬编码的数据,是很有用处的。 有几种方法可以让Django自动创建这些数据:你可以通过fixtures提供初始数据,或者提供一个包含初始数据的sql文件。
通常来讲,使用fixtrue更加简洁,因为它是数据库无关的,而使用sql初始化更加灵活。
通常来讲,使用fixtrue更加简洁,因为它是数据库无关的,而使用sql初始化更加灵活。
## 提供初始数据的fixtures ##
## 提供初始数据的fixtures ##
fixture是数据的集合,让Django了解如何导入到数据库中。创建fixture的最直接的方式,是使用manage.py dumpdata命令,如果数据库中已经有了一些数据。或者你可以手写fixtures。fixtures支持JSON、XML或者YAML(需要安装PyYAML)文档。序列化文档中详细阐述了每一种所支持的序列化格式。
fixture是数据的集合,让Django了解如何导入到数据库中。创建fixture的最直接的方式,是使用manage.py dumpdata命令,如果数据库中已经有了一些数据。或者你可以手写fixtures。fixtures支持JSON、XML或者YAML(需要安装PyYAML)文档。序列化文档中详细阐述了每一种所支持的序列化格式。
下面这个例子展示了一个简单的Person 模型的fixtrue,看起来很像JSON:
下面这个例子展示了一个简单的Person 模型的fixtrue,看起来很像JSON:
```
[
......@@ -35,7 +35,7 @@ fixture
]
```
下面是它的YAML格式:
下面是它的YAML格式:
```
- model: myapp.person
......@@ -50,64 +50,64 @@ fixture
last_name: McCartney
```
你可以把这些数据储存在你应用的fixtures目录中。
你可以把这些数据储存在你应用的fixtures目录中。
加载数据很简单:只要调用manage.py loaddata &lt;fixturename&gt;就好了,其中&lt;fixturename&gt;是你所创建的fixture文件的名字。每次你运行loaddata的时候,数据都会从fixture读出,并且重复加载进数据库。注意这意味着,如果你修改了fixtrue创建的某一行,然后再次运行了 loaddata,你的修改将会被抹掉。
加载数据很简单:只要调用manage.py loaddata &lt;fixturename&gt;就好了,其中&lt;fixturename&gt;是你所创建的fixture文件的名字。每次你运行loaddata的时候,数据都会从fixture读出,并且重复加载进数据库。注意这意味着,如果你修改了fixtrue创建的某一行,然后再次运行了 loaddata,你的修改将会被抹掉。
## 自动加载初始数据的fixtures ##
## 自动加载初始数据的fixtures ##
```
1.7中废除:
1.7中废除:
如果一个应用使用了迁移,将不会自动加载fixtures。由于Django 1.9中,迁移将会是必要的,这一行为经权衡之后被废除。 如果你想在一个应用中加载初始数据,考虑在数据迁移中加载它们。
如果一个应用使用了迁移,将不会自动加载fixtures。由于Django 1.9中,迁移将会是必要的,这一行为经权衡之后被废除。 如果你想在一个应用中加载初始数据,考虑在数据迁移中加载它们。
```
如果你创建了一个命名为 initial_data.[xml/yaml/json]的fixtrue,在你每次运行migrate命令时,fixtrue都会被加载。这非常方面,但是要注意:记住数据在你每次运行migrate命令后都会被刷新。So don’t use initial_data for data you’ll want to edit.
如果你创建了一个命名为 initial_data.[xml/yaml/json]的fixtrue,在你每次运行migrate命令时,fixtrue都会被加载。这非常方面,但是要注意:记住数据在你每次运行migrate命令后都会被刷新。So don’t use initial_data for data you’ll want to edit.
## Django在哪里寻找fixture文件 ##
## Django在哪里寻找fixture文件 ##
通常,Django 在每个应用的fixtures目录中寻找fixture文件。你可以设置FIXTURE_DIRS选项为一个额外目录的列表,Django会从里面寻找。
通常,Django 在每个应用的fixtures目录中寻找fixture文件。你可以设置FIXTURE_DIRS选项为一个额外目录的列表,Django会从里面寻找。
运行manage.py loaddata命令的时候,你也可以指定一个fixture文件的目录,它会覆盖默认设置中的目录。
运行manage.py loaddata命令的时候,你也可以指定一个fixture文件的目录,它会覆盖默认设置中的目录。
> 另见
> 另见
>
> fixtrues也被用于测试框架来搭建一致性的测试环境。
> fixtrues也被用于测试框架来搭建一致性的测试环境。
## 提供初始SQL数据 ##
## 提供初始SQL数据 ##
```
1.7中废除:
1.7中废除:
如果一个应用使用迁移,初始SQL数据将不会加载(包括后端特定的SQL数据)。由于Django 1.9中,迁移将会是必须的,这一行为经权衡后被废除。如果你想在应用中使用初始SQL数据,考虑在数据迁移中使用它们。
如果一个应用使用迁移,初始SQL数据将不会加载(包括后端特定的SQL数据)。由于Django 1.9中,迁移将会是必须的,这一行为经权衡后被废除。如果你想在应用中使用初始SQL数据,考虑在数据迁移中使用它们。
```
Django为数据库无关的SQL提供了一个钩子,当你运行migrate命令时,CREATE TABLE语句执行之后就会执行它。你可以使用这个钩子来建立默认的记录,或者创建SQL函数、视图、触发器以及其它。
Django为数据库无关的SQL提供了一个钩子,当你运行migrate命令时,CREATE TABLE语句执行之后就会执行它。你可以使用这个钩子来建立默认的记录,或者创建SQL函数、视图、触发器以及其它。
钩子十分简单:Django会在你应用的目录中寻找叫做sql/&lt;modelname&gt;.sql的文件,其中 &lt;modelname&gt;是小写的模型名称。
钩子十分简单:Django会在你应用的目录中寻找叫做sql/&lt;modelname&gt;.sql的文件,其中 &lt;modelname&gt;是小写的模型名称。
所以如果在myapp应用中存在Person模型,你应该在myapp目录的文件sql/person.sql中添加数据库无关的SQL。下面的例子展示了文件可能会包含什么:
所以如果在myapp应用中存在Person模型,你应该在myapp目录的文件sql/person.sql中添加数据库无关的SQL。下面的例子展示了文件可能会包含什么:
```
INSERT INTO myapp_person (first_name, last_name) VALUES ('John', 'Lennon');
INSERT INTO myapp_person (first_name, last_name) VALUES ('Paul', 'McCartney');
```
每个提供的SQL文件,都应该含有用于插入数据的有效的SQL语句(例如,格式适当的INSERT语句,用分号分隔)。
每个提供的SQL文件,都应该含有用于插入数据的有效的SQL语句(例如,格式适当的INSERT语句,用分号分隔)。
这些SQL文件可被manage.py中的 sqlcustom和sqlall命令阅读。详见manage.py文档。
这些SQL文件可被manage.py中的 sqlcustom和sqlall命令阅读。详见manage.py文档。
注意如果你有很多SQL数据文件,他们执行的顺序是不确定的。唯一可以确定的是,在你的自定义数据文件被执行之前,所有数据表都被创建好了。
注意如果你有很多SQL数据文件,他们执行的顺序是不确定的。唯一可以确定的是,在你的自定义数据文件被执行之前,所有数据表都被创建好了。
> 初始SQL数据和测试
> 初始SQL数据和测试
>
> 这一技巧不能以测试目的用于提供初始数据。Django的测试框架在每次测试后都会刷新测试数据库的内容。所以,任何使用自定义SQL钩子添加的数据都会丢失。
> 这一技巧不能以测试目的用于提供初始数据。Django的测试框架在每次测试后都会刷新测试数据库的内容。所以,任何使用自定义SQL钩子添加的数据都会丢失。
>
> 如果你需要在测试用例中添加数据,你应该在测试fixture中添加它,或者在测试用例的setUp()中添加。
> 如果你需要在测试用例中添加数据,你应该在测试fixture中添加它,或者在测试用例的setUp()中添加。
## 数据库后端特定的SQL数据 ##
## 数据库后端特定的SQL数据 ##
没有钩子提供给后端特定的SQL数据。例如,你有分别为PostgreSQL和SQLite准备的初始数据文件。对于每个应用,Django都会寻找叫做&lt;app_label&gt;/sql/&lt;modelname&gt;.&lt;backend&gt;.sql的文件,其中&lt;app_label&gt;是小写的模型名称,&lt;modelname&gt;是小写的模型名称,&lt;backend&gt;是你的设置文件中由ENGINE提供的模块名称的最后一部分(例如,如果你定义了一个数据库,ENGINE的值为django.db.backends.sqlite3,Django会寻找&lt;app_label&gt;/sql/&lt;modelname&gt;.sqlite3.sql)。
没有钩子提供给后端特定的SQL数据。例如,你有分别为PostgreSQL和SQLite准备的初始数据文件。对于每个应用,Django都会寻找叫做&lt;app_label&gt;/sql/&lt;modelname&gt;.&lt;backend&gt;.sql的文件,其中&lt;app_label&gt;是小写的模型名称,&lt;modelname&gt;是小写的模型名称,&lt;backend&gt;是你的设置文件中由ENGINE提供的模块名称的最后一部分(例如,如果你定义了一个数据库,ENGINE的值为django.db.backends.sqlite3,Django会寻找&lt;app_label&gt;/sql/&lt;modelname&gt;.sqlite3.sql)。
后端特定的SQL数据会先于后端无关的SQL数据执行。例如,如果你的应用包含了sql/person.sql 和sql/person.sqlite3.sql文件,而且你已经安装了SQLite应用,Django会首先执行 sql/person.sqlite3.sql的内容,其次才是sql/person.sql。
\ No newline at end of file
后端特定的SQL数据会先于后端无关的SQL数据执行。例如,如果你的应用包含了sql/person.sql 和sql/person.sqlite3.sql文件,而且你已经安装了SQLite应用,Django会首先执行 sql/person.sqlite3.sql的内容,其次才是sql/person.sql。
\ No newline at end of file
此差异已折叠。
# 编写视图 #
# 编写视图 #
一个视图函数,或者简短来说叫做视图,是一个简单的Python函数,它接受web请求,并且返回web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了能够把代码放在某个地方,惯例是把视图放在叫做views.py的文件中,然后把它放到你的项目或者应用目录里。
一个视图函数,或者简短来说叫做视图,是一个简单的Python函数,它接受web请求,并且返回web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了能够把代码放在某个地方,惯例是把视图放在叫做views.py的文件中,然后把它放到你的项目或者应用目录里。
## 一个简单的视图 ##
## 一个简单的视图 ##
下面是一个返回当前日期和时间作为HTML文档的视图:
下面是一个返回当前日期和时间作为HTML文档的视图:
```
from django.http import HttpResponse
......@@ -16,24 +16,24 @@ def current_datetime(request):
return HttpResponse(html)
```
让我们逐行阅读上面的代码:
让我们逐行阅读上面的代码:
+ 首先,我们从 django.http模块导入了HttpResponse类,以及Python的datetime库。
+ 接着,我们定义了current_datetime函数。它是一个视图函数。每个视图函数都应接收HttpRequest对象作为第一个参数,一般叫做request。
+ 注意视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够精确地反映出它的功能。
+ 这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都要返回HttpResponse对象。(有例外,我们接下来会讲。)
+ 首先,我们从 django.http模块导入了HttpResponse类,以及Python的datetime库。
+ 接着,我们定义了current_datetime函数。它是一个视图函数。每个视图函数都应接收HttpRequest对象作为第一个参数,一般叫做request。
+ 注意视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够精确地反映出它的功能。
+ 这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都要返回HttpResponse对象。(有例外,我们接下来会讲。)
> Django中的时区
> Django中的时区
>
> Django中包含一个TIME_ZONE设置,默认为America/Chicago。可能并不是你住的地方,所以你可能会在设置文件里修改它。
> Django中包含一个TIME_ZONE设置,默认为America/Chicago。可能并不是你住的地方,所以你可能会在设置文件里修改它。
## 把你的URL映射到视图 ##
## 把你的URL映射到视图 ##
所以,再重复一遍,这个视图函数返回了一个包含当前日期和时间的HTML页面。你需要创建URLconf来展示在特定的URL这一视图; 详见URL 分发器。
所以,再重复一遍,这个视图函数返回了一个包含当前日期和时间的HTML页面。你需要创建URLconf来展示在特定的URL这一视图; 详见URL 分发器。
## 返回错误 ##
## 返回错误 ##
在Django中返回HTTP错误是相当容易的。有一些HttpResponse的子类代表不是200(“OK”)的HTTP状态码。你可以在request/response文档中找到所有可用的子类。你可以返回那些子类的一个实例,而不是普通的HttpResponse ,来表示一个错误。例如:
在Django中返回HTTP错误是相当容易的。有一些HttpResponse的子类代表不是200(“OK”)的HTTP状态码。你可以在request/response文档中找到所有可用的子类。你可以返回那些子类的一个实例,而不是普通的HttpResponse ,来表示一个错误。例如:
```
from django.http import HttpResponse, HttpResponseNotFound
......@@ -46,7 +46,7 @@ def my_view(request):
return HttpResponse('<h1>Page was found</h1>')
```
由于一些状态码不太常用,所以不是每个状态码都有一个特化的子类。然而,如HttpResponse文档中所说的那样,你也可以向HttpResponse的构造器传递HTTP状态码,来创建你想要的任何状态码的返回类。例如:
由于一些状态码不太常用,所以不是每个状态码都有一个特化的子类。然而,如HttpResponse文档中所说的那样,你也可以向HttpResponse的构造器传递HTTP状态码,来创建你想要的任何状态码的返回类。例如:
```
from django.http import HttpResponse
......@@ -58,21 +58,21 @@ def my_view(request):
return HttpResponse(status=201)
```
由于404错误是最常见的HTTP错误,所以处理这一错误的方式非常便利。
由于404错误是最常见的HTTP错误,所以处理这一错误的方式非常便利。
## Http404异常 ##
## Http404异常 ##
**class django.http.Http404**
当你返回一个像HttpResponseNotFound这样的错误时,它会输出这个错误页面的HTML作为结果:
当你返回一个像HttpResponseNotFound这样的错误时,它会输出这个错误页面的HTML作为结果:
```
return HttpResponseNotFound('<h1>Page not found</h1>')
```
为了便利起见,也因为你的站点有个一致的404页面是个好主意,Django提供了Http404异常。如果你在视图函数中的任何地方抛出Http404异常,Django都会捕获它,并且带上HTTP404错误码返回你应用的标准错误页面。
为了便利起见,也因为你的站点有个一致的404页面是个好主意,Django提供了Http404异常。如果你在视图函数中的任何地方抛出Http404异常,Django都会捕获它,并且带上HTTP404错误码返回你应用的标准错误页面。
像这样:
像这样:
```
from django.http import Http404
......@@ -87,33 +87,33 @@ def detail(request, poll_id):
return render_to_response('polls/detail.html', {'poll': p})
```
为了尽可能利用 Http404,你应该创建一个用来在404错误产生时展示的模板。这个模板应该叫做404.html,并且在你的模板树中位于最顶层。
为了尽可能利用 Http404,你应该创建一个用来在404错误产生时展示的模板。这个模板应该叫做404.html,并且在你的模板树中位于最顶层。
如果你在抛出Http404异常时提供了一条消息,当DEBUG为True时它会出现在标准404模板的展示中。你可以将这些消息用于调试;但他们通常不适用于404模板本身。
如果你在抛出Http404异常时提供了一条消息,当DEBUG为True时它会出现在标准404模板的展示中。你可以将这些消息用于调试;但他们通常不适用于404模板本身。
## 自定义错误视图 ##
## 自定义错误视图 ##
Django中默认的错误视图对于大多数web应用已经足够了,但是如果你需要任何自定义行为,重写它很容易。只要在你的URLconf中指定下面的处理器(在其他任何地方设置它们不会有效)。
Django中默认的错误视图对于大多数web应用已经足够了,但是如果你需要任何自定义行为,重写它很容易。只要在你的URLconf中指定下面的处理器(在其他任何地方设置它们不会有效)。
handler404覆盖了page_not_found()视图:
handler404覆盖了page_not_found()视图:
```
handler404 = 'mysite.views.my_custom_page_not_found_view'
```
handler500覆盖了server_error()视图:
handler500覆盖了server_error()视图:
```
handler500 = 'mysite.views.my_custom_error_view'
```
handler403覆盖了permission_denied()视图:
handler403覆盖了permission_denied()视图:
```
handler403 = 'mysite.views.my_custom_permission_denied_view'
```
handler400覆盖了bad_request()视图:
handler400覆盖了bad_request()视图:
```
handler400 = 'mysite.views.my_custom_bad_request_view'
......
# 基于类的视图 #
视图是一个可调用对象,它接收一个请求然后返回一个响应。这个可调用对象可以不只是函数,Django 提供一些可以用作视图的类。它们允许你结构化你的视图并且利用继承和混合重用代码。后面我们将介绍一些用于简单任务的通用视图,但你可能想要设计自己的可重用视图的结构以适合你的使用场景。完整的细节,请参见基于类的视图的参考文档。
+ [基于类的视图简介](http://python.usyiyi.cn/django/topics/class-based-views/intro.html)
+ [内建的基于类的通用视图](http://python.usyiyi.cn/django/topics/class-based-views/generic-display.html)
+ [使用基于类的视图处理表单](http://python.usyiyi.cn/django/topics/class-based-views/generic-editing.html)
+ [使用混合来扩展视图类](http://python.usyiyi.cn/django/topics/class-based-views/mixins.html)
## 基本的示例 ##
Django 提供基本的视图类,它们适用于广泛的应用。所有的视图类继承自`View`类,它负责连接视图到URL、HTTP 方法调度和其它简单的功能。`RedirectView`用于简单的HTTP 重定向,`TemplateView`扩展基类来渲染模板。
## 在URLconf 中的简单用法 ##
使用通用视图最简单的方法是在URLconf 中创建它们。如果你只是修改基于类的视图的一些简单属性,你可以将它们直接传递给`as_view()`方法调用:
```
from django.conf.urls import url
from django.views.generic import TemplateView
urlpatterns = [
url(r'^about/', TemplateView.as_view(template_name="about.html")),
]
```
传递给`as_view()`的参数将覆盖类中的属性。在这个例子中,我们设置`TemplateView``template_name`。可以使用类似的方法覆盖`RedirectView``url`属性。
## 子类化通用视图 ##
第二种,功能更强一点的使用通用视图的方式是继承一个已经存在的视图并在子类中覆盖其属性(例如`template_name`)或方法(例如`get_context_data`)以提供新的值或方法。例如,考虑只显示一个模板`about.html`的视图。Django 有一个通用视图`TemplateView`来做这件事,所以我们可以简单地子类化它,并覆盖模板的名称:
```
# some_app/views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
```
然后我们只需要添加这个新的视图到我们的URLconf 中。`TemplateView`是一个类不是一个函数,所以我们将URL 指向类的`as_view()`方法,它让基于类的视图提供一个类似函数的入口:
```
# urls.py
from django.conf.urls import url
from some_app.views import AboutView
urlpatterns = [
url(r'^about/', AboutView.as_view()),
]
```
关于如何使用内建的通用视图的更多信息,参考下一主题通用的基于类的视图。
## 支持其它HTTP 方法 ##
假设有人想通过HTTP 访问我们的书库,它使用视图作为API。这个API 客户端将随时连接并下载自上次访问以来新出版的书籍的数据。如果没有新的书籍,仍然从数据库中获取书籍、渲染一个完整的响应并发送给客户端将是对CPU 和带宽的浪费。如果有个API 用于查询书籍最新发布的时间将会更好。
我们在URLconf 中映射URL 到书籍列表视图:
```
from django.conf.urls import url
from books.views import BookListView
urlpatterns = [
url(r'^books/$', BookListView.as_view()),
]
```
下面是这个视图:
```
from django.http import HttpResponse
from django.views.generic import ListView
from books.models import Book
class BookListView(ListView):
model = Book
def head(self, *args, **kwargs):
last_book = self.get_queryset().latest('publication_date')
response = HttpResponse('')
# RFC 1123 date format
response['Last-Modified'] = last_book.publication_date.strftime('%a, %d %b %Y %H:%M:%S GMT')
return response
```
如果该视图从GET 请求访问,将在响应中返回一个普通而简单的对象列表(使用`book_list.html`模板)。但如果客户端发出一个`HEAD`请求,响应将具有一个空的响应体而`Last-Modified`头部会指示最新发布的书籍的时间。基于这个信息,客户端可以下载或不下载完整的对象列表。
> 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Overview](https://docs.djangoproject.com/en/1.8/topics/class-based-views/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。
>
> [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。
此差异已折叠。
<!--
译者:Github@wizardforcel
译者:Github@wizardforcel
-->
# 中间件 #
# 中间件 #
中间件是一个介入Django的请求和响应的处理过程中的钩子框架。它是一个轻量级,底层的“插件”系统,用于在全局修改Django的输入或输出。
中间件是一个介入Django的请求和响应的处理过程中的钩子框架。它是一个轻量级,底层的“插件”系统,用于在全局修改Django的输入或输出。
中间件组件责任处理某些特殊的功能。例如,Django包含一个中间件组件,AuthenticationMiddleware ,使用会话将用户和请求关联。
中间件组件责任处理某些特殊的功能。例如,Django包含一个中间件组件,AuthenticationMiddleware ,使用会话将用户和请求关联。
这篇文档讲解了中间件如何工作,如何激活中间件,以及如何编写自己的中间件。Django集成了一些内置的中间件可以直接开箱即用。它们被归档在 内置中间件参考.
这篇文档讲解了中间件如何工作,如何激活中间件,以及如何编写自己的中间件。Django集成了一些内置的中间件可以直接开箱即用。它们被归档在 内置中间件参考.
## 激活中间件 ##
## 激活中间件 ##
要激活一个中间件组件,需要把它添加到你Django配置文件中的MIDDLEWARE_CLASSES 列表中。
要激活一个中间件组件,需要把它添加到你Django配置文件中的MIDDLEWARE_CLASSES 列表中。
在MIDDLEWARE_CLASSES中,每一个中间件组件用字符串的方式描述:一个完整的Python全路径加上中间件的类名称。例如,使用 django-admin startproject创建工程的时候生成的默认值:
在MIDDLEWARE_CLASSES中,每一个中间件组件用字符串的方式描述:一个完整的Python全路径加上中间件的类名称。例如,使用 django-admin startproject创建工程的时候生成的默认值:
```
MIDDLEWARE_CLASSES = (
......@@ -29,90 +29,90 @@ MIDDLEWARE_CLASSES = (
)
```
Django的程序中,中间件不是必需的 — 只要你喜欢,MIDDLEWARE_CLASSES可以为空 — 但是强烈推荐你至少使用CommonMiddleware。
Django的程序中,中间件不是必需的 — 只要你喜欢,MIDDLEWARE_CLASSES可以为空 — 但是强烈推荐你至少使用CommonMiddleware。
MIDDLEWARE_CLASSES中的顺序非常重要,因为一个中间件可能依赖于另外一个。例如,AuthenticationMiddleware在会话中储存已认证的用户。所以它必须在SessionMiddleware之后运行。一些关于Django中间件类的顺序的常见提示,请见Middleware ordering。
MIDDLEWARE_CLASSES中的顺序非常重要,因为一个中间件可能依赖于另外一个。例如,AuthenticationMiddleware在会话中储存已认证的用户。所以它必须在SessionMiddleware之后运行。一些关于Django中间件类的顺序的常见提示,请见Middleware ordering。
## 钩子和应用顺序 ##
## 钩子和应用顺序 ##
在请求阶段中,调用视图之前,Django会按照MIDDLEWARE_CLASSES中定义的顺序自顶向下应用中间件。会用到两个钩子:
在请求阶段中,调用视图之前,Django会按照MIDDLEWARE_CLASSES中定义的顺序自顶向下应用中间件。会用到两个钩子:
+ process_request()
+ process_view()
在响应阶段中,调用视图之后,中间件会按照相反的顺序应用,自底向上。会用到三个钩子:
在响应阶段中,调用视图之后,中间件会按照相反的顺序应用,自底向上。会用到三个钩子:
+ process_exception() (仅当视图抛出异常的时候)
+ process_template_response() (仅用于模板响应)
+ process_exception() (仅当视图抛出异常的时候)
+ process_template_response() (仅用于模板响应)
+ process_response()
![](https://docs.djangoproject.com/en/1.8/_images/middleware.svg)
如果你愿意的话,你可以把它想象成一颗洋葱:每个中间件都是包裹视图的一层“皮”。
如果你愿意的话,你可以把它想象成一颗洋葱:每个中间件都是包裹视图的一层“皮”。
每个钩子的行为接下来会描述。
每个钩子的行为接下来会描述。
## 编写自己的中间件 ##
## 编写自己的中间件 ##
编写自己的中间件很容易的。每个中间件组件是一个单独的Python的class,你可以定一个或多个下面的这些方法:
编写自己的中间件很容易的。每个中间件组件是一个单独的Python的class,你可以定一个或多个下面的这些方法:
### process_request ###
**process_request(request)**
request是一个HttpRequest 对象。
request是一个HttpRequest 对象。
在Django决定执行哪个视图(view)之前,process_request()会被每次请求调用。
在Django决定执行哪个视图(view)之前,process_request()会被每次请求调用。
它应该返回一个None 或一个HttpResponse对象。如果返回 None, Django会继续处理这个请求,执行其他process_request()中间件,然后process_view()中间件显示对应的视图。如果它返回一个HttpResponse对象,Django便不再会去调用其他的请求(request), 视图(view)或其他中间件,或对应的视图;处理HttpResponse的中间件会处理任何返回的响应(response)。
它应该返回一个None 或一个HttpResponse对象。如果返回 None, Django会继续处理这个请求,执行其他process_request()中间件,然后process_view()中间件显示对应的视图。如果它返回一个HttpResponse对象,Django便不再会去调用其他的请求(request), 视图(view)或其他中间件,或对应的视图;处理HttpResponse的中间件会处理任何返回的响应(response)。
### process_view ###
**process_view(request, view_func, view_args, view_kwargs)**
request是一个HttpRequest对象。view_func是 Django会调用的一个Python的函数。(它确实是一个函数对象,不是函数的字符名称。) view_args是一个会被传递到视图的位置参数列表,而view_kwargs 是一个会被传递到视图的关键字参数字典。 view_args和 view_kwargs 都不包括第一个视图参数(request)。
request是一个HttpRequest对象。view_func是 Django会调用的一个Python的函数。(它确实是一个函数对象,不是函数的字符名称。) view_args是一个会被传递到视图的位置参数列表,而view_kwargs 是一个会被传递到视图的关键字参数字典。 view_args和 view_kwargs 都不包括第一个视图参数(request)。
process_view()会在Django调用视图(view)之前被调用。
process_view()会在Django调用视图(view)之前被调用。
它将返回None 或一个HttpResponse 对象。如果返回 None,将会继续处理这个请求,执行其他的process_view() 中间件,然后显示对应的视图。如果返回HttpResponse对象,Django就不再会去调用其他的视图(view),异常中间件(exception middleware)或对应的视图 ;它会把响应中间件应用到HttpResponse上,并返回结果。
它将返回None 或一个HttpResponse 对象。如果返回 None,将会继续处理这个请求,执行其他的process_view() 中间件,然后显示对应的视图。如果返回HttpResponse对象,Django就不再会去调用其他的视图(view),异常中间件(exception middleware)或对应的视图 ;它会把响应中间件应用到HttpResponse上,并返回结果。
> 注意
> 注意
>
> 在中间件内部,从process_request或者process_view方法中访问request.POST或者request.REQUEST将会阻碍该中间 件之后的所有视图无法修改request的上传处理程序, 一般情况要避免这样使用。
> 在中间件内部,从process_request或者process_view方法中访问request.POST或者request.REQUEST将会阻碍该中间 件之后的所有视图无法修改request的上传处理程序, 一般情况要避免这样使用。
> 类CsrfViewMiddleware可以被认为是个例外 ,因为它提供了csrf_exempt() 和 csrf_protect()两个允许视图来精确控制 在哪个点需要开启CSRF验证。
> 类CsrfViewMiddleware可以被认为是个例外 ,因为它提供了csrf_exempt() 和 csrf_protect()两个允许视图来精确控制 在哪个点需要开启CSRF验证。
### process_template_response ###
**process_template_response(request, response)**
request是一个HttpRequest对象。response是一个TemplateResponse对象(或等价的对象),由Django视图或者中间件返回。
request是一个HttpRequest对象。response是一个TemplateResponse对象(或等价的对象),由Django视图或者中间件返回。
如果响应的实例有render()方法,process_template_response()在视图刚好执行完毕之后被调用,这表明了它是一个TemplateResponse对象(或等价的对象)。
如果响应的实例有render()方法,process_template_response()在视图刚好执行完毕之后被调用,这表明了它是一个TemplateResponse对象(或等价的对象)。
这个方法必须返回一个实现了render方法的响应对象。它可以修改给定的response对象,通过修改 response.template_name和response.context_data或者它可以创建一个全新的 TemplateResponse或等价的对象。
这个方法必须返回一个实现了render方法的响应对象。它可以修改给定的response对象,通过修改 response.template_name和response.context_data或者它可以创建一个全新的 TemplateResponse或等价的对象。
你不需要显式渲染响应 —— 一旦所有的模板响应中间件被调用,响应会自动被渲染。
你不需要显式渲染响应 —— 一旦所有的模板响应中间件被调用,响应会自动被渲染。
在一个响应的处理期间,中间件以相反的顺序运行,这包括process_template_response()。
在一个响应的处理期间,中间件以相反的顺序运行,这包括process_template_response()。
### process_response ###
**process_response(request, response)**
request是一个HttpRequest对象。response是Django视图或者中间件返回的HttpResponse或者StreamingHttpResponse对象。
request是一个HttpRequest对象。response是Django视图或者中间件返回的HttpResponse或者StreamingHttpResponse对象。
process_response()在所有响应返回浏览器之前被调用。
process_response()在所有响应返回浏览器之前被调用。
这个方法必须返回HttpResponse或者StreamingHttpResponse对象。它可以改变已有的response,或者创建并返回新的HttpResponse或StreamingHttpResponse对象。
这个方法必须返回HttpResponse或者StreamingHttpResponse对象。它可以改变已有的response,或者创建并返回新的HttpResponse或StreamingHttpResponse对象。
不像 process_request()和process_view()方法,即使同一个中间件类中的process_request()和process_view()方法会因为前面的一个中间件返回HttpResponse而被跳过,process_response()方法总是会被调用。特别是,这意味着你的process_response()方法不能依赖于process_request()方法中的设置。
不像 process_request()和process_view()方法,即使同一个中间件类中的process_request()和process_view()方法会因为前面的一个中间件返回HttpResponse而被跳过,process_response()方法总是会被调用。特别是,这意味着你的process_response()方法不能依赖于process_request()方法中的设置。
最后,记住在响应阶段中,中间件以相反的顺序被应用,自底向上。意思是定义在MIDDLEWARE_CLASSES最底下的类会最先被运行。
最后,记住在响应阶段中,中间件以相反的顺序被应用,自底向上。意思是定义在MIDDLEWARE_CLASSES最底下的类会最先被运行。
### 处理流式响应 ###
### 处理流式响应 ###
不像HttpResponse,StreamingHttpResponse并没有content属性。所以,中间件再也不能假设所有响应都带有content属性。如果它们需要访问内容,他们必须测试是否为流式响应,并相应地调整自己的行为。
不像HttpResponse,StreamingHttpResponse并没有content属性。所以,中间件再也不能假设所有响应都带有content属性。如果它们需要访问内容,他们必须测试是否为流式响应,并相应地调整自己的行为。
```
if response.streaming:
......@@ -121,9 +121,9 @@ else:
response.content = alter_content(response.content)
```
> 注意
> 注意
>
> 我们需要假设streaming_content可能会大到在内存中无法容纳。响应中间件可能会把它封装在新的生成器中,但是一定不要销毁它。封装一般会实现成这样:
> 我们需要假设streaming_content可能会大到在内存中无法容纳。响应中间件可能会把它封装在新的生成器中,但是一定不要销毁它。封装一般会实现成这样:
>
```
def wrap_streaming_content(content):
......@@ -136,33 +136,33 @@ def wrap_streaming_content(content):
**process_exception(request, exception)**
request是一个HttpRequest对象。exception是一个被视图中的方法抛出来的 Exception对象。
request是一个HttpRequest对象。exception是一个被视图中的方法抛出来的 Exception对象。
当一个视图抛出异常时,Django会调用process_exception()来处理。process_exception()应该返回一个None 或者一个HttpResponse对象。如果它返回一个HttpResponse对象,模型响应和响应中间件会被应用,响应结果会返回给浏览器。Otherwise, default exception handling kicks in.
当一个视图抛出异常时,Django会调用process_exception()来处理。process_exception()应该返回一个None 或者一个HttpResponse对象。如果它返回一个HttpResponse对象,模型响应和响应中间件会被应用,响应结果会返回给浏览器。Otherwise, default exception handling kicks in.
再次提醒,在处理响应期间,中间件的执行顺序是倒序执行的,这包括process_exception。如果一个异常处理的中间件返回了一个响应,那这个中间件上面的中间件都将不会被调用。
再次提醒,在处理响应期间,中间件的执行顺序是倒序执行的,这包括process_exception。如果一个异常处理的中间件返回了一个响应,那这个中间件上面的中间件都将不会被调用。
### \_\_init\_\_ ###
大多数的中间件类都不需要一个初始化方法,因为中间件的类定义仅仅是为process\_\*提供一个占位符。如果你确实需要一个全局的状态那就可以通过\_\_init\_\_来加载。然后要铭记如下两个警告:
大多数的中间件类都不需要一个初始化方法,因为中间件的类定义仅仅是为process\_\*提供一个占位符。如果你确实需要一个全局的状态那就可以通过\_\_init\_\_来加载。然后要铭记如下两个警告:
Django初始化你的中间件无需任何参数,因此不要定义一个有参数的\_\_init\_\_方法。
不像process\_\*每次请求到达都要调用\_\_init\_\_只会被调用一次,就是在Web服务启动的时候。
Django初始化你的中间件无需任何参数,因此不要定义一个有参数的\_\_init\_\_方法。
不像process\_\*每次请求到达都要调用\_\_init\_\_只会被调用一次,就是在Web服务启动的时候。
### 标记中间件不被使用 ###
### 标记中间件不被使用 ###
有时在运行时决定是否一个中间件需要被加载是很有用的。 在这种情况下,你的中间件中的 \_\_init\_\_方法可以抛出一个django.core.exceptions.MiddlewareNotUsed异常。Django会从中间件处理过程中移除这部分中间件,并且当DEBUG为True的时候在django.request记录器中记录调试信息。
有时在运行时决定是否一个中间件需要被加载是很有用的。 在这种情况下,你的中间件中的 \_\_init\_\_方法可以抛出一个django.core.exceptions.MiddlewareNotUsed异常。Django会从中间件处理过程中移除这部分中间件,并且当DEBUG为True的时候在django.request记录器中记录调试信息。
```
1.8中的修改:
1.8中的修改:
之前 MiddlewareNotUsed异常不会被记录。
之前 MiddlewareNotUsed异常不会被记录。
```
## 指导准则 ##
## 指导准则 ##
+ 中间件的类不能是任何类的子类。
+ 中间件可以存在与你Python路径中的任何位置。 Django所关心的只是被包含在MIDDLEWARE_CLASSES中的配置。
+ 将Django’s available middleware作为例子随便看看。
+ 如果你认为你写的中间件组建可能会对其他人有用,那就把它共享到社区! 让我们知道它,我们会考虑把它添加到Django中。
\ No newline at end of file
+ 中间件的类不能是任何类的子类。
+ 中间件可以存在与你Python路径中的任何位置。 Django所关心的只是被包含在MIDDLEWARE_CLASSES中的配置。
+ 将Django’s available middleware作为例子随便看看。
+ 如果你认为你写的中间件组建可能会对其他人有用,那就把它共享到社区! 让我们知道它,我们会考虑把它添加到Django中。
\ No newline at end of file
<!--
者:Github@wizardforcel
译者:Github@wizardforcel
-->
# django.contrib.humanize #
一系列Django的模板过滤器,有助于向数据添加“人文关怀”。
一系列Django的模板过滤器,有助于向数据添加“人文关怀”。
把'django.contrib.humanize'添加到INSTALLED_APPS设置来激活这些过滤器。 执行以上步骤之后,在模板中使用{% load humanize %} ,你就可以访问到下面的过滤器了·。
把'django.contrib.humanize'添加到INSTALLED_APPS设置来激活这些过滤器。 执行以上步骤之后,在模板中使用{% load humanize %} ,你就可以访问到下面的过滤器了·。
## 基数词 ##
## 基数词 ##
对于数字1~9,返回拼写出来的数字。否则返回数字本身。这样遵循了出版的格式。
对于数字1~9,返回拼写出来的数字。否则返回数字本身。这样遵循了出版的格式。
如:
例如:
+ 1 会变成one。
+ 2 会变成 two。
+ 10 会变成 10。
+ 1 会变成one。
+ 2 会变成 two。
+ 10 会变成 10。
你可以传递整数,或者整数的字符串形式。
你可以传递整数,或者整数的字符串形式。
## 数间的逗号 ##
## 整数间的逗号 ##
将整数转化为字符串,每三位之间带一个逗号。
将整数转化为字符串,每三位之间带一个逗号。
如:
例如:
+ 4500 会变成 4,500。
+ 45000 会变成 45,000
+ 450000 会变成 450,000。
+ 4500000 会变成 4,500,000。
+ 4500 会变成 4,500。
+ 45000 会变成 45,000
+ 450000 会变成 450,000。
+ 4500000 会变成 4,500,000。
如果启动了格式本地化,将会被遵循。例如,在德语('de')中:
如果启动了格式本地化,将会被遵循。例如,在德语('de')中:
+ 45000 会变成 '45.000'。
+ 450000 会变成 '450.000'。
+ 45000 会变成 '45.000'。
+ 450000 会变成 '450.000'。
你可以传递整数,或者整数的字符串形式。
你可以传递整数,或者整数的字符串形式。
## 整数词组 ##
## 整数词组 ##
将一个大的整数转化为友好的文字表示形式。适用于超过一百万的数字。
将一个大的整数转化为友好的文字表示形式。适用于超过一百万的数字。
如:
例如:
+ 1000000 会变成 1.0 million。
+ 1200000 会变成 1.2 million。
+ 1200000000 会变成 1.2 billion。
+ 1000000 会变成 1.0 million。
+ 1200000 会变成 1.2 million。
+ 1200000000 会变成 1.2 billion。
支持高达10的100次方 (Googol) 的整数。
支持高达10的100次方 (Googol) 的整数。
如果启动了格式本地化将会被遵循。例如,在德语('de')中:
如果启动了格式本地化将会被遵循。例如,在德语('de')中:
+ 1000000 会变成 '1,0 Million'。
+ 1200000 会变成 '1,2 Million'。
+ 1200000000 会变成 '1,2 Milliarden'。
+ 1000000 会变成 '1,0 Million'。
+ 1200000 会变成 '1,2 Million'。
+ 1200000000 会变成 '1,2 Milliarden'。
你可以传递整数,或者整数的字符串形式。
你可以传递整数,或者整数的字符串形式。
## 然日期 ##
## 自然日期 ##
对于当天或者一天之内的日期, 返回“今天”,“明天”或者“昨天”,视情况而定。否则,使用传进来的格式字符串给日期格式化。
对于当天或者一天之内的日期, 返回“今天”,“明天”或者“昨天”,视情况而定。否则,使用传进来的格式字符串给日期格式化。
参数:日期的格式字符串在date标签中描述。
参数:日期的格式字符串在date标签中描述。
例如(其中“今天”是2007年2月17日):
例如(其中“今天”是2007年2月17日):
+ 16 Feb 2007 会变成 yesterday。
+ 17 Feb 2007 会变成 today。
+ 18 Feb 2007 会变成 tomorrow。
+ 16 Feb 2007 会变成 yesterday。
+ 17 Feb 2007 会变成 today。
+ 18 Feb 2007 会变成 tomorrow。
其他日期按照提供的参数格式化,如果没提供参数的话,将会按照DATE_FORMAT 设置。
其他日期按照提供的参数格式化,如果没提供参数的话,将会按照DATE_FORMAT 设置。
## 自然时间 ##
## 自然时间 ##
对于日期时间的值,返回一个字符串来表示多少秒、分钟或者小时之前 —— 如果超过一天之前,则回退为使用timesince格式。如果是未来的日期时间,返回值会自动使用合适的文字表述。
对于日期时间的值,返回一个字符串来表示多少秒、分钟或者小时之前 —— 如果超过一天之前,则回退为使用timesince格式。如果是未来的日期时间,返回值会自动使用合适的文字表述。
例如(其中“现在”是2007年2月17日16时30分0秒):
例如(其中“现在”是2007年2月17日16时30分0秒):
+ 17 Feb 2007 16:30:00 会变成 now。
+ 17 Feb 2007 16:29:31 会变成 29 seconds ago。
+ 17 Feb 2007 16:29:00 会变成 a minute ago。
+ 17 Feb 2007 16:25:35 会变成 4 minutes ago。
+ 17 Feb 2007 15:30:29 会变成 59 minutes ago。
+ 17 Feb 2007 15:30:01 会变成 59 minutes ago。
+ 17 Feb 2007 15:30:00 会变成 an hour ago。
+ 17 Feb 2007 13:31:29 会变成 2 hours ago。
+ 16 Feb 2007 13:31:29 会变成 1 day, 2 hours ago。
+ 16 Feb 2007 13:30:01 会变成 1 day, 2 hours ago。
+ 16 Feb 2007 13:30:00 会变成 1 day, 3 hours ago。
+ 17 Feb 2007 16:30:30 会变成 30 seconds from now。
+ 17 Feb 2007 16:30:29 会变成 29 seconds from now。
+ 17 Feb 2007 16:31:00 会变成 a minute from now。
+ 17 Feb 2007 16:34:35 会变成 4 minutes from now。
+ 17 Feb 2007 17:30:29 会变成 an hour from now。
+ 17 Feb 2007 18:31:29 会变成 2 hours from now。
+ 18 Feb 2007 16:31:29 会变成 1 day from now。
+ 26 Feb 2007 18:31:29 会变成 1 week, 2 days from now。
+ 17 Feb 2007 16:30:00 会变成 now。
+ 17 Feb 2007 16:29:31 会变成 29 seconds ago。
+ 17 Feb 2007 16:29:00 会变成 a minute ago。
+ 17 Feb 2007 16:25:35 会变成 4 minutes ago。
+ 17 Feb 2007 15:30:29 会变成 59 minutes ago。
+ 17 Feb 2007 15:30:01 会变成 59 minutes ago。
+ 17 Feb 2007 15:30:00 会变成 an hour ago。
+ 17 Feb 2007 13:31:29 会变成 2 hours ago。
+ 16 Feb 2007 13:31:29 会变成 1 day, 2 hours ago。
+ 16 Feb 2007 13:30:01 会变成 1 day, 2 hours ago。
+ 16 Feb 2007 13:30:00 会变成 1 day, 3 hours ago。
+ 17 Feb 2007 16:30:30 会变成 30 seconds from now。
+ 17 Feb 2007 16:30:29 会变成 29 seconds from now。
+ 17 Feb 2007 16:31:00 会变成 a minute from now。
+ 17 Feb 2007 16:34:35 会变成 4 minutes from now。
+ 17 Feb 2007 17:30:29 会变成 an hour from now。
+ 17 Feb 2007 18:31:29 会变成 2 hours from now。
+ 18 Feb 2007 16:31:29 会变成 1 day from now。
+ 26 Feb 2007 18:31:29 会变成 1 week, 2 days from now。
## 序数词 ##
## 序数词 ##
将一个整数转化为它的序数词字符串。
将一个整数转化为它的序数词字符串。
如:
例如:
+ 1 会变成 1st。
+ 2 会变成 2nd。
+ 3 会变成 3rd。
+ 1 会变成 1st。
+ 2 会变成 2nd。
+ 3 会变成 3rd。
你可以传递整数,或者整数的字符串形式。
\ No newline at end of file
你可以传递整数,或者整数的字符串形式。
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册