diff --git a/docs/glance.md b/docs/glance.md index 3904d48..8ec4cb6 100644 --- a/docs/glance.md +++ b/docs/glance.md @@ -1,14 +1,14 @@ # 初识 Django -Django 最初被设计用于具有快速开发需求的新闻类站点,目的是要实现简单快捷的网站开发。以下内容简要介绍了如何使用 Django 实现一个数据库驱动的 Web 应用。 +Django 的设计最初应用于具有快速开发需求的新闻类站点,目的是要实现简单快捷的网站开发。以下内容简要介绍了如何使用 Django 实现一个数据库驱动的 Web 应用。 -为了让您充分理解 Django 的工作原理,这份文档为您详细描述了相关的技术细节,不过这并不是一份入门教程或者是参考文档(我们当然也为您准备了这些)。如果您想要马上开始一个项目,可以从 [实例教程(zh)](part1.md)开始入手,或者直接开始阅读详细的[参考文档](https://docs.djangoproject.com/en/1.11/topics/)。 +为了让您充分理解 Django 的工作原理,这份文档为您详细描述了相关的技术细节,不过这并不是一份入门教程或者是参考文档(我们当然也为您准备了这些)。如果您想要马上开始一个项目,可以从 [实例教程(zh)](part1.md)开始入手,或者直接开始阅读详细的[参考文档](https://docs.djangoproject.com/en/2.0/topics/)。 ## 设计模型 Django 无需数据库就可以使用,它提供了[对象关系映射器(ORM)](https://en.wikipedia.org/wiki/Object-relational_mapping)。通过此技术,你可以使用 Python 代码来描述数据库结构。 -[数据模型语法](https://docs.djangoproject.com/en/1.11/topics/db/models/)提供了很多方法来描述你的数据,这解决了多年来在数据库模式中的难题。以下是一个简明的例子: +[数据模型语法](https://docs.djangoproject.com/en/2.0/topics/db/models/)提供了很多方法来描述你的数据,这解决了多年来在数据库模式中的难题。以下是一个简明的例子: ```python # mysite/news/models.py @@ -18,16 +18,16 @@ from django.db import models class Reporter(models.Model): full_name = models.CharField(max_length=70) - def __str__(self): # Python 2 下请使用 __unicode__ + def __str__(self): return self.full_name class Article(models.Model): pub_date = models.DateField() headline = models.CharField(max_length=200) content = models.TextField() - reporter = models.ForeignKey(Reporter) + reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE) - def __str__(self): # Python 2 下请使用 __unicode__ + def __str__(self): return self.headline ``` @@ -39,7 +39,7 @@ class Article(models.Model): $ python manage.py migrate ``` -[**migrate**](https://docs.djangoproject.com/en/1.11/ref/django-admin/#django-admin-migrate) 命令会查找所有可用的模型,如果数据库中没有与之对应的表,则会为其自动创建。Django 也提供了其他[更丰富的控制方式](https://docs.djangoproject.com/en/1.11/topics/migrations/)。 +[**migrate**](https://docs.djangoproject.com/en/2.0/ref/django-admin/#django-admin-migrate) 命令会查找所有可用的模型,如果数据库中没有与之对应的表,则会为其自动创建。Django 也提供了其他[更丰富的控制方式](https://docs.djangoproject.com/en/2.0/topics/migrations/)。 ## 享用便捷的 API @@ -117,9 +117,9 @@ DoesNotExist: Reporter matching query does not exist. ## 动态生成的管理页面:并非徒有其表 -当你的模型完成定义,Django 就会自动生成一个专业的生产级[管理页面](https://docs.djangoproject.com/en/1.11/ref/contrib/admin/) - 一个可以让已认证用户进行添加、更改和删除对象的 Web 站点。你只需简单的在 admin 站点上注册你的模型即可。 +当你的模型完成定义,Django 就会自动生成一个专业的生产级[管理页面](https://docs.djangoproject.com/en/2.0/ref/contrib/admin/) - 一个可以让已认证用户进行添加、更改和删除对象的 Web 站点。你只需简单的在 admin 站点上注册你的模型即可。 -``` python3 +``` python # mysite/news/models.py from django.db import models @@ -149,21 +149,21 @@ admin.site.register(models.Article) 简洁优雅的 URL 规划对于一个高质量 Web 应用来说至关重要。Django 推崇优美的 URL 设计,所以不要把诸如 **.php** 和 **.asp** 之类的冗余的后缀放到 URL 里。 -为了设计你自己的 URL,你需要创建一个叫做 [URLconf](https://docs.djangoproject.com/en/1.11/topics/http/urls/) 的 Python 模块。一张包含 URL 匹配模式和 Python 回调函数之间的映射表。URLconf 也有利于将 Python 代码与 URL 解耦合(译注:使各个模块分离,独立)。 +为了设计你自己的 URL,你需要创建一个叫做 [URLconf](https://docs.djangoproject.com/en/2.0/topics/http/urls/) 的 Python 模块。一张包含 URL 匹配模式和 Python 回调函数之间的映射表。URLconf 也有利于将 Python 代码与 URL 解耦合(译注:使各个模块分离,独立)。 下面这个 URLconf 适用于前面 **Reporter/Article** 的例子: ```python # mysite/news/urls.py -from django.conf.urls import url +from django.urls import path from . import views urlpatterns = [ - url(r'^articles/([0-9]{4})/$', views.year_archive), - url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), - url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), + path('articles//', views.year_archive), + path('articles///', views.month_archive), + path('articles////', views.article_detail), ] ``` @@ -171,11 +171,11 @@ urlpatterns = [ 一旦其中一个正则表达式匹配成功,Django 就会导入并调用指定的视图——那是一个简单的 Python 函数。视图会被传进一个请求(requeset)对象——其中包含了请求元数据——和正则表达式匹配到的那些参数值。 -比如,如果用户请求了“/articles/2005/05/39323/”这样的 URL,Django 就会这样调用函数:**news.views.article_detail(request, '2005', '05', '39323')**。 +比如,如果用户请求了“/articles/2005/05/39323/”这样的 URL,Django 就会这样调用函数:**news.views.article_detail(request, year=2005, month=5, pk=39323)**。 ## 编写视图 -视图函数的执行结果只可能有两种:返回一个包含请求页面内容的 [**HttpResponse**](https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpResponse) 对象;或者是抛出 [**Http404**](https://docs.djangoproject.com/en/1.11/topics/http/views/#django.http.Http404) 这类异常。至于视图接下来还要做什么则由你决定。 +视图函数的执行结果只可能有两种:返回一个包含请求页面内容的 [**HttpResponse**](https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpResponse) 对象;或者是抛出 [**Http404**](https://docs.djangoproject.com/en/2.0/topics/http/views/#django.http.Http404) 这类异常。至于视图接下来还要做什么则由你决定。 通常来说,一个视图的工作就是:从参数获取数据,加载模板,然后模板进行带数据的渲染。下面是一个 **year_archive** 的视图例子: @@ -192,15 +192,15 @@ def year_archive(request, year): return render(request, 'news/year_archive.html', context) ``` -这个例子使用了 Django 的[模板系统](https://docs.djangoproject.com/en/1.11/topics/templates/),它有着很多强大的功能,而且使用起来足够简单,即使不是程序员也可轻松使用。 +这个例子使用了 Django 的[模板系统](https://docs.djangoproject.com/en/2.0/topics/templates/),它有着很多强大的功能,而且使用起来足够简单,即使不是程序员也可轻松使用。 ## 设计模板 上面的代码加载了 **news/year_archive.html** 这个模板。 -Django 允许设置搜索模板路径,这样可以最小化模板之间的冗余。在Django设置中,你可以通过 [**DIRS**](https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-TEMPLATES-DIRS) 参数指定目录列表来检索模板。如果模板不在第一个目录中,就继续检查第二个,以此类推。 +Django 允许设置搜索模板路径,这样可以最小化模板之间的冗余。在Django设置中,你可以通过 [**DIRS**](https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-TEMPLATES-DIRS) 参数指定目录列表来检索模板。如果模板不在第一个目录中,就继续检查第二个,以此类推。 -比如**news/year_archive.html** 模板找到了,它可能是这样的: +比如 **news/year_archive.html** 模板找到了,它可能是这样的: ```html+django # mysite/news/templates/news/year_archive.html @@ -224,11 +224,11 @@ Django 允许设置搜索模板路径,这样可以最小化模板之间的冗 注意: **{{ article.pub_date|date:"F j, Y" }}** 使用了 Unix 风格的“管道符”(“|”字符)。这是一个模板过滤器,用于过滤变量值。在这里过滤器将一个 Python **datetime** 对象转化为指定的格式(就像 PHP 中的日期函数那样)。 -你可以将多个过滤器连在一起使用。你还可以[自定义模板过滤器](https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/#howto-writing-custom-template-filters)。你甚至可以[自定义模板标签](https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/),相关的 Python 代码会在使用标签时在后台运行。 +你可以将多个过滤器连在一起使用。你还可以[自定义模板过滤器](https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/#howto-writing-custom-template-filters)。你甚至可以[自定义模板标签](https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/),相关的 Python 代码会在使用标签时在后台运行。 Django 使用了“模板继承”的概念。这就是 **{% extends "base.html" %}** 的作用。它的含义是“先加载名为 base 的模板作为基类,并且用下面的标记块对模板中定义的标记块进行填充”。简而言之,模板继承可以使模板间的冗余内容最小化:每个模板只需包含与其他文档有区别的内容。 -下面是 **base.html** 可能的样子,它使用了[静态文件](https://docs.djangoproject.com/en/1.11/howto/static-files/): +下面是 **base.html** 可能的样子,它使用了[静态文件](https://docs.djangoproject.com/en/2.0/howto/static-files/): ```html+django # mysite/templates/base.html @@ -255,8 +255,8 @@ Django 使用了“模板继承”的概念。这就是 **{% extends "base.html" 以上只是 Django 的功能性概述。Django 还有更多实用的特性: - - [缓存框架](https://docs.djangoproject.com/en/1.11/topics/cache/)可以与 memcached 或其他后端集成。 - - [聚合器框架](https://docs.djangoproject.com/en/1.11/ref/contrib/syndication/)可以通过简单编写一个 Python 类来推送 RSS 和 Atom。 + - [缓存框架](https://docs.djangoproject.com/en/2.0/topics/cache/)可以与 memcached 或其他后端集成。 + - [聚合器框架](https://docs.djangoproject.com/en/2.0/ref/contrib/syndication/)可以通过简单编写一个 Python 类来推送 RSS 和 Atom。 - 更多令人心动的自动化管理功能:概述里面仅仅浅尝辄止。 接下来您可以[下载 Django(zh)](install.md),阅读 [实例教程(zh)](part1.md),然后加入 [Django 社区](https://www.djangoproject.com/community/)!感谢您的关注! diff --git a/docs/install.md b/docs/install.md index 42f855c..e11b36d 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,17 +1,13 @@ # 快速安装指南 -你需要先安装 Django 才可以使用它。我们有一份[完整安装指南](https://docs.djangoproject.com/en/1.11/topics/install/),它涵盖了所有可能遇到的问题。本指南将会帮助你完成一个简单、最小化的安装。 +你需要先安装 Django 才可以使用它。我们有一份[完整安装指南](https://docs.djangoproject.com/en/2.0/topics/install/),它涵盖了所有可能遇到的问题。本指南将会帮助你完成一个简单、最小化的安装。 ## 安装 Python -作为一个 Python Web 框架,Django 依赖 Python。从 [Django 适用于哪些版本的 Python](https://docs.djangoproject.com/en/1.11/faq/install/#faq-python-version-support)可以获取更多信息。较新版本的 Python 内置一个轻量级的数据库 [SQLite](https://sqlite.org),所以你暂时不需要配置数据库。 +作为一个 Python Web 框架,Django 依赖 Python。从 [Django 适用于哪些版本的 Python](https://docs.djangoproject.com/en/2.0/faq/install/#faq-python-version-support)可以获取更多信息。较新版本的 Python 内置一个轻量级的数据库 [SQLite](https://sqlite.org),所以你暂时不需要配置数据库。 可以从 [Python 官网](https://www.python.org/downloads/)或者系统的包管理工具获取到最新版的 Python。 -> **Jython 上的 Django** -> -> 如果你使用的是 Jython(一种 Java 平台的 Python 实现),你需要做一些额外的步骤。查看[在 Jython 上运行 Django ](https://docs.djangoproject.com/en/1.11/howto/jython/)获取详细信息。 - 你可以在终端下输入命令 **python** 来验证是否已经安装 Python;你应该看到下面的信息: ```pycon @@ -23,19 +19,19 @@ Type "help", "copyright", "credits" or "license" for more information. ## 配置数据库 -只有当你需要使用“大型”数据库例如 PostgreSQL、MySQL 或 Oracle 时,才需要这一步。若要安装这样的数据库,请参考[数据库安装信息](https://docs.djangoproject.com/en/1.11/topics/install/#database-installation)。 +只有当你需要使用“大型”数据库例如 PostgreSQL、MySQL 或 Oracle 时,才需要这一步。若要安装这样的数据库,请参考[数据库安装信息](https://docs.djangoproject.com/en/2.0/topics/install/#database-installation)。 ## 删除旧版本的 Django -如果你是从旧版本的 Django 升级安装,你将需要[在安装新版本之前卸载旧版本的 Django](https://docs.djangoproject.com/en/1.11/topics/install/#removing-old-versions-of-django)。 +如果你是从旧版本的 Django 升级安装,你将需要[在安装新版本之前卸载旧版本的 Django](https://docs.djangoproject.com/en/2.0/topics/install/#removing-old-versions-of-django)。 ## 安装 Django 你可以按下面三个简单的方式来安装 Django: -- [安装官方发布版本](https://docs.djangoproject.com/en/1.11/topics/install/#installing-official-release)。对大多数用户来说这是最好的方式。 -- 安装[操作系统所提供的发行包](https://docs.djangoproject.com/en/1.11/topics/install/#installing-distribution-package)。 -- [安装最新的开发版](https://docs.djangoproject.com/en/1.11/topics/install/#installing-development-version)。这对于那些想要尝试最新最棒的特性而不担心运行崭新代码的用户来说是最好的。你可能会遇到一些 bug,但向 Django 报告这些 bug 将有助于他们的开发。此外,第三方包的很可能不兼容最新的开发版。 +- [安装官方发布版本](https://docs.djangoproject.com/en/2.0/topics/install/#installing-official-release)。对大多数用户来说这是最好的方式。 +- 安装[操作系统所提供的发行包](https://docs.djangoproject.com/en/2.0/topics/install/#installing-distribution-package)。 +- [安装最新的开发版](https://docs.djangoproject.com/en/2.0/topics/install/#installing-development-version)。这对于那些想要尝试最新最棒的特性而不担心运行崭新代码的用户来说是最好的。你可能会遇到一些 bug,但向 Django 报告这些 bug 将有助于他们的开发。此外,第三方包的很可能不兼容最新的开发版。 > **务必参考与你所使用的 Django 版本相对应的文档!** > @@ -48,7 +44,7 @@ Type "help", "copyright", "credits" or "license" for more information. ```pycon >>> import django >>> print(django.get_version()) -1.11 +2.0 ``` 如果版本和上面不一样,那你可能安装了其他版本的 Django 。 diff --git a/docs/part1.md b/docs/part1.md index 0ae3883..67381eb 100644 --- a/docs/part1.md +++ b/docs/part1.md @@ -17,17 +17,17 @@ $ python -m django --version 如果已安装,你会看到安装的版本号;如果还没安装,你会看到错误提示:“No module named django”。 -本教程的目标版本是 Django 1.11 和 Python 3.4 或更高版本。如果 Django 版本不匹配,你可以通过点击页面右下角的切换版本按钮来转到适合你版本的教程,或者你可以选择将 Django 升级到最新版本。如果你还在用 Python 2.7,你将需要对教程中的代码作一些微调,微调内容会被写在代码的注释里。 +本教程的目标版本是 Django 2.0 和 Python 3.4 或更高版本。如果 Django 版本不匹配,你可以通过点击页面右下角的切换版本按钮来转到适合你版本的教程,或者你可以选择将 Django 升级到最新版本。如果你还在用 Python 2.7,你将需要对教程中的代码作一些微调,微调内容会被写在代码的注释里。 你可以查看文档 [快速安装指南(zh)](install.md) 来获得关于移除旧版本,安装新版本的建议。 > **哪里可以获得帮助:** > -> 如果你在阅读或实践本教程中遇到困难,请发消息给 [django-users](https://docs.djangoproject.com/en/1.11/internals/mailing-lists/#django-users-mailing-list) 或加入IRC频道 [django on irc.freenode.net](irc://irc.freenode.net/django) 来与其他 Django 用户进行交流,他们也许能帮到你。 +> 如果你在阅读或实践本教程中遇到困难,请发消息给 [django-users](https://docs.djangoproject.com/en/2.0/internals/mailing-lists/#django-users-mailing-list) 或加入IRC频道 [django on irc.freenode.net](irc://irc.freenode.net/django) 来与其他 Django 用户进行交流,他们也许能帮到你。 ## 创建项目 -如果这是你第一次使用 Django ,你还需要进行一些初始化设置。也就是说,你需要通过自动生成代码来建立一个 Django [项目](https://docs.djangoproject.com/en/1.11/glossary/#term-project)(一个 Django 项目实例需要的设置项集合,包括数据库配置, Django 选项和应用程序的具体设置)。 +如果这是你第一次使用 Django ,你还需要进行一些初始化设置。也就是说,你需要通过自动生成代码来建立一个 Django [项目](https://docs.djangoproject.com/en/2.0/glossary/#term-project)(一个 Django 项目实例需要的设置项集合,包括数据库配置, Django 选项和应用程序的具体设置)。 打开命令行,**cd** 切换到一个你想存放代码的目录,然后运行以下命令: @@ -35,7 +35,7 @@ $ python -m django --version $ django-admin startproject mysite ``` -这行代码将会在当前目录下创建一个 **mysite** 目录。如果命令不起作用,请看文档 [Problems running django-admin](https://docs.djangoproject.com/en/1.11/faq/troubleshooting/#troubleshooting-django-admin)。 +这行代码将会在当前目录下创建一个 **mysite** 目录。如果命令不起作用,请看文档 [Problems running django-admin](https://docs.djangoproject.com/en/2.0/faq/troubleshooting/#troubleshooting-django-admin)。 > **注意** > @@ -47,7 +47,7 @@ $ django-admin startproject mysite > > 你可以把代码放在文档根目录 **以外** 的地方,比如 **/home/mycode**。 -让我们看看 [startproject](https://docs.djangoproject.com/en/1.11/ref/django-admin/#django-admin-startproject) 这命令创建了什么: +让我们看看 [startproject](https://docs.djangoproject.com/en/2.0/ref/django-admin/#django-admin-startproject) 这命令创建了什么: ```text mysite/ @@ -62,12 +62,12 @@ mysite/ 这些目录和文件的用处是: - 最外层的 **mysite/** 根目录只是你项目的容器, Django 不关心它的名字,你可以将它重命名为任何你喜欢的名字。 -- **manage.py**:一个让你可以用各种方式管理该 Django 项目的命令行工具。你可以阅读 [django-admin and manage.py](https://docs.djangoproject.com/en/1.11/ref/django-admin/) 来获取关于 **manage.py** 的更多细节。 +- **manage.py**:一个让你可以用各种方式管理该 Django 项目的命令行工具。你可以阅读 [django-admin and manage.py](https://docs.djangoproject.com/en/2.0/ref/django-admin/) 来获取关于 **manage.py** 的更多细节。 - 里面一层的 **mysite/** 目录就是你项目的实际 Python 包。它的名字就是当你引用它内部任何东西时需要用到的 Python 包名(比如:**mysite.urls**)。 - **mysite/\_\_init\_\_.py**:一个用于指明此目录是 Python 包的空白文件。(如果你刚开始学习 Python,请阅读 Python 官方文档中的 [more about packages](https://docs.python.org/3/tutorial/modules.html#tut-packages) 。) - **mysite/settings.py**:该 Django 项目的配置文件。如果你想知道这个文件是如何工作的,请看文档 [Django settings][settings]。 -- **mysite/urls.py**:该 Django 项目的 URL 声明,就像是你网站的“目录”。阅读 [URL dispatcher](https://docs.djangoproject.com/en/1.11/topics/http/urls/) 文档来获取更多关于 URL 的内容。 -- **mysite/wsgi.py**:当你部署项目到一个兼容 WSGI 的服务器上时所需要的入口点。[How to deploy with WSGI](https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/) 文档内有更多关于这个文件的细节。 +- **mysite/urls.py**:该 Django 项目的 URL 声明,就像是你网站的“目录”。阅读 [URL dispatcher](https://docs.djangoproject.com/en/2.0/topics/http/urls/) 文档来获取更多关于 URL 的内容。 +- **mysite/wsgi.py**:当你部署项目到一个兼容 WSGI 的服务器上时所需要的入口点。[How to deploy with WSGI](https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/) 文档内有更多关于这个文件的细节。 ## 用于开发的服务器 @@ -88,13 +88,13 @@ System check identified no issues (0 silenced). You have unapplied migrations; your app may not work properly until they are applied. Run 'python manage.py migrate' to apply them. -August 02, 2017 - 15:50:53 -Django version 1.11, using settings 'mysite.settings' +January 31, 2018 - 15:50:53 +Django version 2.0, using settings 'mysite.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. ``` -> **注意** +> **注意** > 现在请先忽略关于没有应用数据库迁移的警告,我们将在下一章解决它。 现在你已经开启了 Django 开发服务器 —— 一个纯 Python 编写的轻量级 Web 服务器。我们已经在 Django 里包含了这项功能,所以你可以快速的开发网站,而不用去配置生产环境的服务器(比如 Apache),直到你做好了网站并准备投入生产环境。 @@ -161,7 +161,7 @@ polls/ ## 编写你的第一个视图 -创建并打开 **polls/views.py**,然后写进 Python 代码: +创建并打开 **polls/views.py**,然后写进 Python 代码: ```python # polls/views.py @@ -173,7 +173,7 @@ def index(request): 这也许是 Django 中最简单的视图了。为了调用这个视图,我们需要在它和一个 URL 之间做映射,这就需要 URLconf。 -为了在 polls 目录下创建一个 URLconf,需要先创建 **urls.py** 文件,你的应用目录现在应该是这样的: +为了在 polls 目录下创建一个 URLconf,需要先创建 **urls.py** 文件,你的应用目录现在应该是这样的: ```text polls/ @@ -192,12 +192,12 @@ polls/ ```python # polls/urls.py -from django.conf.urls import url +from django.urls import path from . import views urlpatterns = [ - url(r'^$', views.index, name='index'), + path('', views.index, name='index'), ] ``` @@ -205,12 +205,12 @@ urlpatterns = [ ```python # mysite/urls.py -from django.conf.urls import include, url +from django.urls import include, path from django.contrib import admin urlpatterns = [ - url(r'^polls/', include('polls.urls')), - url(r'^admin/', admin.site.urls), + path('polls/', include('polls.urls')), + path('admin/', admin.site.urls), ] ``` @@ -218,14 +218,14 @@ urlpatterns = [ [**include()**][include] 背后的想法是想使得 URL 的即插即用变得简单。polls 是在它们自己的 URLconf 中( **polls/urls.py** ),它们是可以被放在“/polls/”、 “/fun_polls/”、 “/content/polls/”、 或者其他任何路径,而应用仍然是可以运行的。 -> **什么时候使用 [include()][include]** -> -> 当你要包含其他 URL 匹配模式时,你应该一直使用 **include()**。 **admin.site.urls** 在这里是一个例外。 - -> **不符合你所想看到的?** -> -> 如果你看到的是 **include(admin.iste.urls)** 而不是 **admin.site.urls**,可能是你正在使用的 Django 版本和本教程的目标版本(1.11)不一致。那你就需要切换到旧版本的教程或者是安装较新的 Django。 - +> **什么时候使用 [include()][include]** +> +> 当你要包含其他 URL 匹配模式时,你应该一直使用 **include()**。 **admin.site.urls** 在这里是一个例外。 + +> **不符合你所想看到的?** +> +> 如果你看到的是 **include(admin.iste.urls)** 而不是 **admin.site.urls**,可能是你正在使用的 Django 版本和本教程的目标版本(1.11)不一致。那你就需要切换到旧版本的教程或者是安装较新的 Django。 + 现在你已经将 **index** 视图和 URLconf 连接在一起了。让我们验证下是不是生效了,运行以下命令: ```bash @@ -234,35 +234,32 @@ $ python manage.py runserver 在你的浏览器中打开 [http://localhost:8000/polls/](http://localhost:8000/polls/)。你应该能看到文字 “Hello, world. You’re at the polls index.” —— 这是你在 **index** 视图中定义的。 -[**url**][url] 函数有四个参数,两个必需参数:**regex** 正则和 **view** 视图;两个选项参数:**kwargs**字典和 **name** 名字。在这点上,值得再看下这些参数到底是干什么的。 - -### [url][url]参数:regex +[**path()**][path] 函数有四个参数,两个必需参数:**route** 路由和 **view** 视图;两个选项参数:**kwargs**字典和 **name** 名字。在这点上,值得再看下这些参数到底是干什么的。 -术语 “regex” 是正则表达式 “regular expression” 的缩写,是匹配字符串的一段语法,像这里例子的是 url 匹配模式。Django 从列表的第一个正则表达式开始,按顺序匹配请求的 URL,直到找到与之匹配的。 +### [path()][path] 参数:route -注意,这些正则表达式不会去匹配 GET 和 POST 请求的参数值,或者域名。比如 **https://www.example.com/myapp/** 这个请求,URLconf 会找 **myapp/**;**https://www.example.com/myapp/?page=3** 这个请求,URLconf 同样只会找 **mysqpp/**。 +**route** 是一个包含了 URL 模式的字符串。在开始处理一个请求时,Django 会在 **urlpatterns** 中从第一条模式起,顺着列表自上而下一条条寻找比较,直到发现相匹配的模式。 -如果你需要正则表达式的帮助,可以看 [**Wikipedia’s entry**](https://en.wikipedia.org/wiki/Regular_expression) 和 关于 [**re**](https://docs.python.org/3/library/re.html#module-re) 模块的文档。还有,由 Jeffrey Friedl 写的书 《掌握正则表达式》 也是很棒的。实际上,你并不需要成为正则表达式方面的专家,你真正要会的是如何使用简单捕获模式。因为复杂的正则可能会有不尽人意的查找性能,所以你不太应该全依赖于正则匹配。 +但这些模式并不理会 GET 或 POST 请求所带的参数,或是域名。比如说,在 **https://www.example.com/myapp/** 这个请求中,URLconf 将会寻找 **myapp/**。而在 **https://www.example.com/myapp/?page=3** 这个请求中,URLconf 也只会去寻找 **myapp/**。 -最后,一个性能注意点:这些正则表达式在 URLconf 模块加载后的第一时间就被编译了。它们都是非常快的(只要查找的不是特别复杂就像上面举例的)。 -### [url][url] 参数:view +### [path()][path] 参数:view 当 Django 发现一个正则表达式匹配时,Django 就会调用指定的视图函数,[**HttpRequest**][HttpRequest] 对象作为第一个参数,正则表达式捕获的值作为其他参数。如果正则使用简单捕获,值会作为位置参数传递;如果使用命名捕获,值会作为关键字传递。我们稍后会给出一个例子。 -### [url][url] 参数:kwargs +### [path()][path] 参数:kwargs 任意的关键字参数都可以作为字典传递到目标视图。但我们不准备在本教程里使用 Django 的这个特性。 -### [url][url] 参数:name +### [path()][path] 参数:name 命名你的 URL 可以让你在 Django 的别处明白引用的是什么,特别是在模版里。这个强大的特性允许你在项目里对一个文件操作就能对 URL 模式做全局改变。 当你对基本的请求和响应流都明白时,你就可以阅读 [教程第二部分(zh)](part2.md)开始使用数据库了。 -[settings]: https://docs.djangoproject.com/en/1.11/topics/settings/ -[runserver]: https://docs.djangoproject.com/en/1.11/ref/django-admin/#django-admin-runserver -[url]: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url -[include]: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.include -[HttpRequest]: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpRequest \ No newline at end of file +[settings]: https://docs.djangoproject.com/en/2.0/topics/settings/ +[runserver]: https://docs.djangoproject.com/en/2.0/ref/django-admin/#django-admin-runserver +[path]: https://docs.djangoproject.com/en/2.0/ref/urls/#django.urls.path +[include]: https://docs.djangoproject.com/en/2.0/ref/urls/#django.conf.urls.include +[HttpRequest]: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpRequest diff --git a/docs/part2.md b/docs/part2.md index 288927d..271ddbb 100644 --- a/docs/part2.md +++ b/docs/part2.md @@ -9,22 +9,22 @@ 默认情况下,配置的数据库是 SQLite,如果你对数据库不太熟,或者你只是对尝试 Django 感兴趣,这是最简单的选择。SQLite 内嵌在 Python 里,所以你不用再安装其他东西来支持你的数据库。但是当你开始做第一个实际的项目时,你也许想使用一个可扩展的数据库,比如 PostgreSQL 来避免令人头痛地切换数据库问题。 如果你希望使用其他数据库,你需要安装合适的 [database bingings][database-installation] 和在 [DATABASES][DATABASES] 'default' 默认项里改变一些键值,以匹配你的数据库设置: -- [**引擎(ENGINE)**][ENGINE] - **'django.db.backends.sqlite3'**、 - **'django.db.backends.postgresql'**、 - **'django.db.backends.mysql'** 或者 +- [**引擎(ENGINE)**][ENGINE] - **'django.db.backends.sqlite3'**、 + **'django.db.backends.postgresql'**、 + **'django.db.backends.mysql'** 或者 **'django.db.backends.oracle'**。 其他的后端 [也可以参考][third-party-notes]。 - [**名字(NAME)**][NAME] - 你数据库的名字。如果你正在使用 SQLite,数据库将以文件形式保存在你的电脑;在这种情况下,[**名字**][NAME] 应该是绝对路径,包括文件名。默认 **os.path.join(BASE_DIR, 'db.sqlite3')** 将把文件保存在你项目的目录下。 如果你不使用 SQLite 作为你的数据库,那就必须额外设置下比如 [**USER**][USER], [**PASSWORD**][PASSWORD] 和 [**HOST**][HOST]。若想查看更多详情,可以参考文档 [**DATABASES**][DATABASES]。 -> 对于 SQLite 以外的数据库 -> -> 如果你使用除 SQLite 以外的数据库,请确认你已经创建了数据库。在你的数据库交互提示里用 “CREATE DATABASE database_name;” 创建数据库。 -> -> 同样要确认在 **mysite/settings.py** 中的数据库用户拥有创建数据库的权限。这可以允许自动创建 [测速数据库][the-test-database] —— 后面的教程需要。 -> -> 如果你在使用 SQLite,你不需要在这之前创建什么 —— 数据库文件会在需要的时候自动创建。 - +> 对于 SQLite 以外的数据库 +> +> 如果你使用除 SQLite 以外的数据库,请确认你已经创建了数据库。在你的数据库交互提示里用 “CREATE DATABASE database_name;” 创建数据库。 +> +> 同样要确认在 **mysite/settings.py** 中的数据库用户拥有创建数据库的权限。这可以允许自动创建 [测速数据库][the-test-database] —— 后面的教程需要。 +> +> 如果你在使用 SQLite,你不需要在这之前创建什么 —— 数据库文件会在需要的时候自动创建。 + 当你编辑 **mysite/settings** 的时候,记得把时区 [**TIME_ZONE**][TIME_ZONE] 设成你要的时区。 同样的,注意 [**INSTALLED_APPS**][INSTALLED_APPS] 应该设置在文件的较顶端处。它放着这个 Django 实例激活的所有 Django 应用程序。应用可以被用在多个项目中,你可以把它们打包分发,供其他人在项目中使用。 @@ -33,10 +33,10 @@ - [**django.contrib.admin**][admin] —— 管理站点。你可以快捷地使用它。 - [**django.contrib.auth**][auth] —— 认证系统。 -- [**django.contrib.contenttypes**][contenttypes] —— 内容类型框架。 -- [**django.contrib.sessions**][sessions] —— session 框架。 -- [**django.contrib.messages**][messages] —— 消息框架。 -- [**django.contrib.staticfiles**][staticfiles] —— 静态文件管理框架。 +- [**django.contrib.contenttypes**][contenttypes] —— 内容类型框架。 +- [**django.contrib.sessions**][sessions] —— session 框架。 +- [**django.contrib.messages**][messages] —— 消息框架。 +- [**django.contrib.staticfiles**][staticfiles] —— 静态文件管理框架。 通常情况下为了方便,这些应用默认已被包含。 @@ -48,20 +48,20 @@ $ python manage.py migrate [**migrate**][migrate] 命令在 **mysite/settings.py** 文件的 [**INSTALLED_APPS**][INSTALLED_APPS] 设置中寻找,并根据数据库设置创建一些必要的数据库表和随应用迁移的数据库(我们将会稍后介绍)。你将看到应用于各个迁移的消息。如果你感兴趣,可以运行一下你的数据库客户端,然后输入(**\dt** (PostgreSQL), **SHOW TABLES;** (MySQL), **.schema** (SQLite), **SELECT TABLE_NAME FROM USER_TABLES;** (Oracle))来看下 Django 创建的这些表。 -> **给极简主义者** -> +> **给极简主义者** +> > 就像我们上面说的,通常情况下,这些默认应用都被包含了,但不是人人都需要它们的。如果你不需要其中一些,或者不需要它们全部,在运行 [**migrate**][migrate] 之前,可以随心把它们从 [**INSTALLED_APPS**][INSTALLED_APPS] 中注释或删掉。[**migrate**][migrate] 命令只会迁移那些在 [**INSTALLED_APPS**][INSTALLED_APPS] 中激活的应用的数据库。 ## 创建模型 现在我们将用额外的元数据来定义你的模型 —— 本质上是你的数据库布局。 -> **设计哲学** -> -> 模型是你数据的简单明确的描述。它包含了储存的数据所必要的字段和行为。Django 遵循 [DRY 原则][dry]。它的目标是让你只需要在一个地方定义数据模型,Django 就能自动从中导出迁移代码。 -> -> 来介绍一下迁移 - 举个例子,不像 Ruby On Rails,Django 的迁移代码全部都是从你的模型文件导出的,它本质上只是个历史记录,Django 可以通过滚动更新数据库来匹配你当前的模型。 - +> **设计哲学** +> +> 模型是你数据的简单明确的描述。它包含了储存的数据所必要的字段和行为。Django 遵循 [DRY 原则][dry]。它的目标是让你只需要在一个地方定义数据模型,Django 就能自动从中导出迁移代码。 +> +> 来介绍一下迁移 - 举个例子,不像 Ruby On Rails,Django 的迁移代码全部都是从你的模型文件导出的,它本质上只是个历史记录,Django 可以通过滚动更新数据库来匹配你当前的模型。 + 在这个简单的投票应用中,我们将创建两个模型:问题 **Question** 和选项 **Choice**。**Question** 模型包括问题描述和发布时间。**Choice** 模型有两个字段:选项描述和当前票数。每个 **选项** 属于一个 **问题**。 这些概念可以通过一个简单的 Python 类来表示。像下面那样编辑 **polls/models.py** 文件: @@ -229,21 +229,6 @@ $ python manage.py shell 我们使用这个命令而不是简单的使用 “Python” 是因为 **manage.py** 会设置 **DJANGO_SETTINGS_MODULE** 环境变量,这个变量会让 Django 根据 **mysite/settings.py** 文件来设置 Python 包的导入路径。 -> **我就是不想用 manage.py** -> -> 如果你不想使用 **manage.py** ,没问题,你只要手动将 [**DJANGO_SETTINGS_MODULE**][DJANGO_SETTINGS_MODULE] 环境变量设置为 **mysite.settings** 就行。打开一个普通的 Python 命令行,然后输入以下命令来配置 Django: -> -```pycon ->>> import django ->>> django.setup() -``` -> -> 如果抛出 [**AttributeError**](https://docs.python.org/3/library/exceptions.html#AttributeError) 错误,说明你使用的 Django 版本可能和本教程不一致。你可以切换到旧版本的教程或者把 Django 升级至最新版本。 -> -> 你必须在 **manage.py** 所在目录中运行 **python** 命令,或者确保这个目录在 Python path 里,因为只有这样 import mysite 才能被正确的执行。 -> -> 阅读 [Django-admin 文档][django-admin] 获取更多信息。 - 当你成功进入命令行后,来试试 [数据库 API][queries] 吧: ```pycon @@ -291,15 +276,12 @@ datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=) ```python # polls/models.py from django.db import models -from django.utils.encoding import python_2_unicode_compatible、 -@python_2_unicode_compatible # 如果你想支持 Python 2 class Question(models.Model): # ... def __str__(self): return self.question_text -@python_2_unicode_compatible # 如果你想支持 Python 2 class Choice(models.Model): # ... def __str__(self): @@ -407,7 +389,7 @@ True >>> c.delete() ``` -阅读 [Accessing related objects](https://docs.djangoproject.com/en/1.11/ref/models/relations/) 文档可以获取关于数据库关系的更多内容。想知道关于双下划线的更多用法,参见 [Field Lookup][field-lookups-intro] 文档。数据库 API 的所有细节可以在 [数据库 API 参考][queries] 文档中找到。 +阅读 [Accessing related objects](https://docs.djangoproject.com/en/2.0/ref/models/relations/) 文档可以获取关于数据库关系的更多内容。想知道关于双下划线的更多用法,参见 [Field Lookup][field-lookups-intro] 文档。数据库 API 的所有细节可以在 [数据库 API 参考][queries] 文档中找到。 ## 介绍下 Django 的管理站点 @@ -523,38 +505,38 @@ admin.site.register(Question) 当你明白了模型的 API 和熟悉了你自己的管理站点后,你就可以开始阅读 [教程第三部分(zh)](part3.md) ,学习如何在投票应用中添加更多的视图。 -[utils.timezone]: https://docs.djangoproject.com/en/1.11/ref/utils/#module-django.utils.timezone -[max_length]: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.CharField.max_length -[ForeignKey]: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.ForeignKey -[Model]:https://docs.djangoproject.com/en/1.11/ref/models/instances/#django.db.models.Model -[django-admin]: https://docs.djangoproject.com/en/1.11/ref/django-admin/ -[DJANGO_SETTINGS_MODULE]: https://docs.djangoproject.com/en/1.11/topics/settings/#envvar-DJANGO_SETTINGS_MODULE -[sqlmigrate]: https://docs.djangoproject.com/en/1.11/ref/django-admin/#django-admin-sqlmigrate -[timezones]: https://docs.djangoproject.com/en/1.11/topics/i18n/timezones/ -[field-lookups-intro]: https://docs.djangoproject.com/en/1.11/topics/db/queries/#field-lookups-intro -[django-admin-check]: https://docs.djangoproject.com/en/1.11/ref/django-admin/#django-admin-check -[admin]: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#module-django.contrib.admin -[third-party-notes]: https://docs.djangoproject.com/en/1.11/ref/databases/#third-party-notes -[database-installation]: https://docs.djangoproject.com/en/1.11/topics/install/#database-installation -[queries]: https://docs.djangoproject.com/en/1.11/topics/db/queries/ -[auth]: https://docs.djangoproject.com/en/1.11/topics/auth/#module-django.contrib.auth -[the-test-database]: https://docs.djangoproject.com/en/1.11/topics/testing/overview/#the-test-database -[TIME_ZONE]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-TIME_ZONE -[USER]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-USER -[PASSWORD]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-PASSWORD -[HOST]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-HOST -[NAME]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-NAME -[DATABASES]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-DATABASES -[INSTALLED_APPS]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-INSTALLED_APPS -[migrate]: https://docs.djangoproject.com/en/1.11/ref/django-admin/#django-admin-migrate -[Field]: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.Field -[CharField]: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.CharField -[DateTimeField]: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.DateTimeField -[default]: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.Field.default -[ENGINE]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-DATABASE-ENGINE -[translation]: https://docs.djangoproject.com/en/1.11/topics/i18n/translation/ -[contenttypes]: https://docs.djangoproject.com/en/1.11/ref/contrib/contenttypes/#module-django.contrib.contenttypes -[sessions]: https://docs.djangoproject.com/en/1.11/topics/http/sessions/#module-django.contrib.sessions -[messages]: https://docs.djangoproject.com/en/1.11/ref/contrib/messages/#module-django.contrib.messages -[staticfiles]: https://docs.djangoproject.com/en/1.11/ref/contrib/staticfiles/#module-django.contrib.staticfiles -[dry]: https://docs.djangoproject.com/en/1.11/misc/design-philosophies/#dry \ No newline at end of file +[utils.timezone]: https://docs.djangoproject.com/en/2.0/ref/utils/#module-django.utils.timezone +[max_length]: https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.CharField.max_length +[ForeignKey]: https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey +[Model]:https://docs.djangoproject.com/en/2.0/ref/models/instances/#django.db.models.Model +[django-admin]: https://docs.djangoproject.com/en/2.0/ref/django-admin/ +[DJANGO_SETTINGS_MODULE]: https://docs.djangoproject.com/en/2.0/topics/settings/#envvar-DJANGO_SETTINGS_MODULE +[sqlmigrate]: https://docs.djangoproject.com/en/2.0/ref/django-admin/#django-admin-sqlmigrate +[timezones]: https://docs.djangoproject.com/en/2.0/topics/i18n/timezones/ +[field-lookups-intro]: https://docs.djangoproject.com/en/2.0/topics/db/queries/#field-lookups-intro +[django-admin-check]: https://docs.djangoproject.com/en/2.0/ref/django-admin/#django-admin-check +[admin]: https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#module-django.contrib.admin +[third-party-notes]: https://docs.djangoproject.com/en/2.0/ref/databases/#third-party-notes +[database-installation]: https://docs.djangoproject.com/en/2.0/topics/install/#database-installation +[queries]: https://docs.djangoproject.com/en/2.0/topics/db/queries/ +[auth]: https://docs.djangoproject.com/en/2.0/topics/auth/#module-django.contrib.auth +[the-test-database]: https://docs.djangoproject.com/en/2.0/topics/testing/overview/#the-test-database +[TIME_ZONE]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-TIME_ZONE +[USER]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-USER +[PASSWORD]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-PASSWORD +[HOST]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-HOST +[NAME]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-NAME +[DATABASES]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-DATABASES +[INSTALLED_APPS]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-INSTALLED_APPS +[migrate]: https://docs.djangoproject.com/en/2.0/ref/django-admin/#django-admin-migrate +[Field]: https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.Field +[CharField]: https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.CharField +[DateTimeField]: https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.DateTimeField +[default]: https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.Field.default +[ENGINE]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-DATABASE-ENGINE +[translation]: https://docs.djangoproject.com/en/2.0/topics/i18n/translation/ +[contenttypes]: https://docs.djangoproject.com/en/2.0/ref/contrib/contenttypes/#module-django.contrib.contenttypes +[sessions]: https://docs.djangoproject.com/en/2.0/topics/http/sessions/#module-django.contrib.sessions +[messages]: https://docs.djangoproject.com/en/2.0/ref/contrib/messages/#module-django.contrib.messages +[staticfiles]: https://docs.djangoproject.com/en/2.0/ref/contrib/staticfiles/#module-django.contrib.staticfiles +[dry]: https://docs.djangoproject.com/en/2.0/misc/design-philosophies/#dry diff --git a/docs/part3.md b/docs/part3.md index 84ece94..efe4d44 100644 --- a/docs/part3.md +++ b/docs/part3.md @@ -30,7 +30,7 @@ Django 中的视图的概念是「一类具有相同功能和模板的网页的 为了将 URL 和视图关联起来,Django 使用了 “URLconfs” 来配置。URLconf 将 URL 模式(表现为一个正则表达式)映射到视图。 -本教程只会介绍 URLconf 的基础内容,你可以查看 [**django.core.urlresolvers**][module-django.urls] 以获取更多内容。 +本教程只会介绍 URLconf 的基础内容,你可以查看 [**URL dispatcher**][module-django.urls] 以获取更多内容。 ## 编写更多的视图 @@ -50,42 +50,41 @@ def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id) ``` -要把这些新视图添加进 **polls.urls** 模块里,只需添加几个 **url()** 函数调用: +要把这些新视图添加进 **polls.urls** 模块里,只需添加几个 **path()** 函数调用: ```python # polls/urls.py -from django.conf.urls import url +from django.urls import path from . import views urlpatterns = [ # ex: /polls/ - url(r'^$', views.index, name='index'), + path('', views.index, name='index'), # ex: /polls/5/ - url(r'^(?P[0-9]+)/$', views.detail, name='detail'), + path('/', views.detail, name='detail'), # ex: /polls/5/results/ - url(r'^(?P[0-9]+)/results/$', views.results, name='results'), + path('/results/', views.results, name='results'), # ex: /polls/5/vote/ - url(r'^(?P[0-9]+)/vote/$', views.vote, name='vote'), + path('/vote/', views.vote, name='vote'), ] ``` 然后在你的浏览器里转到 “/polls/34/”,Django 将会运行 **detail()** 方法并展示你在 URL 里提供的问题 ID。再试试 “/polls/34/results” 和 “/polls/34/vote/” —— 你将会看到暂时用于占位的结果和投票页。 - 当某人请求你网站的某一页面时——比如说,“/polls/34/”,Django 将会载入 **mysite.urls** 模块,因为配置项 [**ROOT_URLCONF**][ROOT_URLCONF] 说要载入它。然后 Django 寻找名为 **urlpatterns** 变量并且按序遍历正则表达式。Django 找到匹配的正则表达式 **'^polls/'** -然后 Django 将会去除被匹配的部分(**polls/**),然后发送剩下的文本 —— **“34/”** —— 给 “polls.urls” 这个 URLconf 做进一步处理。然后找到匹配的正则表达式 **r'^(?P[0-9]+)/$'**,随后用以下方式调用 **detail()** 函数: +当某人请求你网站的某一页面时——比如说,“/polls/34/”,Django 将会载入 **mysite.urls** 模块,因为配置项 [**ROOT_URLCONF**][ROOT_URLCONF] 说要载入它。然后 Django 寻找名为 **urlpatterns** 变量并且按序遍历模式。Django 找到匹配的模式 **'polls/'**。然后 Django 将会去除被匹配的部分(**"polls/"**),然后发送剩下的文本 —— **“34/”** —— 给 “polls.urls” 这个 URLconf 做进一步处理。然后找到匹配的 **'\/'**,随后用以下方式调用 **detail()** 函数: ```python detail(request=, question_id='34') ``` -**question_id='34'** 这一部分是由 **(?P[0-9+])** 产生的。使用括号来包围一部分模式,就可以“捕获”这部分所匹配到的文本,随后作为参数被传递给视图函数;**?P** 用于定义匹配部分的名字;**[0-9]+** 是用于匹配一连串数字(也就是所有整数)的正则表达式。 +**question_id='34'** 这一部分是由 **\** 产生的。使用括号来包围一部分模式,就可以“捕获”这部分所匹配到的文本,随后作为参数被传递给视图函数;**:question_id>** 用于定义匹配部分的名字;**[0-9]+)/$', views.detail, name='detail'), ```python ... # 增加 specifics -url(r'^specifics/(?P[0-9]+)/$', views.detail, name='detail'), +path('specifics//', views.detail, name='detail'), ... ``` @@ -311,16 +310,16 @@ url(r'^specifics/(?P[0-9]+)/$', views.detail, name='detail'), ```python # polls/urls.py -from django.conf.urls import url +from django.urls import path from . import views app_name = 'polls' urlpatterns = [ - url(r'^$', views.index, name='index'), - url(r'^(?P[0-9]+)/$', views.detail, name='detail'), - url(r'^(?P[0-9]+)/results/$', views.results, name='results'), - url(r'^(?P[0-9]+)/vote/$', views.vote, name='vote'), + path('', views.index, name='index'), + path('/', views.detail, name='detail'), + path('/results/', views.results, name='results'), + path('/vote/', views.vote, name='vote'), ] ``` @@ -344,20 +343,20 @@ urlpatterns = [ 当你弄懂如何编写视图之后,就可以去看教程的 [教程第四部分(zh)](part4.md),来学习关于表单处理和视图类的相关内容。 -[ROOT_URLCONF]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-ROOT_URLCONF -[TEMPLATES]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-TEMPLATES -[APP_DIRS]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-TEMPLATES-APP_DIRS -[INSTALLED_APPS]:https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-INSTALLED_APPS -[templates]: https://docs.djangoproject.com/en/1.11/topics/templates/ -[module-django.urls]: https://docs.djangoproject.com/en/1.11/ref/urlresolvers/#module-django.urls -[filter]: https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.filter -[templatetag-for]: https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-for -[render]: https://docs.djangoproject.com/en/1.11/topics/http/shortcuts/#django.shortcuts.render -[HttpResponse]: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpResponse -[HttpRequest]: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpRequest -[Http404]: https://docs.djangoproject.com/en/1.11/topics/http/views/#django.http.Http404 -[ObjectDoesNotExist]: https://docs.djangoproject.com/en/1.11/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist -[get_object_or_404]: https://docs.djangoproject.com/en/1.11/topics/http/shortcuts/#django.shortcuts.get_object_or_404 -[get_list_or_404]: https://docs.djangoproject.com/en/1.11/topics/http/shortcuts/#django.shortcuts.get_list_or_404 -[django.shortcuts]: https://docs.djangoproject.com/en/1.11/topics/http/shortcuts/#module-django.shortcuts -[get]: https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.get \ No newline at end of file +[ROOT_URLCONF]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-ROOT_URLCONF +[TEMPLATES]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-TEMPLATES +[APP_DIRS]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-TEMPLATES-APP_DIRS +[INSTALLED_APPS]:https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-INSTALLED_APPS +[templates]: https://docs.djangoproject.com/en/2.0/topics/templates/ +[module-django.urls]: https://docs.djangoproject.com/en/2.0/topics/http/urls/ +[filter]: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#django.db.models.query.QuerySet.filter +[templatetag-for]: https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#std:templatetag-for +[render]: https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#django.shortcuts.render +[HttpResponse]: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpResponse +[HttpRequest]: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpRequest +[Http404]: https://docs.djangoproject.com/en/2.0/topics/http/views/#django.http.Http404 +[ObjectDoesNotExist]: https://docs.djangoproject.com/en/2.0/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist +[get_object_or_404]: https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#django.shortcuts.get_object_or_404 +[get_list_or_404]: https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#django.shortcuts.get_list_or_404 +[django.shortcuts]: https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#module-django.shortcuts +[get]: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#django.db.models.query.QuerySet.get diff --git a/docs/part4.md b/docs/part4.md index 2ee0667..6db95e6 100644 --- a/docs/part4.md +++ b/docs/part4.md @@ -6,7 +6,7 @@ ## 编写一个简单的表单 -让我们更新一下在上一个教程中编写的投票详细页面的模板(“polls/detail.html”),让它包含一个 HTML <**form**>元素: +让我们更新一下在上一个教程中编写的投票详细页面的模板(“polls/detail.html”),让它包含一个 HTML **\**元素: @@ -41,7 +41,7 @@ ``` python3 # polls/urls.py -url(r'^(?P[0-9]+)/vote/$', views.vote, name='vote'), +path('/vote/', views.vote, name='vote'), ``` 我们还创建了一个 **vote()** 函数的虚拟实现。让我们来创建一个真实的版本。 将下面的代码添加到 **polls/views.py**: @@ -75,19 +75,21 @@ def vote(request, question_id): 以上代码中有些内容还未在本教程中提到过: - [**request.POST**][POST] 是一个类字典对象,让你可以通过关键字的名字获取提交的数据。 这个例子中,**request.POST['choice']** 以字符串形式返回选择的 Choice 的 ID。[**request.POST**][POST] 的值永远是字符串。 -注意,Django 还以同样的方式提供 [**request.GET**][GET] 用于访问 GET 数据 —— 但我们在代码中显式地使用 [**request.POST**][POST] ,以保证数据只能通过POST调用改动。 - + + 注意,Django 还以同样的方式提供 [**request.GET**][GET] 用于访问 GET 数据 —— 但我们在代码中显式地使用 [**request.POST**][POST] ,以保证数据只能通过POST调用改动。 + - 如果在 POST 数据中没有提供 **choice**,**request.POST['choice']** 将引发一个 [**KeyError**][KeyError]。上面的代码检查 [**KeyError**][KeyError],如果没有给出 **choice** 将重新显示Question表单和一个错误信息。 - + - 在增加Choice的得票数之后,代码返回一个 [**HttpResponseRedirect**][HttpResponseRedirect] 而不是常用的 [**HttpResponse**][HttpResponse]。[**HttpResponseRedirect**][HttpResponseRedirect] 只接收一个参数:用户将要被重定向的 URL(请继续看下去,我们将会解释如何构造这个例子中的 URL)。 -正如上面的Python注释指出的,你应该在成功处理 POST 数据后总是返回一个 [**HttpResponseRedirect**][HttpResponseRedirect]。 这不是 Django 的特定技巧;这是那些优秀网站在开发实践中形成的共识。 - + + 正如上面的Python注释指出的,你应该在成功处理 POST 数据后总是返回一个 [**HttpResponseRedirect**][HttpResponseRedirect]。 这不是 Django 的特定技巧;这是那些优秀网站在开发实践中形成的共识。 + - 在这个例子中,我们在 [**HttpResponseRedirect**][HttpResponseRedirect] 的构造函数中使用 [**reverse()**][reverse] 函数。这个函数避免了我们在视图函数中硬编码 URL。它需要我们给出我们想要跳转的视图的名字和该视图所对应的URL模式中需要给该视图提供的参数。 在本例中,使用在 [第三部分(zh)](part3.md)中设定的URLconf, [**reverse()**][reverse] 调用将返回一个这样的字符串: - + ``` '/polls/3/results/' ``` - + 其中 **3** 是 **question.id** 的值。重定向的 URL 将调用 **'results'** 视图来显示最终的页面。 正如在 [第三部分(zh)](part3.md)中提到的,**request** 是一个 [**HttpRequest**][HttpRequest] 对象。更多关于 [**HttpRequest**][HttpRequest] 对象的内容,请参见 [*请求和响应的文档*][request-response]。 @@ -124,10 +126,10 @@ def results(request, question_id): 现在,在你的浏览器中访问 **/polls/1/** 然后为 Question 投票。你应该看到一个投票结果页面,并且在你每次投票之后都会更新。 如果你提交时没有选择任何Choice,你应该看到错误信息。 -> **注意** -> -> 我们的 **vote()** 视图代码有点小问题。它首先从数据库中得到 **selected_choice** 对象,然后计算新的票数(**votes**),最后把新的票数存回数据库中。但如果两个用户几乎在同一时间在我们的网站上投票就会出现错误:同票数,比如说 42 张票。然后两个用户计算和保存的票数都会是 43,而不是我们期待的 44。 -> +> **注意** +> +> 我们的 **vote()** 视图代码有点小问题。它首先从数据库中得到 **selected_choice** 对象,然后计算新的票数(**votes**),最后把新的票数存回数据库中。但如果两个用户几乎在同一时间在我们的网站上投票就会出现错误:同票数,比如说 42 张票。然后两个用户计算和保存的票数都会是 43,而不是我们期待的 44。 +> > 这就是*竞争条件(race condition)*,如果你感兴趣,可以阅读 [使用 F() 来避免竞争条件][avoiding-race-conditions-using-f],学一下如何解决这个问题。 ## 使用通用视图:代码还是少点好 @@ -147,9 +149,9 @@ def results(request, question_id): 请继续阅读来了解详细信息。 > **为什么要重构代码?** -> +> > 一般来说,当编写一个 Django 应用时,你应该先评估一下通用视图是否可以解决你的问题,你应该在一开始使用它,而不是进行到一半时重构代码。本教程目前为止是有意将重点放在以“艰难的方式”编写视图,这是为将重点放在核心概念上。 -> +> > 就像在使用计算器之前你需要知道基本的数学一样。 ### **改良 URLconf** @@ -159,20 +161,20 @@ def results(request, question_id): ``` python3 # polls/urls.py -from django.conf.urls import url +from django.urls import path from . import views app_name = 'polls' urlpatterns = [ - url(r'^$', views.IndexView.as_view(), name='index'), - url(r'^(?P[0-9]+)/$', views.DetailView.as_view(), name='detail'), - url(r'^(?P[0-9]+)/results/$', views.ResultsView.as_view(), name='results'), - url(r'^(?P[0-9]+)/vote/$', views.vote, name='vote'), + path('', views.IndexView.as_view(), name='index'), + path('/', views.DetailView.as_view(), name='detail'), + path('/results/', views.ResultsView.as_view(), name='results'), + path('/vote/', views.vote, name='vote'), ] ``` -注意在第二个和第三个模式的正则表达式中,匹配的模式的名字由 **<question_id>**  变成 **<pk>**。 +注意在第二个和第三个模式中,匹配的模式的名字由 **\**  变成 **\**。 ### **改良视图** @@ -225,21 +227,21 @@ def vote(request, question_id): 启动服务器,使用一下基于通用视图的新投票应用。 -更多关于通用视图的详细信息,请查看 [通用视图的文档](https://docs.djangoproject.com/en/1.11/topics/class-based-views/)。 +更多关于通用视图的详细信息,请查看 [通用视图的文档](https://docs.djangoproject.com/en/2.0/topics/class-based-views/)。 当你明白了这些表单和通用视图后,可以继续阅读 [教程第五部分(zh)](part5.md) 来了解如何测试我们的投票应用。 -[request-response]: https://docs.djangoproject.com/en/1.11/ref/request-response/ -[for]: https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-for -[csrf_token]: https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-csrf_token -[POST]: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpRequest.POST -[GET]: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpRequest.GET +[request-response]: https://docs.djangoproject.com/en/2.0/ref/request-response/ +[for]: https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#std:templatetag-for +[csrf_token]: https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#std:templatetag-csrf_token +[POST]: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpRequest.POST +[GET]: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpRequest.GET [KeyError]: https://docs.python.org/3/library/exceptions.html#KeyError -[HttpResponseRedirect]: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpResponseRedirect -[HttpResponse]: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpResponse -[HttpRequest]: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpRequest -[reverse]: https://docs.djangoproject.com/en/1.11/ref/urlresolvers/#django.urls.reverse -[ListView]: https://docs.djangoproject.com/en/1.11/ref/class-based-views/generic-display/#django.views.generic.list.ListView -[DetailView]: https://docs.djangoproject.com/en/1.11/ref/class-based-views/generic-display/#django.views.generic.detail.DetailView -[avoiding-race-conditions-using-f]: https://docs.djangoproject.com/en/1.11/ref/models/expressions/#avoiding-race-conditions-using-f \ No newline at end of file +[HttpResponseRedirect]: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpResponseRedirect +[HttpResponse]: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpResponse +[HttpRequest]: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpRequest +[reverse]: https://docs.djangoproject.com/en/2.0/ref/urlresolvers/#django.urls.reverse +[ListView]: https://docs.djangoproject.com/en/2.0/ref/class-based-views/generic-display/#django.views.generic.list.ListView +[DetailView]: https://docs.djangoproject.com/en/2.0/ref/class-based-views/generic-display/#django.views.generic.detail.DetailView +[avoiding-race-conditions-using-f]: https://docs.djangoproject.com/en/2.0/ref/models/expressions/#avoiding-race-conditions-using-f diff --git a/docs/part5.md b/docs/part5.md index 04acb07..14543ff 100644 --- a/docs/part5.md +++ b/docs/part5.md @@ -146,7 +146,7 @@ Destroying test database for alias 'default'... 发生了什么呢?以下是自动化测试的运行过程: -- **`python manage.py test polls`** 将会寻找 **poll** 应用里的测试代码 +- **python manage.py test polls** 将会寻找 **poll** 应用里的测试代码 - 它找到了一个 [**django.test.TestCase**][TestCase] 的子类 - 它创建一个特殊的数据库供测试使用 - 它在类中寻找测试方法——以 **test** 开头的方法。 @@ -253,10 +253,8 @@ Django 提供了一个供测试使用的 [**Client**][Client] 来模拟用户和 >>> # 获取 '/' 的响应 >>> response = client.get('/') Not Found: / ->>> # 我们期望返回一个 404; -如果你看到一个 ->>> # “无效 HTTP_HOST_header” 错误 和 一个 -400 响应,那你可能 +>>> # 我们期望返回一个 404;如果你看到一个 +>>> # “无效 HTTP_HOST_header” 错误 和 一个 400 响应,那你可能 >>> # 忘记了这之前要调用的 setup_test_environment() >>> response.status_code 404 @@ -304,7 +302,9 @@ from django.utils import timezone # polls/views.py def get_queryset(self): - """返回最近发布的五个投票(不包括那些被设置为在将来发布的)""" + """ + 返回最近发布的五个投票(不包括那些被设置为在将来发布的) + """ return Question.objects.filter( pub_date__lte=timezone.now() ).order_by('-pub_date')[:5] @@ -489,13 +489,13 @@ class QuestionDetailViewTests(TestCase): 当你已经比较熟悉该如何测试 Django 的视图之后,就可以继续于读 [教程第六部分(zh)](part6.md),来学习关于静态文件管理的相关知识。 -[topics-testing-code-coverage]: https://docs.djangoproject.com/en/1.11/topics/testing/advanced/#topics-testing-code-coverage -[shell]: https://docs.djangoproject.com/en/1.11/ref/django-admin/#django-admin-shell -[TestCase]: https://docs.djangoproject.com/en/1.11/topics/testing/tools/#django.test.TestCase -[Client]: https://docs.djangoproject.com/en/1.11/topics/testing/tools/#django.test.Client -[setup_test_environment]: https://docs.djangoproject.com/en/1.11/topics/testing/advanced/#django.test.utils.setup_test_environment -[assertContains]: https://docs.djangoproject.com/en/1.11/topics/testing/tools/#django.test.SimpleTestCase.assertContains -[assertQuerysetEqual]: https://docs.djangoproject.com/en/1.11/topics/testing/tools/#django.test.TransactionTestCase.assertQuerysetEqual -[LiveServerTestCase]: https://docs.djangoproject.com/en/1.11/topics/testing/tools/#django.test.LiveServerTestCase -[ListView]: https://docs.djangoproject.com/en/1.11/ref/class-based-views/generic-display/#django.views.generic.list.ListView -[testing]: https://docs.djangoproject.com/en/1.11/topics/testing/ \ No newline at end of file +[topics-testing-code-coverage]: https://docs.djangoproject.com/en/2.0/topics/testing/advanced/#topics-testing-code-coverage +[shell]: https://docs.djangoproject.com/en/2.0/ref/django-admin/#django-admin-shell +[TestCase]: https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.TestCase +[Client]: https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.Client +[setup_test_environment]: https://docs.djangoproject.com/en/2.0/topics/testing/advanced/#django.test.utils.setup_test_environment +[assertContains]: https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.SimpleTestCase.assertContains +[assertQuerysetEqual]: https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.TransactionTestCase.assertQuerysetEqual +[LiveServerTestCase]: https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.LiveServerTestCase +[ListView]: https://docs.djangoproject.com/en/2.0/ref/class-based-views/generic-display/#django.views.generic.list.ListView +[testing]: https://docs.djangoproject.com/en/2.0/topics/testing/ diff --git a/docs/part6.md b/docs/part6.md index 0c89550..1690380 100644 --- a/docs/part6.md +++ b/docs/part6.md @@ -69,10 +69,10 @@ body { 如果你明白静态文件,来阅读 [教程第七部分(zh)](part7.md) 学习如何自定义 Django 自动生成的管理站点吧! -[STATICFILES_FINDERS]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-STATICFILES_FINDERS -[INSTALLED_APPS]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-INSTALLED_APPS -[static]: https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-static -[STATIC_URL]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-STATIC_URL -[static-files]: https://docs.djangoproject.com/en/1.11/howto/static-files/ -[staticfiles]: https://docs.djangoproject.com/en/1.11/ref/contrib/staticfiles/ -[deployment]: https://docs.djangoproject.com/en/1.11/howto/static-files/deployment/ \ No newline at end of file +[STATICFILES_FINDERS]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-STATICFILES_FINDERS +[INSTALLED_APPS]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-INSTALLED_APPS +[static]: https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#std:templatetag-static +[STATIC_URL]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-STATIC_URL +[static-files]: https://docs.djangoproject.com/en/2.0/howto/static-files/ +[staticfiles]: https://docs.djangoproject.com/en/2.0/ref/contrib/staticfiles/ +[deployment]: https://docs.djangoproject.com/en/2.0/howto/static-files/deployment/ diff --git a/docs/part7.md b/docs/part7.md index 717dea8..fcaf1f1 100644 --- a/docs/part7.md +++ b/docs/part7.md @@ -112,7 +112,7 @@ admin.site.register(Question, QuestionAdmin) ![Add question page now has choices on it](img/admin11t.png) -现在有3个可以添加相关选项(choices)的单元 —— “3” 是由代码里的 extra 所规定的——并且当你每次进入一个已经创建好的对象的修改界面时,总会多出另外三个单元让你可以添加选项。 +原理是这样的:现在有3个可以添加相关选项(choices)的单元 —— “3” 是由代码里的 extra 所规定的——并且当你每次进入一个已经创建好的对象的修改界面时,总会多出另外三个单元让你可以添加选项。 在三个添加选项单元的最下方有一个“添加一个选项(Choices)”按钮。但你点击它时,上方会增加一个添加选项的单元。如果你想删掉添加的单元,可以点击单元右上角的X。请注意:你无法移除初始的那三个单元。下面这张图片显示新增了一个单元的效果: @@ -289,17 +289,17 @@ Django 管理界面的所有默认模板都能够被覆盖。如果你想覆盖 如果你熟悉 Python 的打包机制,并且对如何将投票应用转化为一个 “可重用的应用” 感兴趣,请看 [**高级教程:如何编写可重用的应用**](reusable_app.md)。 -[fieldsets]: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets -[list_display]: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display -[list_filter]: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter -[DateTimeField]: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.DateTimeField -[list_per_page]: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_per_page -[search_fields]: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields -[date_hierarchy]: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.date_hierarchy -[DIRS]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-TEMPLATES-DIRS -[TEMPLATES]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-TEMPLATES -[APP_DIRS]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-TEMPLATES-APP_DIRS -[template-loading]: https://docs.djangoproject.com/en/1.11/topics/templates/#template-loading -[INSTALLED_APPS]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-INSTALLED_APPS -[site_header]: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.AdminSite.site_header -[ForeignKey]: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.ForeignKey \ No newline at end of file +[fieldsets]: https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets +[list_display]: https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display +[list_filter]: https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter +[DateTimeField]: https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.DateTimeField +[list_per_page]: https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_per_page +[search_fields]: https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields +[date_hierarchy]: https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.date_hierarchy +[DIRS]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-TEMPLATES-DIRS +[TEMPLATES]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-TEMPLATES +[APP_DIRS]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-TEMPLATES-APP_DIRS +[template-loading]: https://docs.djangoproject.com/en/2.0/topics/templates/#template-loading +[INSTALLED_APPS]: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-INSTALLED_APPS +[site_header]: https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.AdminSite.site_header +[ForeignKey]: https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey diff --git a/docs/patch.md b/docs/patch.md index 9dac868..20d77e5 100644 --- a/docs/patch.md +++ b/docs/patch.md @@ -8,10 +8,10 @@ ## 本教程的目标对象? -> **参见** -> -> 如果你正在为如何提交补丁,寻找参考指南,可以看[提交补丁](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-code/submitting-patches/)文档。 - +> **参见** +> +> 如果你正在为如何提交补丁,寻找参考指南,可以看[提交补丁](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/submitting-patches/)文档。 + 在本教程里,我们期望你至少能基本明白 Django 是如何工作的。这意味着你应该顺利地读完了[实例教程](index.md)。还有,你应该对 Python 本身也有了很好地了解。如果没有,可以在线看看这本有趣的 [Dive Into Python](www.diveintopython3.net),它面向 Python 新手程序员。 不熟悉版本控制系统和 Trac 的人会发现本教程及其链接的信息刚刚好够开始。但是,如果你打算定期为 Django 做贡献,你可能会想了解更多关于这些不同工具的知识。 @@ -20,455 +20,458 @@ > **哪里可以获得帮助:** > -> 如果你在阅读或实践本教程中遇到困难,请发消息给 [django-users](https://docs.djangoproject.com/en/1.11/internals/mailing-lists/#django-users-mailing-list) 或加入IRC频道 [django on irc.freenode.net](irc://irc.freenode.net/django) 来与其他 Django 用户进行交流,他们也许能帮到你。 - -## 本教程覆盖了哪些知识点 - -首先我们会带你走完为 Django 贡献补丁的流程。在本教程结束时,你应该对工具和流程都有了基本认识。特别地,我们将会介绍以下内容: - -- 安装 Git -- 如何下载 Django 开发版的拷贝 -- 运行 Django 的测试套件 -- 为你的补丁编写测试程序 -- 为你的补丁编写代码 -- 测试你的补丁 -- 提交拉去请求(pull request) -- 哪里可以查看更多信息 - -一旦你完成了本教程,你就可以去看完[为 Django 做贡献文档](https://docs.djangoproject.com/en/1.11/internals/contributing/)的剩下部分了。那里有很多信息,而且是那些想成为定期贡献者必看的。如果你有问题,你也可能在那得到答案。 - -> 需要 **Python 3**! -> -> 本教程假定你用的就是 Python 3。请到[Python 官网下载页](https://www.python.org/downloads/)或者你系统的包管理安装最新版的 Python3。 - -> **给 Windows 用户** -> -> 给 Windows 安装 Python 时,请确保把 python.exe 添加进了 Path,这样可以直接在命令行使用。 - -## 行为准则(Code of Conduct) - -作为一名贡献者,你可以帮助我们保持 Django 社区的开放和包容。请阅读和遵循我们的[行为准则(Code of Conduct)](https://www.djangoproject.com/conduct/) - -## 安装 Git - -本教程里,为了下载到最新的开发版 Django 和生成你修改过的补丁文件,你将需要安装 Git。 - -为了确认是否安装了 Git,你可以在命令行输入 git。如果提示说命令找不到,那你就需要下载安装了,[Git 下载页](https://git-scm.com/download)。 - -> **给 Windows 用户** -> -> 给 Windows 安装 Git 时,请确保给 “Git Bash” 打勾了,这样 Git 可以用它自己的 shell 运行。本教程假定你已经安装了它。 - -如果你对 Git 不太熟悉,你可以在命令行输入 **git help** 获取更多命令信息。 - -## 下载 Django 开发版的拷贝 - -第一步就是得到 Django 源码的拷贝。首先[在 GitHub 里 fork Django项目](https://github.com/django/django/fork)。然后在命令行里,使用 **cd** 切换到你想放 Django 本地拷贝的目录里。 - -使用下面的命令下载 Django 源码仓库: - -```bash -$ git clone git@github.com:YourGitHubName/django.git -``` - -现在你有了一份 Django 的本地拷贝,你可以安装它,就像使用 **pip** 安装其他包那样。最方便的方式是使用 *虚拟环境(virtual environment)*(或者 virtualenv),这是 Python 内置的功能,允许您为每个项目单独设立已安装软件包的目录,让它们不会相互干扰。 - -最好是把你所以的虚拟环境(virtualenvs)都放在一个目录下,比如在你的 home 目录下的 **.virtualenvs/** 目录。如果还没创建: - -```bash -$ mkdir ~/.virtualenvs -``` - -现在,运行以下命令创建新的虚拟环境(virtualenvs): - -```bash -$ python3 -m venv ~/.virtualenvs/djangodev -``` - -路径就是新的虚拟环境,而它会被保存在你的电脑里。 - -> **给 Windows 用户** -> -> 如果你在 Windows 下使用 Git 的 shell,使用内置的 **venv** 模块会无效,由于启动脚本是为系统 shell 创建的(**.bat**)和 PowerShell (**.ps1**)。使用 **virtualenv** 包代替 venv 模块: -> ```bash -> $ pip install virtualenv -> $ virtualenv ~/.virtualenvs/djangodev -> ``` - -> **给 Ubuntu 用户** -> -> 在一些 Ubuntu 版本中,上面的命令可能会失败。 -> -> 使用 **virtualenv** 包代替,确认你安装了 pip3: -> ```bash -> $ sudo apt-get install python3-pip -> # 如果下面的命令因为没有权限出错了,那就加上 sudo -> $ pip3 install virtualenv -> $ virtualenv --python=`which python3` ~/.virtualenvs/djangodev -> ``` - -最后一步就是让你的虚拟环境(virtualenvs)生效: - -```bash -$ source ~/.virtualenvs/djangodev/bin/activate -``` - -如果 **source** 命令无效,你可以试试用一个 “点” 代替: - -```bash -$ . ~/.virtualenvs/djangodev/bin/activate -``` - -> **给 Windows 用户** -> -> 为了在 Windows 上让虚拟环境(virtualenvs)生效,运行下面命令: -> ```bash -> $ source ~/virtualenvs/djangodev/Scripts/activate -> ``` - -无论什么时候,当你打开一个新的终端窗口时都要激活一下虚拟环境(virtualenvs)。为了方便这种情况,[virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/) 是很有用的工具。 - -从现在开始,你用 **pip** 安装的任何东西都会被安装进你新的的虚拟环境(virtualenvs),这会隔绝其他环境和系统包。同样,当前激活的虚拟环境(virtualenvs)的名字会在命令行前面显示,这可以让你知道使用的是哪个虚拟环境。 - -继续,安装之前克隆(clone)下来的 Django 拷贝: - -```bash -$ pip install -e /path/to/your/local/clone/django/ -``` - -安装的 Django 现在指向了你的本地拷贝。你将马上看到你做的任何修改,这对你编写第一个补丁很有帮助。 - -## 回滚到先前的 Django 版本 - -本教程里,我们将会使用 [#24788](https://code.djangoproject.com/ticket/24788) 任务(ticket)作为例子学习,所以在任务(ticket)补丁应用之前,我们将回退到 Django 的历史版本。这让我们可以走完所有步骤,包括从头开始写编写补丁,还有运行 Django 的测试套件。 - -**请记住,为了下面的教程,我们将使用 Django 的主干旧版本,当你编写自己的补丁时,你应该始终使用 Django 当前的开发版本** - -> **注意** -> -> 这个任务(ticket)的补丁已经由 Paweł Marczewski 编写了,也已经被应用到了 Django 中,[commit 4df7e8483b2679fc1cba3410f08960bac6f51115](https://github.com/django/django/commit/4df7e8483b2679fc1cba3410f08960bac6f51115)。因此,我们将使用的是 Django 先前的版本,[commit 4ccfc4439a7add24f8db4ef3960d02ef8ae09887](https://github.com/django/django/commit/4ccfc4439a7add24f8db4ef3960d02ef8ae09887)。 - -切换到 Django 的根目录(那里有 **django,docs,tests,AUTHORS** 等),你可以检查下这个旧版本 Django: - -```bash -$ git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887 -``` - -## 第一次运行 Django 的测试套件 - -这非常重要:当向 Django 贡献时,你修改的代码不会将 bug 引到 Django 的其他地方。一个检查 Django 还可以工作的方法就是在你修改之后运行 Django 的测试套件。如果所有的测试都通过了,你就有理由确认你的修改不会完全破坏 Django。如果你之前没有运行过测试套件,最好在这之前运行熟悉下它的输出大概是什么样的。 - -在运行之前,**cd** 切换到 Django 的 **tests/** 目录,安装它的依赖关系: - -```bash -$ pip install -r requirements/py3.txt -``` - -如果你在安装过程中遇到错误,你的系统可能有一些 Python 包的依赖关系缺失。查询一下安装失败的包的文档,或者在网上搜索一下你遇到的这个错误信息。 - -现在我们准备好运行测试套件了。如果你用的是 GNU/Linux,macOS 或者其他 Unix 风格的系统,运行: - -```bash -$ ./runtests.py -``` - -现在坐下来歇会。Django 整个测试套件有超过 9,600 个不同的测试,所以需要 5 到 15 分钟的时间运行,这取决于你电脑的运算速度。 - -当 Django 测试套件运行的时候,你可以看到一段显示着每个正在运行的测试的状态的字符流。**E** 表示在测试期间抛出了一个错误,**F** 表示一个测试的断言(assertions)失败。这两者都可以被认为测试失败了。同时,**x** 和 **s** 分别表示预期失败和跳过测试。“点” 表示通过测试。 - -跳过测试是由于运行测试时缺少额外的库;可以查看[运行所有测试](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-code/unit-tests/#running-unit-tests-dependencies)所需要的一系列的依赖,确认和你修改的相关的一些测试依赖被安装了(本教程我们什么都不需要安装)。一些测试是特别针对特别的后台数据库的,如果不对那后台进行测试,就会被跳过。SQLite 是默认设置的后台数据库。如果是使用其他的后台数据库,为了运行测试,可以查看[使用其他设置模块](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-code/unit-tests/#running-unit-tests-settings)。 - -一旦测试完毕,你应该会收到一条关于测试套件是通过了还是失败了的信息。由于你还没有对 Django 的代码做修改,所以整个测试套件 **应该是(should)** 通过。如果你得到的是失败或者错误的信息,请确保你之前走的所有步骤都正确。可以查看[运行单元测试](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-code/unit-tests/#running-unit-tests)获得更多信息。如果你正在使用 Python3.5+,将会有些与不被赞成的警告(deprecation warnings)相关的失败,那些你可以忽略掉。这些失败已经被 Django 修复了。 - -注意,最新的 Django 主干(trunk)版本不总是稳定的。当开发版本遇上主干(trunk)版本,你可以检查 [Django 的持续集成构建(Django’s continuous integration builds)](https://djangoci.com)来确认是特别针对你的机器的失败还是已经存在在 Django 官方构建里的。如果你点击去看一个特别的构建,你可以看到 “配置矩阵(Configuration Matrix)” 显示着由于 Python 版本和数据库后台问题出现的失败。 - -> **注意** -> -> 在本教程和我们正在工作的任务(ticket)里,测试使用 SQLite 足够了,但是,[运行其他数据库进行测试](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-code/unit-tests/#running-unit-tests-settings)是可能的(有时是必要的)。 - -## 为你的补丁创建一个分支 - -在修改之前,先为任务(ticket)创建一个新的分支: - -```bash -$ git checkout -b ticket_24788 -``` - -你可以为这个分支选择任何你想要的名字,例如 “ticket_24788”。在这个分支做的所有修改都是特别针对这个任务的,并不会影响到我们先前克隆下来的主要代码。 - -## 为你的任务编写一些测试 - -在大多数情况下,被 Django 接收进来的补丁都必须测试过。针对 bug 修复的补丁,这意味着要编写回退测试代码来确保 bug 不会在 Django 的后续版本再次出现。一个回退测试应该是这样的:当 bug 存在时会失败,当 bug 被修复时会通过。对于包含新特性的补丁,你的测试需要能保证新特性是能正常工作的。当新特性不存在是,它们也是要失败的;一旦被应用又是能通过的。 - -一个好方法是:首先在修改代码前,编写你的新测试。这种开发风格被称为[测试驱动开发(test-driven development)](https://en.wikipedia.org/wiki/Test-driven_development),而且既能被应用在整个项目又能应用在单个补丁。在编写完你的测试后,运行一下它们以确保它们确实是失败的(由于你还没有修复 bug 或者新增特性)。如果你的新测试没有失败,你就需要修复一下这些测试好让它们失败。毕竟,无论 bug 是否存在,回退测试都会通过,那在防止 bug 重新出现这事上是非常没有帮助的。 - -现在让我们动手做下实例。 - -## 为任务 #24788 编写一些测试 - -任务 #24788 推荐加点小功能:给 Form 类指定类级别(class level)属性加 **前缀(prefix)** 的功能: - -```text -[…] forms which ship with apps could effectively namespace themselves such -that N overlapping form fields could be POSTed at once and resolved to the -correct form. -``` - -为了解决这个任务,我们将会为 **BaseForm** 类添加前缀属性。当实例化这个类时,传递一个前缀给 **__init__()** 方法也将会给被创建的实例设置这个前缀。但如果不传递一个前缀(或者传递 **None**)将会使用类级别的前缀。在我们修改之前,我们准备编写一些测试来验证我们的修改函数没有问题,在将来也没有问题。 - -切换到 Django 的 **tests/forms_tests/tests/** 文件夹里,打开 **test_forms.py** 文件。在第 1674 行 **test_forms_with_null_boolean** 函数之前添加以下代码: - -```python -# tests/forms_tests/tests/test_forms.py - -def test_class_prefix(self): - # 前缀也可以在类级别指定 - class Person(Form): - first_name = CharField() - prefix = 'foo' - - p = Person() - self.assertEqual(p.prefix, 'foo') - - p = Person(prefix='bar') - self.assertEqual(p.prefix, 'bar') -``` - -这个新的测试:检查 [设置一个类级别前缀] 是否如预期一样地工作;并且在创建实例时传递一个 **前缀(prefix)** 参数,看是否也工作。 - -> **但这个测试的东西看起来特别困难……** -> -> 如果你之前从来没有接触过测试,第一眼看上去它们确实有点难。幸运的是,测试在电脑编程中是个 *非常* 大的主题,有很多出自这里的信息: -> - [编写和运行测试文档](https://docs.djangoproject.com/en/1.11/topics/testing/overview/) —— 给 Django 编写测试。 -> - [单元测试介绍](www.diveintopython3.net/unit-testing.html) —— 《深入 Python》(一本给 Python 初学开发者的免费的在线书籍)。 -> - 在阅读完这些后,如果你求知若渴,还可以看看 Python 的官方文档 [**unittest**](https://docs.python.org/3/library/unittest.html#module-unittest)。 - -## 运行你写的新测试 - -请记住,我们实际上没有对 **BaseForm** 做任何修改,所以我们的测试应该会失败。为了确保失败真的出现了,让我们在 **forms_tests** 文件夹运行一下所有的测试。从命令行里, **cd** 切换进 Django 的 **tests/** 目录里,然后运行: - -```bash -$ ./runtests.py forms_tests -``` - -如果测试运行正常,你应该看到和我们添加的测试方法相对应的一个失败。如果所有的测试都通过了,那么你要确认一下你已经将新测试添加到了上面合适的文件夹和类里。 - -## 为你的任务编写代码 - -下一步我们将给任务(ticket)[#24788](https://code.djangoproject.com/ticket/24788) 添加函数描述。 - -### **为任务 #24788 编写代码** - -切换到 **django/django/forms/** 文件夹里,然后打开 **forms.py**。在第 72 行找到 **BaseForm** 类,然后在 **field_order** 属性之后添加 **前缀(prefix)** 类属性: - -```python -class BaseForm(object): - # This is the main implementation of all the Form logic. Note that this - # class is different than Form. See the comments by the Form class for - # more information. Any improvements to the form API should be made to - # *this* class, not to the Form class. - field_order = None - prefix = None -``` - -### **现在验证并通过你的测试** - -一旦你修改完了 Django,我们就需要确保之前写的测试都能通过,这样我们才能看到我们上面写的代码正确工作。为了在 **forms_tests** 文件夹运行测试, **cd** 切换到 Django 的 **tests/** 目录里,然后运行: - -```bash -$ ./runtests.py forms_tests -``` - -噢,我们写的那些测试好了!您应该仍然会看到一个例外失败: - -```bash -AssertionError: None != 'foo' -``` - -我们忘记在 **__init__()** 方法里添加条件语句了。在 **django/forms/forms.py** 现在的第 87 行修改 **self.prefix = prefix**,添加一个条件语句: - -```python -if prefix is not None: - self.prefix = prefix -``` - -重新运行测试,所有都应该是通过的。如果不是,请确认你像上面那样正确修改了 **BaseForm** 类,然后正确复制了新的测试。 - -## 第二次运行 Django 的测试套件 - -一旦你验证了你的补丁,你的测试都是正常工作的,运行整个测试套件是个好主意,去验证你的修改没有将 bug 引入到 Django 的其他地方。整个测试套件成功通过并不保证你的代码是没有 bug 的,尽管它确实有助于识别很多可能被忽视的错误和回退。 - -为了运行整个 Django 测试套件, **cd** 切换到 Django 的 **tests/** 目录里,然后运行: - -```bash -$ ./runtests.py -``` - -只要你没看到任何失败,你就可以继续了。 - -## 写文档 - -这是个新功能,所以应该被记录到文档里。在 **django/docs/ref/forms/api.txt** 的第 1068 行(文件结尾)添加: - -```text -The prefix can also be specified on the form class:: - - >>> class PersonForm(forms.Form): - ... ... - ... prefix = 'person' - -.. versionadded:: 1.9 - - The ability to specify ``prefix`` on the form class was added. -``` - -由于这个新功能将被添加到还没发布的 Django 1.9 版本的发行说明中,在 **docs/releases/1.9.txt** 文件的 164 行的 “Forms” 部分: - -```text -* A form prefix can be specified inside a form class, not only when - instantiating a form. See :ref:`form-prefix` for details. -``` - -有关编写文档的更多信息,包括有关 **versionadded** 的说明,可以查看[文档编写](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-documentation/)。文章还包括了一篇关于怎样在本地建立文档副本的说明,好让你可以预览将被生成的 HTML。 - -## 预览你的修改 - -现在是时候看看我们的补丁做的所有修改了。为了显示你当前的 Django 副本(有你的修改)和你最初的版本: - -```bash -$ git diff -``` - -使用箭头键上下移动。 - -```git -diff --git a/django/forms/forms.py b/django/forms/forms.py -index 509709f..d1370de 100644 ---- a/django/forms/forms.py -+++ b/django/forms/forms.py -@@ -75,6 +75,7 @@ class BaseForm(object): - # information. Any improvements to the form API should be made to *this* - # class, not to the Form class. - field_order = None -+ prefix = None - - def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, - initial=None, error_class=ErrorList, label_suffix=None, -@@ -83,7 +84,8 @@ class BaseForm(object): - self.data = data or {} - self.files = files or {} - self.auto_id = auto_id -- self.prefix = prefix -+ if prefix is not None: -+ self.prefix = prefix - self.initial = initial or {} - self.error_class = error_class - # Translators: This is the default suffix added to form field labels -diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt -index 3bc39cd..008170d 100644 ---- a/docs/ref/forms/api.txt -+++ b/docs/ref/forms/api.txt -@@ -1065,3 +1065,13 @@ You can put several Django forms inside one ``
`` tag. To give each - >>> print(father.as_ul()) -
  • -
  • -+ -+The prefix can also be specified on the form class:: -+ -+ >>> class PersonForm(forms.Form): -+ ... ... -+ ... prefix = 'person' -+ -+.. versionadded:: 1.9 -+ -+ The ability to specify ``prefix`` on the form class was added. -diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt -index 5b58f79..f9bb9de 100644 ---- a/docs/releases/1.9.txt -+++ b/docs/releases/1.9.txt -@@ -161,6 +161,9 @@ Forms - :attr:`~django.forms.Form.field_order` attribute, the ``field_order`` - constructor argument , or the :meth:`~django.forms.Form.order_fields` method. - -+* A form prefix can be specified inside a form class, not only when -+ instantiating a form. See :ref:`form-prefix` for details. -+ - Generic Views - ^^^^^^^^^^^^^ - -diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py -index 690f205..e07fae2 100644 ---- a/tests/forms_tests/tests/test_forms.py -+++ b/tests/forms_tests/tests/test_forms.py -@@ -1671,6 +1671,18 @@ class FormsTestCase(SimpleTestCase): - self.assertEqual(p.cleaned_data['last_name'], 'Lennon') - self.assertEqual(p.cleaned_data['birthday'], datetime.date(1940, 10, 9)) - -+ def test_class_prefix(self): -+ # Prefix can be also specified at the class level. -+ class Person(Form): -+ first_name = CharField() -+ prefix = 'foo' -+ -+ p = Person() -+ self.assertEqual(p.prefix, 'foo') -+ -+ p = Person(prefix='bar') -+ self.assertEqual(p.prefix, 'bar') -+ - def test_forms_with_null_boolean(self): - # NullBooleanField is a bit of a special case because its presentation (widget) - # is different than its data. This is handled transparently, though. -``` - -当你预览完了这个补丁,按下 **q** 键回到命令行。如果补丁的内容看起来是对的,那就是时候 提交(commit) 这些修改了。 - -## 提交补丁中的修改 - -为了提交修改: - -```bash -$ git commit -a -``` - -为了输入提交的信息,会打开一个文本编辑器。根据[提交信息准则](https://docs.djangoproject.com/en/1.11/internals/contributing/committing-code/#committing-guidelines),写下像这样的信息: - -```text -Fixed #24788 -- Allowed Forms to specify a prefix at the class level. -``` - -## 推送提交和拉取请求(pull request) - -在提交了补丁之后,把它发送到在 GitHub 上你 fork 下来的仓库(如果不一样,就把 “ticket_24788”替换为分支的名字): - -```bash -$ git push origin ticket_24788 -``` - -通过 [Django 的 GitHub 页面](https://github.com/django/django/),你可以创建一个拉取请求。你会看到你的分支在 “你最近推送的分支(Your recently pushed branches)” 里。点击旁边的 “对比和拉取请求(Compare & pull request)”。 - -但在本教程里,请别那么做,在下一页里显示预览补丁,你可以点击 “创建拉取请求(Create pull request)” - -## 下一步 - -恭喜你,你已经学会了如何给 Django 创建拉取请求了!更多进阶技术细节你可以看[用 Git 和 GitHub 工作](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-code/working-with-git/)。 - -现在,你可以通过帮助改进 Django 的代码库来使这些技能得到很好的使用了。 - -### **更多关于新贡献者的信息** - -在你给 Django 写补丁之前,这里有些关于贡献的信息,你应该看一下: - -- 你应该确保读了 Django 的文档[认领任务和提交补丁](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-code/submitting-patches/)。它涵盖了 Trac 礼仪,如何申请自己的任务,期望的补丁代码风格以及很多其他的重要细节。 -- 首次贡献者,也应该读下 Django 的[给第一次贡献者文档](https://docs.djangoproject.com/en/1.11/internals/contributing/new-contributors/)。那里有很多好的建议。 -- 读完这些后,如果你仍然渴望得到更多关于贡献的信息,你可以随时浏览 [Django 关于做贡献的文档](https://docs.djangoproject.com/en/1.11/internals/contributing/)的剩下部分。那里包含了非常多的信息,也应该是回答你任何问题的第一来源。 - -### **寻找你真正的第一次任务** - -一旦你已经看完了所有的那些信息,你就准备好入门了,然后寻找属于你的任务,去编写补丁吧。特别留意那些写着 “轻松(easy pickings)” 级别的任务。这些任务通常更简单,对于首次贡献者而言是非常棒的。一旦你熟悉了为 Django 做贡献,你就可以继续为更困难更复杂的任务编写补丁了。 - -如果你只是想在已经完成的任务上开始(没人会怪你),可以看看这个列表[需要补丁的简单任务](https://code.djangoproject.com/query?status=new&status=reopened&has_patch=0&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority)和[已有补丁但需要改进的简单任务](https://code.djangoproject.com/query?status=new&status=reopened&needs_better_patch=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority)。如果你对编写测试很熟,你也可以看看这个列表[需要测试的简单任务](https://code.djangoproject.com/query?status=new&status=reopened&needs_tests=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority)。要记得遵循 Django 的关于[认领任务和提交补丁](https://docs.djangoproject.com/en/1.11/internals/contributing/writing-code/submitting-patches/)文档中提到的认领任务问题。 - -### **在创建完拉取请求后还要干什么呢?** - -在任务有了补丁之后,它需要被第二次审查。在提交了拉取请求后,通过设置标志在任务上,比如 “有补丁了(has patch”)”,“不用测试了(doesn’t need tests)” 等,来更新任务元数据,好让其他人为了审查而找到它。做贡献不意味着总是从头开始写补丁。审查已经存在的补丁也是非常有帮助的一次贡献。查看 [Triaging tickets](https://docs.djangoproject.com/en/1.11/internals/contributing/triaging-tickets/) 获取更多细节。 \ No newline at end of file +> 如果你在阅读或实践本教程中遇到困难,请发消息给 [django-users](https://docs.djangoproject.com/en/2.0/internals/mailing-lists/#django-users-mailing-list) 或加入IRC频道 [django on irc.freenode.net](irc://irc.freenode.net/django) 来与其他 Django 用户进行交流,他们也许能帮到你。 + +## 本教程覆盖了哪些知识点 + +首先我们会带你走完为 Django 贡献补丁的流程。在本教程结束时,你应该对工具和流程都有了基本认识。特别地,我们将会介绍以下内容: + +- 安装 Git +- 如何下载 Django 开发版的拷贝 +- 运行 Django 的测试套件 +- 为你的补丁编写测试程序 +- 为你的补丁编写代码 +- 测试你的补丁 +- 提交拉去请求(pull request) +- 哪里可以查看更多信息 + +一旦你完成了本教程,你就可以去看完[为 Django 做贡献文档](https://docs.djangoproject.com/en/2.0/internals/contributing/)的剩下部分了。那里有很多信息,而且是那些想成为定期贡献者必看的。如果你有问题,你也可能在那得到答案。 + +> **要求 Python 3!** +> +> 当前版本的 Django 不支持 Python 2.7。请到[Python 官网下载页](https://www.python.org/downloads/)或者你系统的包管理安装最新版的 Python3。 + +> **对于 Windows 用户** +> +> 给 Windows 安装 Python 时,请确保把 python.exe 添加进了 Path,这样可以直接在命令行使用。 + +## 行为准则(Code of Conduct) + +作为一名贡献者,你可以帮助我们保持 Django 社区的开放和包容。请阅读和遵循我们的[行为准则(Code of Conduct)](https://www.djangoproject.com/conduct/) + +## 安装 Git + +本教程里,为了下载到最新的开发版 Django 和生成你修改过的补丁文件,你将需要安装 Git。 + +为了确认是否安装了 Git,你可以在命令行输入 git。如果提示说命令找不到,那你就需要下载安装了,[Git 下载页](https://git-scm.com/download)。 + +> **对于 Windows 用户** +> +> 给 Windows 安装 Git 时,请确保给 “Git Bash” 打勾了,这样 Git 可以用它自己的 shell 运行。本教程假定你已经安装了它。 + +如果你对 Git 不太熟悉,你可以在命令行输入 **git help** 获取更多命令信息。 + +## 下载 Django 开发版的拷贝 + +第一步就是得到 Django 源码的拷贝。首先[在 GitHub 里 fork Django项目](https://github.com/django/django/fork)。然后在命令行里,使用 **cd** 切换到你想放 Django 本地拷贝的目录里。 + +使用下面的命令下载 Django 源码仓库: + +```bash +$ git clone git@github.com:YourGitHubName/django.git +``` + +现在你有了一份 Django 的本地拷贝,你可以安装它,就像使用 **pip** 安装其他包那样。最方便的方式是使用 *虚拟环境(virtual environment)*(或者 virtualenv),这是 Python 内置的功能,允许您为每个项目单独设立已安装软件包的目录,让它们不会相互干扰。 + +最好是把你所以的虚拟环境(virtualenvs)都放在一个目录下,比如在你的 home 目录下的 **.virtualenvs/** 目录。如果还没创建: + +```bash +$ mkdir ~/.virtualenvs +``` + +现在,运行以下命令创建新的虚拟环境(virtualenvs): + +```bash +$ python3 -m venv ~/.virtualenvs/djangodev +``` + +路径就是新的虚拟环境,而它会被保存在你的电脑里。 + +> **对于 Windows 用户** +> +> 如果你在 Windows 下使用 Git 的 shell,使用内置的 **venv** 模块会无效,由于启动脚本是为系统 shell 创建的(**.bat**)和 PowerShell (**.ps1**)。使用 **virtualenv** 包代替 venv 模块: +> +> ```bash +> $ pip install virtualenv +> $ virtualenv ~/.virtualenvs/djangodev +> ``` + +> **针对 Ubuntu 用户** +> +> 在一些 Ubuntu 版本中,上面的命令可能会失败。 +> +> 使用 **virtualenv** 包代替,确认你安装了 pip3: +> +> ```bash +> $ sudo apt-get install python3-pip +> # 如果下面的命令因为没有权限出错了,那就加上 sudo +> $ pip3 install virtualenv +> $ virtualenv --python=`which python3` ~/.virtualenvs/djangodev +> ``` + +最后一步就是让你的虚拟环境(virtualenvs)生效: + +```bash +$ source ~/.virtualenvs/djangodev/bin/activate +``` + +如果 **source** 命令无效,你可以试试用一个 “点” 代替: + +```bash +$ . ~/.virtualenvs/djangodev/bin/activate +``` + +> **对于 Windows 用户** +> +> 为了在 Windows 上让虚拟环境(virtualenvs)生效,运行下面命令: +> +> ```bash +> $ source ~/virtualenvs/djangodev/Scripts/activate +> ``` + +无论什么时候,当你打开一个新的终端窗口时都要激活一下虚拟环境(virtualenvs)。为了方便这种情况,[virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/) 是很有用的工具。 + +从现在开始,你用 **pip** 安装的任何东西都会被安装进你新的的虚拟环境(virtualenvs),这会隔绝其他环境和系统包。同样,当前激活的虚拟环境(virtualenvs)的名字会在命令行前面显示,这可以让你知道使用的是哪个虚拟环境。 + +继续,安装之前克隆(clone)下来的 Django 拷贝: + +```bash +$ pip install -e /path/to/your/local/clone/django/ +``` + +安装的 Django 现在指向了你的本地拷贝。你将马上看到你做的任何修改,这对你编写第一个补丁很有帮助。 + +## 回滚到先前的 Django 版本 + +本教程里,我们将会使用 [#24788](https://code.djangoproject.com/ticket/24788) 任务(ticket)作为例子学习,所以在任务(ticket)补丁应用之前,我们将回退到 Django 的历史版本。这让我们可以走完所有步骤,包括从头开始写编写补丁,还有运行 Django 的测试套件。 + +**请记住,为了下面的教程,我们将使用 Django 的主干旧版本,当你编写自己的补丁时,你应该始终使用 Django 当前的开发版本** + +> **注意** +> +> 这个任务(ticket)的补丁已经由 Paweł Marczewski 编写了,也已经被应用到了 Django 中,[commit 4df7e8483b2679fc1cba3410f08960bac6f51115](https://github.com/django/django/commit/4df7e8483b2679fc1cba3410f08960bac6f51115)。因此,我们将使用的是 Django 先前的版本,[commit 4ccfc4439a7add24f8db4ef3960d02ef8ae09887](https://github.com/django/django/commit/4ccfc4439a7add24f8db4ef3960d02ef8ae09887)。 + +切换到 Django 的根目录(那里有 **django,docs,tests,AUTHORS** 等),你可以检查下这个旧版本 Django: + +```bash +$ git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887 +``` + +## 第一次运行 Django 的测试套件 + +这非常重要:当向 Django 贡献时,你修改的代码不会将 bug 引到 Django 的其他地方。一个检查 Django 还可以工作的方法就是在你修改之后运行 Django 的测试套件。如果所有的测试都通过了,你就有理由确认你的修改不会完全破坏 Django。如果你之前没有运行过测试套件,最好在这之前运行熟悉下它的输出大概是什么样的。 + +在运行之前,**cd** 切换到 Django 的 **tests/** 目录,安装它的依赖关系: + +```bash +$ pip install -r requirements/py3.txt +``` + +如果你在安装过程中遇到错误,你的系统可能有一些 Python 包的依赖关系缺失。查询一下安装失败的包的文档,或者在网上搜索一下你遇到的这个错误信息。 + +现在我们准备好运行测试套件了。如果你用的是 GNU/Linux,macOS 或者其他 Unix 风格的系统,运行: + +```bash +$ ./runtests.py +``` + +现在坐下来歇会。Django 整个测试套件有超过 9,600 个不同的测试,所以需要 5 到 15 分钟的时间运行,这取决于你电脑的运算速度。 + +当 Django 测试套件运行的时候,你可以看到一段显示着每个正在运行的测试的状态的字符流。**E** 表示在测试期间抛出了一个错误,**F** 表示一个测试的断言(assertions)失败。这两者都可以被认为测试失败了。同时,**x** 和 **s** 分别表示预期失败和跳过测试。“点” 表示通过测试。 + +跳过测试是由于运行测试时缺少额外的库;可以查看[运行所有测试](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/unit-tests/#running-unit-tests-dependencies)所需要的一系列的依赖,确认和你修改的相关的一些测试依赖被安装了(本教程我们什么都不需要安装)。一些测试是特别针对特别的后台数据库的,如果不对那后台进行测试,就会被跳过。SQLite 是默认设置的后台数据库。如果是使用其他的后台数据库,为了运行测试,可以查看[使用其他设置模块](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/unit-tests/#running-unit-tests-settings)。 + +一旦测试完毕,你应该会收到一条关于测试套件是通过了还是失败了的信息。由于你还没有对 Django 的代码做修改,所以整个测试套件 **应该是(should)** 通过。如果你得到的是失败或者错误的信息,请确保你之前走的所有步骤都正确。可以查看[运行单元测试](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/unit-tests/#running-unit-tests)获得更多信息。如果你正在使用 Python3.5+,将会有些与不被赞成的警告(deprecation warnings)相关的失败,那些你可以忽略掉。这些失败已经被 Django 修复了。 + +注意,最新的 Django 主干(trunk)版本不总是稳定的。当开发版本遇上主干(trunk)版本,你可以检查 [Django 的持续集成构建(Django’s continuous integration builds)](https://djangoci.com)来确认是特别针对你的机器的失败还是已经存在在 Django 官方构建里的。如果你点击去看一个特别的构建,你可以看到 “配置矩阵(Configuration Matrix)” 显示着由于 Python 版本和数据库后台问题出现的失败。 + +> **注意** +> +> 在本教程和我们正在工作的任务(ticket)里,测试使用 SQLite 足够了,但是,[运行其他数据库进行测试](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/unit-tests/#running-unit-tests-settings)是可能的(有时是必要的)。 + +## 为你的补丁创建一个分支 + +在修改之前,先为任务(ticket)创建一个新的分支: + +```bash +$ git checkout -b ticket_24788 +``` + +你可以为这个分支选择任何你想要的名字,例如 “ticket_24788”。在这个分支做的所有修改都是特别针对这个任务的,并不会影响到我们先前克隆下来的主要代码。 + +## 为你的任务编写一些测试 + +在大多数情况下,被 Django 接收进来的补丁都必须测试过。针对 bug 修复的补丁,这意味着要编写回退测试代码来确保 bug 不会在 Django 的后续版本再次出现。一个回退测试应该是这样的:当 bug 存在时会失败,当 bug 被修复时会通过。对于包含新特性的补丁,你的测试需要能保证新特性是能正常工作的。当新特性不存在是,它们也是要失败的;一旦被应用又是能通过的。 + +一个好方法是:首先在修改代码前,编写你的新测试。这种开发风格被称为[测试驱动开发(test-driven development)](https://en.wikipedia.org/wiki/Test-driven_development),而且既能被应用在整个项目又能应用在单个补丁。在编写完你的测试后,运行一下它们以确保它们确实是失败的(由于你还没有修复 bug 或者新增特性)。如果你的新测试没有失败,你就需要修复一下这些测试好让它们失败。毕竟,无论 bug 是否存在,回退测试都会通过,那在防止 bug 重新出现这事上是非常没有帮助的。 + +现在让我们动手做下实例。 + +## 为任务 #24788 编写一些测试 + +任务 #24788 推荐加点小功能:给 Form 类指定类级别(class level)属性加 **前缀(prefix)** 的功能: + +```text +[…] forms which ship with apps could effectively namespace themselves such +that N overlapping form fields could be POSTed at once and resolved to the +correct form. +``` + +为了解决这个任务,我们将会为 **BaseForm** 类添加前缀属性。当实例化这个类时,传递一个前缀给 **__init__()** 方法也将会给被创建的实例设置这个前缀。但如果不传递一个前缀(或者传递 **None**)将会使用类级别的前缀。在我们修改之前,我们准备编写一些测试来验证我们的修改函数没有问题,在将来也没有问题。 + +切换到 Django 的 **tests/forms_tests/tests/** 文件夹里,打开 **test_forms.py** 文件。在第 1674 行 **test_forms_with_null_boolean** 函数之前添加以下代码: + +```python +# tests/forms_tests/tests/test_forms.py + +def test_class_prefix(self): + # 前缀也可以在类级别指定 + class Person(Form): + first_name = CharField() + prefix = 'foo' + + p = Person() + self.assertEqual(p.prefix, 'foo') + + p = Person(prefix='bar') + self.assertEqual(p.prefix, 'bar') +``` + +这个新的测试:检查 [设置一个类级别前缀] 是否如预期一样地工作;并且在创建实例时传递一个 **前缀(prefix)** 参数,看是否也工作。 + +> **但这个测试的东西看起来特别困难……** +> +> 如果你之前从来没有接触过测试,第一眼看上去它们确实有点难。幸运的是,测试在电脑编程中是个 *非常* 大的主题,有很多出自这里的信息: +> - [编写和运行测试文档](https://docs.djangoproject.com/en/2.0/topics/testing/overview/) —— 给 Django 编写测试。 +> - [单元测试介绍](www.diveintopython3.net/unit-testing.html) —— 《深入 Python》(一本给 Python 初学开发者的免费的在线书籍)。 +> - 在阅读完这些后,如果你求知若渴,还可以看看 Python 的官方文档 [**unittest**](https://docs.python.org/3/library/unittest.html#module-unittest)。 + +## 运行你写的新测试 + +请记住,我们实际上没有对 **BaseForm** 做任何修改,所以我们的测试应该会失败。为了确保失败真的出现了,让我们在 **forms_tests** 文件夹运行一下所有的测试。从命令行里, **cd** 切换进 Django 的 **tests/** 目录里,然后运行: + +```bash +$ ./runtests.py forms_tests +``` + +如果测试运行正常,你应该看到和我们添加的测试方法相对应的一个失败。如果所有的测试都通过了,那么你要确认一下你已经将新测试添加到了上面合适的文件夹和类里。 + +## 为你的任务编写代码 + +下一步我们将给任务(ticket)[#24788](https://code.djangoproject.com/ticket/24788) 添加函数描述。 + +### **为任务 #24788 编写代码** + +切换到 **django/django/forms/** 文件夹里,然后打开 **forms.py**。在第 72 行找到 **BaseForm** 类,然后在 **field_order** 属性之后添加 **前缀(prefix)** 类属性: + +```python +class BaseForm(object): + # This is the main implementation of all the Form logic. Note that this + # class is different than Form. See the comments by the Form class for + # more information. Any improvements to the form API should be made to + # *this* class, not to the Form class. + field_order = None + prefix = None +``` + +### **现在验证并通过你的测试** + +一旦你修改完了 Django,我们就需要确保之前写的测试都能通过,这样我们才能看到我们上面写的代码正确工作。为了在 **forms_tests** 文件夹运行测试, **cd** 切换到 Django 的 **tests/** 目录里,然后运行: + +```bash +$ ./runtests.py forms_tests +``` + +噢,我们写的那些测试好了!您应该仍然会看到一个例外失败: + +```bash +AssertionError: None != 'foo' +``` + +我们忘记在 **__init__()** 方法里添加条件语句了。在 **django/forms/forms.py** 现在的第 87 行修改 **self.prefix = prefix**,添加一个条件语句: + +```python +if prefix is not None: + self.prefix = prefix +``` + +重新运行测试,所有都应该是通过的。如果不是,请确认你像上面那样正确修改了 **BaseForm** 类,然后正确复制了新的测试。 + +## 第二次运行 Django 的测试套件 + +一旦你验证了你的补丁,你的测试都是正常工作的,运行整个测试套件是个好主意,去验证你的修改没有将 bug 引入到 Django 的其他地方。整个测试套件成功通过并不保证你的代码是没有 bug 的,尽管它确实有助于识别很多可能被忽视的错误和回退。 + +为了运行整个 Django 测试套件, **cd** 切换到 Django 的 **tests/** 目录里,然后运行: + +```bash +$ ./runtests.py +``` + +只要你没看到任何失败,你就可以继续了。 + +## 撰写文档 + +这是个新功能,所以应该被记录到文档里。在 **django/docs/ref/forms/api.txt** 的第 1068 行(文件结尾)添加: + +```text +The prefix can also be specified on the form class:: + + >>> class PersonForm(forms.Form): + ... ... + ... prefix = 'person' + +.. versionadded:: 1.9 + + The ability to specify ``prefix`` on the form class was added. +``` + +由于这个新功能将被添加到还没发布的 Django 1.9 版本的发行说明中,在 **docs/releases/1.9.txt** 文件的 164 行的 “Forms” 部分: + +```text +* A form prefix can be specified inside a form class, not only when + instantiating a form. See :ref:`form-prefix` for details. +``` + +有关编写文档的更多信息,包括有关 **versionadded** 的说明,可以查看[文档编写](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-documentation/)。文章还包括了一篇关于怎样在本地建立文档副本的说明,好让你可以预览将被生成的 HTML。 + +## 预览你的修改 + +现在是时候看看我们的补丁做的所有修改了。为了显示你当前的 Django 副本(有你的修改)和你最初的版本: + +```bash +$ git diff +``` + +使用箭头键上下移动。 + +```git +diff --git a/django/forms/forms.py b/django/forms/forms.py +index 509709f..d1370de 100644 +--- a/django/forms/forms.py ++++ b/django/forms/forms.py +@@ -75,6 +75,7 @@ class BaseForm(object): + # information. Any improvements to the form API should be made to *this* + # class, not to the Form class. + field_order = None ++ prefix = None + + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + initial=None, error_class=ErrorList, label_suffix=None, +@@ -83,7 +84,8 @@ class BaseForm(object): + self.data = data or {} + self.files = files or {} + self.auto_id = auto_id +- self.prefix = prefix ++ if prefix is not None: ++ self.prefix = prefix + self.initial = initial or {} + self.error_class = error_class + # Translators: This is the default suffix added to form field labels +diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt +index 3bc39cd..008170d 100644 +--- a/docs/ref/forms/api.txt ++++ b/docs/ref/forms/api.txt +@@ -1065,3 +1065,13 @@ You can put several Django forms inside one ```` tag. To give each + >>> print(father.as_ul()) +
  • +
  • ++ ++The prefix can also be specified on the form class:: ++ ++ >>> class PersonForm(forms.Form): ++ ... ... ++ ... prefix = 'person' ++ ++.. versionadded:: 1.9 ++ ++ The ability to specify ``prefix`` on the form class was added. +diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt +index 5b58f79..f9bb9de 100644 +--- a/docs/releases/1.9.txt ++++ b/docs/releases/1.9.txt +@@ -161,6 +161,9 @@ Forms + :attr:`~django.forms.Form.field_order` attribute, the ``field_order`` + constructor argument , or the :meth:`~django.forms.Form.order_fields` method. + ++* A form prefix can be specified inside a form class, not only when ++ instantiating a form. See :ref:`form-prefix` for details. ++ + Generic Views + ^^^^^^^^^^^^^ + +diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py +index 690f205..e07fae2 100644 +--- a/tests/forms_tests/tests/test_forms.py ++++ b/tests/forms_tests/tests/test_forms.py +@@ -1671,6 +1671,18 @@ class FormsTestCase(SimpleTestCase): + self.assertEqual(p.cleaned_data['last_name'], 'Lennon') + self.assertEqual(p.cleaned_data['birthday'], datetime.date(1940, 10, 9)) + ++ def test_class_prefix(self): ++ # Prefix can be also specified at the class level. ++ class Person(Form): ++ first_name = CharField() ++ prefix = 'foo' ++ ++ p = Person() ++ self.assertEqual(p.prefix, 'foo') ++ ++ p = Person(prefix='bar') ++ self.assertEqual(p.prefix, 'bar') ++ + def test_forms_with_null_boolean(self): + # NullBooleanField is a bit of a special case because its presentation (widget) + # is different than its data. This is handled transparently, though. +``` + +当你预览完了这个补丁,按下 **q** 键回到命令行。如果补丁的内容看起来是对的,那就是时候 提交(commit) 这些修改了。 + +## 提交补丁中的修改 + +为了提交修改: + +```bash +$ git commit -a +``` + +为了输入提交的信息,会打开一个文本编辑器。根据[提交信息准则](https://docs.djangoproject.com/en/2.0/internals/contributing/committing-code/#committing-guidelines),写下像这样的信息: + +```text +Fixed #24788 -- Allowed Forms to specify a prefix at the class level. +``` + +## 推送提交和拉取请求(pull request) + +在提交了补丁之后,把它发送到在 GitHub 上你 fork 下来的仓库(如果不一样,就把 “ticket_24788”替换为分支的名字): + +```bash +$ git push origin ticket_24788 +``` + +通过 [Django 的 GitHub 页面](https://github.com/django/django/),你可以创建一个拉取请求。你会看到你的分支在 “你最近推送的分支(Your recently pushed branches)” 里。点击旁边的 “对比和拉取请求(Compare & pull request)”。 + +但在本教程里,请别那么做,在下一页里显示预览补丁,你可以点击 “创建拉取请求(Create pull request)” + +## 下一步 + +恭喜你,你已经学会了如何给 Django 创建拉取请求了!更多进阶技术细节你可以看[用 Git 和 GitHub 工作](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/working-with-git/)。 + +现在,你可以通过帮助改进 Django 的代码库来使这些技能得到很好的使用了。 + +### **更多关于新贡献者的信息** + +在你给 Django 写补丁之前,这里有些关于贡献的信息,你应该看一下: + +- 你应该确保读了 Django 的文档[认领任务和提交补丁](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/submitting-patches/)。它涵盖了 Trac 礼仪,如何申请自己的任务,期望的补丁代码风格以及很多其他的重要细节。 +- 首次贡献者,也应该读下 Django 的[给第一次贡献者文档](https://docs.djangoproject.com/en/2.0/internals/contributing/new-contributors/)。那里有很多好的建议。 +- 读完这些后,如果你仍然渴望得到更多关于贡献的信息,你可以随时浏览 [Django 关于做贡献的文档](https://docs.djangoproject.com/en/2.0/internals/contributing/)的剩下部分。那里包含了非常多的信息,也应该是回答你任何问题的第一来源。 + +### **寻找你真正的第一次任务** + +一旦你已经看完了所有的那些信息,你就准备好入门了,然后寻找属于你的任务,去编写补丁吧。特别留意那些写着 “轻松(easy pickings)” 级别的任务。这些任务通常更简单,对于首次贡献者而言是非常棒的。一旦你熟悉了为 Django 做贡献,你就可以继续为更困难更复杂的任务编写补丁了。 + +如果你只是想在已经完成的任务上开始(没人会怪你),可以看看这个列表[需要补丁的简单任务](https://code.djangoproject.com/query?status=new&status=reopened&has_patch=0&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority)和[已有补丁但需要改进的简单任务](https://code.djangoproject.com/query?status=new&status=reopened&needs_better_patch=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority)。如果你对编写测试很熟,你也可以看看这个列表[需要测试的简单任务](https://code.djangoproject.com/query?status=new&status=reopened&needs_tests=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority)。要记得遵循 Django 的关于[认领任务和提交补丁](https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/submitting-patches/)文档中提到的认领任务问题。 + +### **在创建完拉取请求后还要干什么呢?** + +在任务有了补丁之后,它需要被第二次审查。在提交了拉取请求后,通过设置标志在任务上,比如 “有补丁了(has patch”)”,“不用测试了(doesn’t need tests)” 等,来更新任务元数据,好让其他人为了审查而找到它。做贡献不意味着总是从头开始写补丁。审查已经存在的补丁也是非常有帮助的一次贡献。查看 [Triaging tickets](https://docs.djangoproject.com/en/2.0/internals/contributing/triaging-tickets/) 获取更多细节。 diff --git a/docs/reusable_app.md b/docs/reusable_app.md index 575cc8f..947fc2a 100644 --- a/docs/reusable_app.md +++ b/docs/reusable_app.md @@ -65,7 +65,7 @@ mysite/ ## 安装一些必备工具 -Python 打包的解决方案目前有点混乱,因为有各种不同的工具。在本教程中,我们将使用 [setuptools](https://pypi.python.org/pypi/setuptools) 建立我们的包。这是推荐的打包工具(与**distribute**分支合并后)。可是使用 pip 来安装和卸载它。你现在应该安装这两个软件包。如果需要帮助,你可以参考[如何使用 pip 安装 Django](https://docs.djangoproject.com/en/1.11/topics/install/#installing-official-release)。您可以用相同的方式安装 **setuptools**。 +Python 打包的解决方案目前有点混乱,因为有各种不同的工具。在本教程中,我们将使用 [setuptools](https://pypi.python.org/pypi/setuptools) 建立我们的包。这是推荐的打包工具(与**distribute**分支合并后)。可是使用 pip 来安装和卸载它。你现在应该安装这两个软件包。如果需要帮助,你可以参考[如何使用 pip 安装 Django](https://docs.djangoproject.com/en/2.0/topics/install/#installing-official-release)。您可以用相同的方式安装 **setuptools**。 ## 打包你的应用 @@ -77,7 +77,7 @@ Python 的 *打包* 是指将你的应用制作成特定的格式,以便能被 > > 当为你的包选择名称时,记得检查 PyPI 上的内容以避免与现有的包产生冲突。以 **django-** 作为模块名称前缀是很实用的,这将有助于想寻找 Django 应用的人来识别哪些包是用于 Django 的。 > -> 应用标签(即,以点分隔的模块路径的最后一部分)*必须* 在 [**INSTALLED_APPS**](https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-INSTALLED_APPS) 里是独一无二的。避免和 Django [contrib package](https://docs.djangoproject.com/en/1.11/ref/contrib/) 使用相同的标签,例如 **auth**,**admin**,或 **message**。 +> 应用标签(即,以点分隔的模块路径的最后一部分)*必须* 在 [**INSTALLED_APPS**](https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-INSTALLED_APPS) 里是独一无二的。避免和 Django [contrib package](https://docs.djangoproject.com/en/2.0/ref/contrib/) 使用相同的标签,例如 **auth**,**admin**,或 **message**。 2.移动 **polls** 目录到 **django-polls**目录下 @@ -131,34 +131,32 @@ with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: README = readme.read() # 使 setup.py 能在任何地方运行 -os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) - -setup( - name='django-polls', - version='0.1', - packages=find_packages(), - include_package_data=True, - license='BSD License', # example license - description='A simple Django app to conduct Web-based polls.', - long_description=README, - url='https://www.example.com/', - author='Your Name', - author_email='yourname@example.com', - classifiers=[ - 'Environment :: Web Environment', - 'Framework :: Django', - 'Framework :: Django :: X.Y', # replace "X.Y" as appropriate - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', # example license - 'Operating System :: OS Independent', - 'Programming Language :: Python', - # Replace these appropriately if you are stuck on Python 2. - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Topic :: Internet :: WWW/HTTP', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - ], +os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) + +setup( + name='django-polls', + version='0.1', + packages=find_packages(), + include_package_data=True, + license='BSD License', # example license + description='A simple Django app to conduct Web-based polls.', + long_description=README, + url='https://www.example.com/', + author='Your Name', + author_email='yourname@example.com', + classifiers=[ + 'Environment :: Web Environment', + 'Framework :: Django', + 'Framework :: Django :: X.Y', # replace "X.Y" as appropriate + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', # example license + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + ], ) ``` diff --git a/docs/whats_next.md b/docs/whats_next.md index eeee28b..ef242e0 100644 --- a/docs/whats_next.md +++ b/docs/whats_next.md @@ -12,7 +12,7 @@ ## 如何查询文档 -Django 拥有着大量的文档 – 大概在 450,000 单词左右 – 因此有时候查询您所需要的内容并非易事。一些查看文档的好地方是[搜索页面](https://docs.djangoproject.com/en/1.11/search/)和[索引页](https://docs.djangoproject.com/en/1.11/genindex/)。 +Django 拥有着大量的文档 – 大概在 450,000 单词左右 – 因此有时候查询您所需要的内容并非易事。一些查看文档的好地方是[搜索页面](https://docs.djangoproject.com/en/2.0/search/)和[索引页](https://docs.djangoproject.com/en/2.0/genindex/)。 或者您可以完整浏览! @@ -20,21 +20,21 @@ Django 拥有着大量的文档 – 大概在 450,000 单词左右 – 因此有 Django 官方文档的主体内容可以根据满足不同的需要分解成不同的部分: -- [介绍文档](https://docs.djangoproject.com/en/1.11/intro/)通常是为刚接触 Djang 或者 Web 开发的人所设计的。它并不涉及太多高深的内容,但相对地提供了一个宏观的概览视角帮助培养如何使用 Django 进行开发的“感觉”。 +- [介绍文档](https://docs.djangoproject.com/en/2.0/intro/)通常是为刚接触 Djang 或者 Web 开发的人所设计的。它并不涉及太多高深的内容,但相对地提供了一个宏观的概览视角帮助培养如何使用 Django 进行开发的“感觉”。 -- 另一方面,[主题指南](https://docs.djangoproject.com/en/1.11/topics/)对 Django 的每一块进行了深入讲解。主题指南包括了对于 Django 的[模型系统](https://docs.djangoproject.com/en/1.11/topics/db/), [模板引擎](https://docs.djangoproject.com/en/1.11/topics/templates/), [表单框架](https://docs.djangoproject.com/en/1.11/topics/forms/)以及更多内容的完整指导。 +- 另一方面,[主题指南](https://docs.djangoproject.com/en/2.0/topics/)对 Django 的每一块进行了深入讲解。主题指南包括了对于 Django 的[模型系统](https://docs.djangoproject.com/en/2.0/topics/db/), [模板引擎](https://docs.djangoproject.com/en/2.0/topics/templates/), [表单框架](https://docs.djangoproject.com/en/2.0/topics/forms/)以及更多内容的完整指导。 这里可能是您花费时间最多的地方;如果您动手实践了这些指导文档包含的内容,那么您应该对 Django 非常熟悉了。 -- Web 开发涉猎范围广,但是并不深入——遇到的问题可能跨越了不同的领域,为此我们撰写了一系列 [how-to guides](https://docs.djangoproject.com/en/1.11/howto/)文档来回答常见诸如 “How do I..?”(我该如何)这类的问题。在这部分的文档里,您可以找到关于[使用 Django 生成 PDF](https://docs.djangoproject.com/en/1.11/howto/outputting-pdf/),[如何编写自定义的模板标签](https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/)等等的内容。 +- Web 开发涉猎范围广,但是并不深入——遇到的问题可能跨越了不同的领域,为此我们撰写了一系列 [how-to guides](https://docs.djangoproject.com/en/2.0/howto/)文档来回答常见诸如 “How do I..?”(我该如何)这类的问题。在这部分的文档里,您可以找到关于[使用 Django 生成 PDF](https://docs.djangoproject.com/en/2.0/howto/outputting-pdf/),[如何编写自定义的模板标签](https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/)等等的内容。 -对于常见问题的答案可以在 [FAQ](https://docs.djangoproject.com/en/1.11/faq/) 找到。 +对于常见问题的答案可以在 [FAQ](https://docs.djangoproject.com/en/2.0/faq/) 找到。 -- 指导文档和 How-To 文档并没有完全覆盖到 Django 中的每一个类、函数、方法,那样的话内容会太多,不利于学习。实际上,每个类、函数、方法还有模块的实现细节都记录在[参考部分](https://docs.djangoproject.com/en/1.11/ref/)中。那里才是当你需要查找函数细节或是其他具体细节的地方。 +- 指导文档和 How-To 文档并没有完全覆盖到 Django 中的每一个类、函数、方法,那样的话内容会太多,不利于学习。实际上,每个类、函数、方法还有模块的实现细节都记录在[参考部分](https://docs.djangoproject.com/en/2.0/ref/)中。那里才是当你需要查找函数细节或是其他具体细节的地方。 -- 如果您对于部署 Django 项目到公共网络感兴趣的话,我们文档也提供了一些关于各种部署设置的[指导](https://docs.djangoproject.com/en/1.11/howto/deployment/),包括您所需要关注的[部署清单](https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/)。 +- 如果您对于部署 Django 项目到公共网络感兴趣的话,我们文档也提供了一些关于各种部署设置的[指导](https://docs.djangoproject.com/en/2.0/howto/deployment/),包括您所需要关注的[部署清单](https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/)。 -- 最后,有一些"特殊"的文档通常与大多数开发者无关,比如[发行记录](https://docs.djangoproject.com/en/1.11/releases/)以及针对于那些想为 Django 项目贡献力量的开发人员的[内部文档](https://docs.djangoproject.com/en/1.11/internals/),此外还包括了一些[不好分类的杂散文档](https://docs.djangoproject.com/en/1.11/misc/)。 +- 最后,有一些"特殊"的文档通常与大多数开发者无关,比如[发行记录](https://docs.djangoproject.com/en/2.0/releases/)以及针对于那些想为 Django 项目贡献力量的开发人员的[内部文档](https://docs.djangoproject.com/en/2.0/internals/),此外还包括了一些[不好分类的杂散文档](https://docs.djangoproject.com/en/2.0/misc/)。 ## 文档是如何更新的 @@ -57,7 +57,7 @@ Django 文档和其代码使用同一份版本控制进行管理。它位于我 我们鼓励您通过[反馈系统](https://code.djangoproject.com) 提交更新,修正和建议以促进文档质量的改善。Django的开发人员会积极地关注反馈系统,并根据您的反馈来改善文档。 -请注意,无论如何,提交的反馈应该和文档密切相关,而不是涉及技术支持的问题。如果您需要额外的技术支持,请试试 [django user](https://docs.djangoproject.com/en/1.11/internals/mailing-lists/#django-users-mailing-list) 邮件列表或者 [#django IRC channel](irc://irc.freenode.net/django)。 +请注意,无论如何,提交的反馈应该和文档密切相关,而不是涉及技术支持的问题。如果您需要额外的技术支持,请试试 [django user](https://docs.djangoproject.com/en/2.0/internals/mailing-lists/#django-users-mailing-list) 邮件列表或者 [#django IRC channel](irc://irc.freenode.net/django)。 ### 纯文本 @@ -113,6 +113,6 @@ make.bat html - 为了在文档中区分特性的更改或者添加,我们使用了如下说明文字:“`New in version X.Y`”,意思是将出现在接下来的发行版本中(因此,还处在开发的阶段中)。 -- 在某些情况下,文档的修复和改进可能会在开发人员的同意下回迁到最后的发行分支。然而,一旦某个版本的 Django [不再被支持](https://docs.djangoproject.com/en/1.11/internals/release-process/#backwards-compatibility-policy),那么对应版本的文档也会停止进一步的更新。 +- 在某些情况下,文档的修复和改进可能会在开发人员的同意下回迁到最后的发行分支。然而,一旦某个版本的 Django [不再被支持](https://docs.djangoproject.com/en/2.0/internals/release-process/#backwards-compatibility-policy),那么对应版本的文档也会停止进一步的更新。 - [文档主页面](https://docs.djangoproject.com/en/dev/) 包含着所有旧版本的文档。请务必确保您阅读的文档版本对应着您正在使用的 Django!