├── .gitignore ├── 10_Performance and optimization overview.md ├── 11_1_Jython support.md ├── 11_2_Python 3 compatibility.md ├── 11_Python compatibility.md ├── 13_1.md ├── 13_10_Sitemaps.md ├── 13_11_Static files management.md ├── 13_12_Data validation.md ├── 13_1_1_Overview.md ├── 13_1_2_Using the authentication system.md ├── 13_1_3_Password management.md ├── 13_1_4_Customizing authentication.md ├── 13_1_5_API Reference.md ├── 13_2_Caching.md ├── 13_3_Logging.md ├── 13_4_Sending emails.md ├── 13_5_Syndication feeds.md ├── 13_6_Pagination.md ├── 13_7_Messages framework.md ├── 13_8_Serialization.md ├── 13_9_1_Sessions.md ├── 13_Common Web application tools.md ├── 14_1_Conditional content processing.md ├── 14_2_Content types and generic relations.md ├── 14_3_Flatpages.md ├── 14_4_1_Redirects.md ├── 14_5_Signals.md ├── 14_6_System check framework.md ├── 14_7_The sites framework.md ├── 14_8_Unicode in Django.md ├── 14_Other core functionalities.md ├── 1_1.md ├── 1_1_1_Django at a glance.md ├── 1_1_2_Quick install guide.md ├── 1_2.md ├── 1_2_1_Part 1 Models.md ├── 1_2_2_Part 2 The admin site.md ├── 1_2_3_Part 3 Views and templates.md ├── 1_2_4_Part 4 Forms and generic views.md ├── 1_2_5_Part 5 Testing.md ├── 1_2_6_Part 6 Static files.md ├── 1_3.md ├── 1_3_1_How to write reusable apps.md ├── 1_3_2_Writing your first patch for Django.md ├── 1_First steps.md ├── 2_1.md ├── 2_1_1_Model syntax.md ├── 2_1_2_Field types.md ├── 2_1_3_Meta options.md ├── 2_1_4_Model class.md ├── 2_2.md ├── 2_2_1_Making queries.md ├── 2_2_2_QuerySet method reference.md ├── 2_2_3_Lookup expressions.md ├── 2_3.md ├── 2_3_1_Instance methods.md ├── 2_3_2_Accessing related objects.md ├── 2_4.md ├── 2_4_1_Introduction to Migrations.md ├── 2_4_2_Operations reference.md ├── 2_4_3_SchemaEditor.md ├── 2_4_4_Writing migrations.md ├── 2_5.md ├── 2_5_10_Database Functions.md ├── 2_5_1_Manager.md ├── 2_5_2_Performing raw SQL queries.md ├── 2_5_3_Transactions.md ├── 2_5_4_Aggregation.md ├── 2_5_5_Custom fields.md ├── 2_5_6_Multiple databases.md ├── 2_5_7_Custom lookups.md ├── 2_5_8_Query Expressions.md ├── 2_5_9_Conditional Expressions.md ├── 2_6.md ├── 2_6_1_Supported databases.md ├── 2_6_2_Legacy databases.md ├── 2_6_3_Providing initial data.md ├── 2_6_4_Optimize database access.md ├── 2_The model layer.md ├── 3_1.md ├── 3_1_1_URLconfs.md ├── 3_1_2_View functions.md ├── 3_1_3_Shortcuts.md ├── 3_1_4_Decorators.md ├── 3_2.md ├── 3_2_1_Built-in Views.md ├── 3_2_2_Request response objects.md ├── 3_2_3_TemplateResponse objects.md ├── 3_2_4_TemplateResponse objects.md ├── 3_3.md ├── 3_3_1_Overview.md ├── 3_3_2_File objects.md ├── 3_3_3_Storage API.md ├── 3_3_4_Managing files.md ├── 3_3_5_Custom storage.md ├── 3_4.md ├── 3_4_1_Overview.md ├── 3_4_2_Built-in display views.md ├── 3_4_3_Built-in editing views.md ├── 3_4_4_Using mixins.md ├── 3_4_5_API reference.md ├── 3_4_6_Flattened index.md ├── 3_5.md ├── 3_5_1_Generating CSV.md ├── 3_5_2_Generating PDF.md ├── 3_6.md ├── 3_6_1_Overview.md ├── 3_6_2_Built-in middleware classes.md ├── 3_The view layer.md ├── 4_1.md ├── 4_1_1_Overview.md ├── 4_2.md ├── 4_2_1_Language overview.md ├── 4_2_2_Built-in tags and filters.md ├── 4_2_3_Web design helpers.md ├── 4_2_4_Humanization.md ├── 4_3.md ├── 4_3_1_Template API.md ├── 4_3_2_Custom tags and filters.md ├── 4_The template layer.md ├── 5_1.md ├── 5_1_1_Overview.md ├── 5_1_2_Form API.md ├── 5_1_3_Built-in widgets.md ├── 5_1_4_Built-in widgets.md ├── 5_2.md ├── 5_2_1_Forms for models.md ├── 5_2_2_Integrating media.md ├── 5_2_3_Formsets.md ├── 5_2_4_Customizing validation.md ├── 5_Forms.md ├── 6_1.md ├── 6_1_1_Overview.md ├── 6_1_2_Full list of settings.md ├── 6_2.md ├── 6_2_Overview.md ├── 6_3.md ├── 6_3_Overview.md ├── 6_4.md ├── 6_4_1_Overview.md ├── 6_4_2_Adding custom commands.md ├── 6_5.md ├── 6_5_1_Introduction.md ├── 6_5_2_Writing and running tests.md ├── 6_5_3_Included testing tools.md ├── 6_5_4_Advanced topics.md ├── 6_6.md ├── 6_6_1_Overview.md ├── 6_6_2_WSGI servers.md ├── 6_6_3_FastCGI SCGI AJP.md ├── 6_6_4_Deploying static files.md ├── 6_6_5_Tracking code errors by email.md ├── 6_The development process.md ├── 7_1_Admin site.md ├── 7_2_Admin actions.md ├── 7_3_Admin documentation generator.md ├── 7_Admin.md ├── 8_1_Security overview.md ├── 8_2_Disclosed security issues in Django.md ├── 8_3_Clickjacking protection.md ├── 8_4_Cross Site Request Forgery protection.md ├── 8_5_Cryptographic signing.md ├── 8_6_Security Middleware.md ├── 8_Security.md ├── 9_1_1_Overview.md ├── 9_1_2_Internationalization.md ├── 9_1_4_Localized Web UI formatting and form input.md ├── 9_2_“Local flavor”.md ├── 9_3_Time zones.md ├── 9_Internationalization and localization.md ├── README.md ├── SUMMARY.md ├── img ├── usyiyi_qr_alipay.png └── usyiyi_qr_wechat.png └── styles └── ebook.css /.gitignore: -------------------------------------------------------------------------------- 1 | _book 2 | Thumbs.db -------------------------------------------------------------------------------- /11_1_Jython support.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Running Django on Jython 4 | 5 | [Jython](http://www.jython.org/) 是在 Java 平台 (JVM) 运行的 Python 实现。这个文档将让你在Jython之上运行Django。 6 | 7 | ## Installing Jython 8 | 9 | Django使用Jython 2.7b2及更高版本。有关下载和安装说明,请参阅[Jython](http://www.jython.org/)网站。 10 | 11 | ## Creating a servlet container 12 | 13 | 如果你只是想试验Django,请跳到下一节; Django包含一个可以用于测试的轻量级Web服务器,因此在准备在生产环境中部署Django之前,您不需要设置任何其他内容。 14 | 15 | 如果要在生产站点上使用Django,请使用Java servlet容器,例如[Apache Tomcat](http://tomcat.apache.org/)。如果您需要其包含的额外功能,则完整的JavaEE应用程序服务器(如[GlassFish](https://glassfish.java.net/)或[JBoss](http://www.jboss.org/))也可以。 16 | 17 | ## Installing Django 18 | 19 | 下一步是安装Django本身。这与在标准Python上安装Django完全相同,因此请参阅[_Remove any old versions of Django_](../topics/install.html#removing-old-versions-of-django)和[_Install the Django code_](../topics/install.html#install-django-code)以获取相关说明。 20 | 21 | ## Installing Jython platform support libraries 22 | 23 | [django-jython](http://code.google.com/p/django-jython/)项目包含用于Django / Jython开发的数据库后端和管理命令。注意,内置的Django后端不会在Jython之上工作。 24 | 25 | 要安装它,请按照项目网站上详细的[安装说明](https://pythonhosted.org/django-jython/quickstart.html#install)操作。另外,阅读[数据库后端](https://pythonhosted.org/django-jython/database-backends.html)文档。 26 | 27 | ## Differences with Django on Jython 28 | 29 | 此时,Jython上的Django应该与运行在标准Python上的Django几乎相同。但是,有几点差异需要牢记: 30 | 31 | * 请记住使用`jython`命令而不是`python`。文档使用`python`来保持一致性,但是如果你使用的是Jython,你会想每次发生时用`jython`来替代`python`。 32 | * 同样,您需要使用`JYTHONPATH`环境变量,而不是`PYTHONPATH`。 33 | * Django中需要[枕头](http://pillow.readthedocs.org/en/latest/)的任何部分都将无法工作。 34 | 35 | -------------------------------------------------------------------------------- /11_Python compatibility.md: -------------------------------------------------------------------------------- 1 | # Python 的兼容性 2 | 3 | Django 的目的,是要与多个不同的 Python 版本兼容。 -------------------------------------------------------------------------------- /13_1.md: -------------------------------------------------------------------------------- 1 | # 认证 -------------------------------------------------------------------------------- /13_1_1_Overview.md: -------------------------------------------------------------------------------- 1 | # Django 中的用户认证 # 2 | 3 | Django从开始就带有一个用户认证系统。它处理用户账号、组、权限以及基于cookie的用户会话。本节文档解释默认的实现如何直接使用,以及如何[扩展和定制](http://python.usyiyi.cn/django/topics/auth/customizing.html)它以适合你项目的需要。 4 | 5 | ## 概览 ## 6 | 7 | Django认证系统同时处理认证和授权。简单地讲,认证验证一个用户是它们声称的那个人,授权决定一个认证通过的用户允许做什么。这里的词语认证同时指代这两项任务。 8 | 9 | 认证系统包含: 10 | 11 | + 用户 12 | + 权限:二元(是/否)标志指示一个用户是否可以做一个特定的任务。 13 | + 组:对多个用户运用标签和权限的一种通用的方式。 14 | + 一个可配置的密码哈希系统 15 | + 用于登录用户或限制内容的表单和视图 16 | + 一个可插拔的后台系统 17 | 18 | Django中的认证系统的目标是非常通用且不提供在web认证系统中某些常见的功能。某些常见问题的解决方法已经在第三方包中实现: 19 | 20 | + 密码强度检查 21 | + 登录尝试的制约 22 | + 第三方认证(例如OAuth) 23 | 24 | ## 安装 ## 25 | 26 | 认证的支持作为Django的一个contrib模块,打包于`django.contrib.auth`中。默认情况下,要求的配置已经包含在`django-admin startproject`生成的`settings.py`中,它们的组成包括`INSTALLED_APPS`设置中的两个选项: 27 | 28 | 1. '`django.contrib.auth`'包含认证框架的核心和默认的模型。 29 | 2. '`django.contrib.contenttypes`'是Django内容类型系统,它允许权限与你创建的模型关联。 30 | 和`MIDDLEWARE_CLASSES`设置中的两个选项: 31 | 32 | 1. `SessionMiddleware`管理请求之间的会话。 33 | 2. `AuthenticationMiddleware`使用会话将用户与请求管理起来。 34 | 35 | 有了这些设置,运行`manage.py migrate`命令将为认证相关的模型创建必要的数据库表并为你的应用中定义的任意模型创建权限。 36 | 37 | ## 使用 ## 38 | 39 | [使用Django默认的实现](http://python.usyiyi.cn/django/topics/auth/default.html) 40 | 41 | + [使用User对象](http://python.usyiyi.cn/django/topics/auth/default.html#user-objects) 42 | + [权限和授权](http://python.usyiyi.cn/django/topics/auth/default.html#topic-authorization) 43 | + [Web 请求中的认证](http://python.usyiyi.cn/django/topics/auth/default.html#auth-web-requests) 44 | + [ 在admin 中管理用户](http://python.usyiyi.cn/django/topics/auth/default.html#auth-admin) 45 | 46 | [默认实现的API参考](http://python.usyiyi.cn/django/ref/contrib/auth.html) 47 | 48 | [自定义Users和认证](http://python.usyiyi.cn/django/topics/auth/customizing.html) 49 | 50 | [Django中的密码管理](http://python.usyiyi.cn/django/topics/auth/passwords.html) 51 | 52 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Overview](https://docs.djangoproject.com/en/1.8/topics/auth/)。 53 | > 54 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 55 | > 56 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 57 | -------------------------------------------------------------------------------- /13_1_3_Password management.md: -------------------------------------------------------------------------------- 1 | # Django中的密码管理 2 | 3 | 密码管理在非必要情况下一般不会重新发明,Django致力于提供一套安全、灵活的工具集来管理用户密码。本文档描述Django存储密码和hash存储方法配置的方式,以及使用hash密码的一些实例。 4 | 5 | 另见 6 | 7 | 即使用户可能会使用强密码,攻击者也可能窃听到他们的连接。使用[_HTTPS_](../security.html#security-recommendation-ssl)来避免在HTTP连接上发送密码(或者任何敏感的数据),因为否则密码又被嗅探的风险。 8 | 9 | ## Django如何储存密码 10 | 11 | Django通常使用PBKDF2来提供灵活的密码储存系统。 12 | 13 | [`User`](../../ref/contrib/auth.html#django.contrib.auth.models.User "django.contrib.auth.models.User") 对象的[`password`](../../ref/contrib/auth.html#django.contrib.auth.models.User.password "django.contrib.auth.models.User.password")属性是一个这种格式的字符串: 14 | 15 | ``` 16 | $$$ 17 | 18 | ``` 19 | 20 | 那些就是用于储存用户密码的部分,以美元字符分分隔。它们由哈希算法、算法迭代次数(工作因数)、随机的salt、以及生成的密码哈希值组成。算法是Django可以使用的,单向哈希或者密码储存算法之一,请见下文。迭代描述了算法在哈希上执行的次数。salt是随机的种子值,哈希值是这个单向函数的结果。 21 | 22 | 通常,Django以SHA256的哈希值使用[PBKDF2](http://en.wikipedia.org/wiki/PBKDF2)算法,由[NIST](http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf)推荐的一种密码伸缩机制。这对于大多数用户都很有效:它非常安全,需要大量的计算来破解。 23 | 24 | 然而,取决于你的需求,你可以选择一个不同的算法,或者甚至使用自定义的算法来满足你的特定的安全环境。不过,大多数用户并不需要这样做 -- 如果你不确定,最好不要这样。如果你打算这样做,请继续阅读: 25 | 26 | DJango通过访问[`PASSWORD_HASHERS`](../../ref/settings.html#std:setting-PASSWORD_HASHERS)设置来选择要使用的算法。这里有一个列表,列出了Django支持的哈希算法类。列表的第一个元素 (即`settings.`PASSWORD_HASHERS[0]) 会用于储存密码, 所有其它元素都是用于验证的哈希值,它们可以用于检查现有的密码。意思是如果你打算使用不同的算法,你需要修改[`PASSWORD_HASHERS`](../../ref/settings.html#std:setting-PASSWORD_HASHERS),来将你最喜欢的算法在列表中放在首位。 27 | 28 | [`PASSWORD_HASHERS`](../../ref/settings.html#std:setting-PASSWORD_HASHERS)默认为: 29 | 30 | ``` 31 | PASSWORD_HASHERS = ( 32 | 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 33 | 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 34 | 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 35 | 'django.contrib.auth.hashers.BCryptPasswordHasher', 36 | 'django.contrib.auth.hashers.SHA1PasswordHasher', 37 | 'django.contrib.auth.hashers.MD5PasswordHasher', 38 | 'django.contrib.auth.hashers.CryptPasswordHasher', 39 | ) 40 | 41 | ``` 42 | 43 | 这意味着,Django会使用 [PBKDF2](http://en.wikipedia.org/wiki/PBKDF2) 储存所有密码,但是支持使用 PBKDF2SHA1, [bcrypt](http://en.wikipedia.org/wiki/Bcrypt), [SHA1](http://en.wikipedia.org/wiki/SHA1)等等算法来检查储存的密码。下一节会描述一些通用的方法,高级用户可能想通过它来修改这个设置。 44 | 45 | ### 在Django中使用bcrypt 46 | 47 | [Bcrypt](http://en.wikipedia.org/wiki/Bcrypt)是一种流行的密码储存算法,它特意被设计用于长期的密码储存。Django并没有默认使用它,由于它需要使用三方的库,但是由于很多人都想使用它,Django会以最小的努力来支持。 48 | 49 | 执行以下步骤来作为你的默认储存算法来使用Bcrypt: 50 | 51 | 1. 安装[bcrypt 库](https://pypi.python.org/pypi/bcrypt/)。这可以通过运行`pip install django[bcrypt]`,,或者下载并运行 `python setup.py install`来实现。 52 | 53 | 2. 修改 [`PASSWORD_HASHERS`](../../ref/settings.html#std:setting-PASSWORD_HASHERS) ,将 `BCryptSHA256PasswordHasher`放在首位。也就是说,在你的设置文件中应该: 54 | 55 | ``` 56 | PASSWORD_HASHERS = ( 57 | 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 58 | 'django.contrib.auth.hashers.BCryptPasswordHasher', 59 | 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 60 | 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 61 | 'django.contrib.auth.hashers.SHA1PasswordHasher', 62 | 'django.contrib.auth.hashers.MD5PasswordHasher', 63 | 'django.contrib.auth.hashers.CryptPasswordHasher', 64 | ) 65 | 66 | ``` 67 | 68 | (你应该将其它元素留在列表中,否则Django不能升级密码;见下文)。 69 | 70 | 配置完毕 -- 现在Django会使用Bcrypt作为默认的储存算法。 71 | 72 | BCryptPasswordHasher的密码截断 73 | 74 | bcrypt的设计者会在72个字符处截断所有的密码,这意味着`bcrypt(password_with_100_chars) == bcrypt(password_with_100_chars[:72])`。原生的 `BCryptPasswordHasher` 并不会做任何的特殊处理, 所以它也会受到这一隐藏密码长度限制的约束。`BCryptSHA256PasswordHasher` 通过事先使用 sha256生成哈希来解决这一问题。这样就可以防止密码截断了,所以你还是应该优先考虑`BCryptPasswordHasher`。这个截断带来的实际效果很微不足道,因为大多数用户不会使用长度超过72的密码,并且即使在72个字符处截断,破解brypt所需的计算能力依然是天文数字。虽然如此,我们还是推荐使用`BCryptSHA256PasswordHasher` ,根据 “有备无患”的原则。 75 | 76 | 其它 bcrypt 的实现 77 | 78 | 有一些其它的bcrypt 实现,可以让你在Django中使用它。Django的bcrypt 支持并不直接兼容这些实现。你需要修改数据库中的哈希值,改为 `bcrypt$(raw bcrypt output)`的形式,来升级它们。例如: `bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy`。 79 | 80 | ### 增加工作因数 81 | 82 | PBKDF2 和bcrypt 算法使用大量的哈希迭代或循环。这会有意拖慢攻击者,使对哈希密码的攻击更难以进行。然而,随着计算机能力的不断增加,迭代的次数也需要增加。我们选了一个合理的默认值(并且在Django的每个发行版会不断增加),但是你可能想要调高或者调低它,取决于你的安全需求和计算能力。要想这样做,你可以继承相应的算法,并且覆写`iterations`参数。例如,增加PBKDF2算法默认使用的迭代次数: 83 | 84 | 1. 创建`django.contrib.auth.hashers.PBKDF2PasswordHasher`的子类: 85 | 86 | ``` 87 | from django.contrib.auth.hashers import PBKDF2PasswordHasher 88 | 89 | class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher): 90 | """ 91 | A subclass of PBKDF2PasswordHasher that uses 100 times more iterations. 92 | """ 93 | iterations = PBKDF2PasswordHasher.iterations * 100 94 | 95 | ``` 96 | 97 | 把它保存在项目中的某个位置。例如,把它放在类似于`myproject/hashers.py`的文件中。 98 | 99 | 2. 将你的新的hasher作为第一个元素添加到[`PASSWORD_HASHERS`](../../ref/settings.html#std:setting-PASSWORD_HASHERS): 100 | 101 | ``` 102 | PASSWORD_HASHERS = ( 103 | 'myproject.hashers.MyPBKDF2PasswordHasher', 104 | 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 105 | 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 106 | 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 107 | 'django.contrib.auth.hashers.BCryptPasswordHasher', 108 | 'django.contrib.auth.hashers.SHA1PasswordHasher', 109 | 'django.contrib.auth.hashers.MD5PasswordHasher', 110 | 'django.contrib.auth.hashers.CryptPasswordHasher', 111 | ) 112 | 113 | ``` 114 | 115 | 配置完毕 -- 现在DJango在储存使用PBKDF2的密码时会使用更多的迭代次数。 116 | 117 | ### 密码升级 118 | 119 | 用户登录之后,如果他们的密码没有以首选的密码算法来储存,Django会自动将算法升级为首选的那个。这意味着Django中旧的安装会在用户登录时自动变得更加安全,并且你可以随意在新的(或者更好的)储存算法发明之后切换到它们。 120 | 121 | 然而,Django只会升级在 [`PASSWORD_HASHERS`](../../ref/settings.html#std:setting-PASSWORD_HASHERS)中出现的算法,所以升级到新系统时,你应该确保不要 _移除_列表中的元素。如果你移除了,使用列表中没有的算法的用户不会被升级。修改PBKDF2迭代次数之后,密码也会被升级。 122 | 123 | ## Manually managing a user’s password 124 | 125 | [`django.contrib.auth.hashers`](#module-django.contrib.auth.hashers "django.contrib.auth.hashers")模块提供了一系列的函数来创建和验证哈希密码。你可以独立于`User`模型之外使用它们。 126 | 127 | `check_password`(_password_, _encoded_)[[source]](../../_modules/django/contrib/auth/hashers.html#check_password) 128 | 129 | 如果你打算通过比较纯文本密码和数据库中哈希后的密码来手动验证用户,要使用[`check_password()`](#django.contrib.auth.hashers.check_password "django.contrib.auth.hashers.check_password")这一便捷的函数。它接收两个参数:要检查的纯文本密码,和数据库中用户的`password`字段的完整值。如果二者匹配,返回`True` ,否则返回`False` 。 130 | 131 | `make_password`(_password_, _salt=None_, _hasher='default'_)[[source]](../../_modules/django/contrib/auth/hashers.html#make_password) 132 | 133 | 以当前应用所使用的格式创建哈希密码。它接受一个必需参数:纯文本密码。如果你不想使用默认值(`PASSWORD_HASHERS`设置的首选项),你可以提供salt值和要使用的哈希算法,它们是可选的。当前支持的算法是: `'pbkdf2_sha256'`, `'pbkdf2_sha1'`, `'bcrypt_sha256'` (参见[_在 Django中使用Bcrypt_](#bcrypt-usage)), `'bcrypt'`, `'sha1'`, `'md5'`, `'unsalted_md5'` (仅仅用于向后兼容) 和 `'crypt'` (如果你安装了 `crypt`库)。如果password参数是`None`,会返回一个不可用的密码(它永远不会被[`check_password()`](#django.contrib.auth.hashers.check_password "django.contrib.auth.hashers.check_password")接受)。 134 | 135 | `is_password_usable`(_encoded_password_)[[source]](../../_modules/django/contrib/auth/hashers.html#is_password_usable) 136 | 137 | 检查提供的字符串是否是可以用[`check_password()`](#django.contrib.auth.hashers.check_password "django.contrib.auth.hashers.check_password")验证的哈希密码。 138 | 139 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Password management](https://docs.djangoproject.com/en/1.8/topics/auth/passwords/)。 140 | > 141 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 142 | > 143 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 144 | -------------------------------------------------------------------------------- /13_Common Web application tools.md: -------------------------------------------------------------------------------- 1 | # 常见的网站应用工具 # 2 | 3 | Django 提供了多种工具用于开发Web应用程序 4 | -------------------------------------------------------------------------------- /14_1_Conditional content processing.md: -------------------------------------------------------------------------------- 1 | # 按需内容处理 2 | 3 | HTTP客户端可能发送一些协议头来告诉服务端它们已经看过了哪些资源。这在获取网页(使用HTTP`GET`请求)时非常常见,可以避免发送客户端已经获得的完整数据。然而,相同的协议头可用于所有HTTP方法(`POST`, `PUT`, `DELETE`, 以及其它)。 4 | 5 | 对于每一个Django从视图发回的页面(响应),都会提供两个HTTP协议头:`ETag`和`Last-Modified`。这些协议头在HTTP响应中是可选的。它们可以由你的视图函数设置,或者你可以依靠 [`CommonMiddleware`](../ref/middleware.html#django.middleware.common.CommonMiddleware "django.middleware.common.CommonMiddleware") 中间件来设置`ETag` 协议头。 6 | 7 | 当你的客户端再次请求相同的资源时,它可能会发送 [If-modified-since](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25) 或者[If-unmodified-since](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.28)的协议头,包含之前发送的最后修改时间;或者 [If-match](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24) 或[If-none-match](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26)协议头,包含之前发送的`ETag`。如果页面的当前版本匹配客户端发送的`ETag`,或者如果资源没有被修改,会发回304状态码,而不是一个完整的回复,告诉客户端没有任何修改。根据协议头,如果页面被修改了,或者不匹配客户端发送的 `ETag`,会返回412(先决条件失败,Precondition Failed)状态码。 8 | 9 | 当你需要更多精细化的控制时,你可以使用每个视图的按需处理函数。 10 | 11 | Changed in Django 1.8: 12 | 13 | 向按需视图处理添加`If-unmodified-since`协议头的支持 14 | 15 | ## The `condition` 16 | 17 | 有时(实际上是经常),你可以创建一些函数来快速计算出资源的[ETag](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11)值或者最后修改时间,**并不**需要执行构建完整视图所需的所有步骤。Django可以使用这些函数来为视图处理提供一个“early bailout”的选项。来告诉客户端,内容自从上次请求并没有任何改动。 18 | 19 | 这两个函数作为参数传递到`django.views.decorators.http.condition`装饰器中。这个装时期使用这两个函数(如果你不能既快又容易得计算出来,你只需要提供一个)来弄清楚是否HTTP请求中的协议头匹配那些资源。如果它们不匹配,会生成资源的一份新的副本,并调用你的普通视图。 20 | 21 | `condition`装饰器的签名为i: 22 | 23 | ``` 24 | condition(etag_func=None, last_modified_func=None) 25 | 26 | ``` 27 | 28 | 计算ETag的最后修改时间的两个函数,会以相同的顺序传入`request`对象和相同的参数,就像它们封装的视图函数那样。`last_modified_func`函数应该返回一个标准的datetime值,它制订了资源修改的最后时间,或者资源不存在为 `None`。传递给`etag`装饰器的函数应该返回一个表示资源[Etag](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11)的字符串,或者资源不存在时为`None`。 29 | 30 | 用一个例子可以很好展示如何使用这一特性。假设你有这两个模型,表示一个简单的博客系统: 31 | 32 | ``` 33 | import datetime 34 | from django.db import models 35 | 36 | class Blog(models.Model): 37 | ... 38 | 39 | class Entry(models.Model): 40 | blog = models.ForeignKey(Blog) 41 | published = models.DateTimeField(default=datetime.datetime.now) 42 | ... 43 | 44 | ``` 45 | 46 | 如果头版展示最后的博客文章,仅仅在你添加新文章的时候修改,你可以非常快速地计算出最后修改时间。你需要这个博客每一篇文章的最后 `发布` 日期。实现它的一种方式是: 47 | 48 | ``` 49 | def latest_entry(request, blog_id): 50 | return Entry.objects.filter(blog=blog_id).latest("published").published 51 | 52 | ``` 53 | 54 | 接下来你可以使用这个函数,来为你的头版视图事先探测未修改的页面: 55 | 56 | ``` 57 | from django.views.decorators.http import condition 58 | 59 | @condition(last_modified_func=latest_entry) 60 | def front_page(request, blog_id): 61 | ... 62 | 63 | ``` 64 | 65 | ## 只计算一个值的快捷方式 66 | 67 | 一个普遍的原则是,如果你提供了计算 ETag_和_最后修改时间的函数,你应该这样做:你并不知道HTTP客户端会发给你哪个协议头,所以要准备好处理两种情况。但是,有时只有二者之一容易计算,并且Django只提供给你计算ETag或最后修改日期的装饰器。 68 | 69 | `django.views.decorators.http.etag` 和`django.views.decorators.http.last_modified`作为`condition`装饰器,传入相同类型的函数。他们的签名是: 70 | 71 | ``` 72 | etag(etag_func) 73 | last_modified(last_modified_func) 74 | 75 | ``` 76 | 77 | 我们可以编写一个初期的示例,它仅仅使用最后修改日期的函数,使用这些装饰器之一: 78 | 79 | ``` 80 | @last_modified(latest_entry) 81 | def front_page(request, blog_id): 82 | ... 83 | 84 | ``` 85 | 86 | ...或者: 87 | 88 | ``` 89 | def front_page(request, blog_id): 90 | ... 91 | front_page = last_modified(latest_entry)(front_page) 92 | 93 | ``` 94 | 95 | ### Use `condition` 96 | 97 | 如果你想要测试两个先决条件,把`etag` 和`last_modified`装饰器链到一起看起来很不错。但是,这会导致不正确的行为: 98 | 99 | ``` 100 | # Bad code. Don't do this! 101 | @etag(etag_func) 102 | @last_modified(last_modified_func) 103 | def my_view(request): 104 | # ... 105 | 106 | # End of bad code. 107 | 108 | ``` 109 | 110 | 第一个装饰器不知道后面的任何事情,并且可能发送“未修改”的响应,即使第二个装饰器会处理别的事情。`condition`装饰器同时更使用两个回调函数,来弄清楚哪个是正确的行为。 111 | 112 | ## 使用带有其它HTTP方法的装饰器 113 | 114 | `condition`装饰器不仅仅对`GET` 和 `HEAD`请求有用(`HEAD`请求在这种情况下和`GET`相同)。它也可以用于为 `POST`, `PUT` 和 `DELETE`请求提供检查。在这些情况下,不是要返回一个“未修改(not modified,314)”的响应,而是要告诉服务端,它们尝试修改的资源在此期间被修改了。 115 | 116 | 例如,考虑以下客户端和服务端之间的交互: 117 | 118 | 1. 客户端请求`/foo/`。 119 | 2. 服务端回复一些带有`"abcd1234"`ETag的内容。 120 | 3. 客户端发送HTTP `PUT` 请求到 `/foo/` 来更新资源。同时也发送了`If-Match: "abcd1234"` 协议头来指定尝试更新的版本。 121 | 4. 服务端检查是否资源已经被修改,通过和`GET` 上所做的相同方式计算ETag(使用相同的函数)。如果资源 _已经_ 修改了,会返回412状态码,意思是“先决条件失败(precondition failed)”。 122 | 5. 客户端在接收到412响应之后,发送 `GET`请求到 `/foo/`,来在更新之前获取内容的新版本。 123 | 124 | 重要的事情是,这个例子展示了在所有情况下,ETag和最后修改时间值都采用相同函数计算。实际上,你 **应该** 使用相同函数,以便每次都返回相同的值。 125 | 126 | ## 使用中间件按需处理来比较 127 | 128 | 你可能注意到,Django已经通过[`django.middleware.http.ConditionalGetMiddleware`](../ref/middleware.html#django.middleware.http.ConditionalGetMiddleware "django.middleware.http.ConditionalGetMiddleware") 和 [`CommonMiddleware`](../ref/middleware.html#django.middleware.common.CommonMiddleware "django.middleware.common.CommonMiddleware").提供了简单和直接的`GET` 的按需处理。这些中间件易于使用并且适用于多种情况,然而它们的功能有一些高级用法上的限制: 129 | 130 | * 它们在全局上用于你项目中的所有视图。 131 | * 它们不会代替你生成响应本身,这可能要花一些代价。 132 | * 它们只适用于HTTP `GET` 请求。 133 | 134 | 在这里,你应该选择最适用于你特定问题的工具。如果你有办法快速计算出ETag和修改时间,并且如果一些视图需要花一些时间来生成内容,你应该考虑使用这篇文档描述的`condition`装饰器。如果一些都执行得非常快,坚持使用中间件在如果视图没有修改的条件下也会使发回客户端的网络流量也会减少。 135 | 136 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Conditional content processing](https://docs.djangoproject.com/en/1.8/topics/conditional-view-processing/)。 137 | > 138 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 139 | > 140 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 141 | -------------------------------------------------------------------------------- /14_4_1_Redirects.md: -------------------------------------------------------------------------------- 1 | # 重定向应用 # 2 | 3 | Django 原生自带一个可选的重定向应用。它将简单的重定向保存到数据库中并处理重定向。它默认使用HTTP 响应状态码`301 Moved Permanently`。 4 | 5 | ## 安装 ## 6 | 7 | 请依照下面的步骤安装重定向应用: 8 | 9 | 1. 确保`django.contrib.sites` 框架已经安装。 10 | 2. 添加'`django.contrib.redirects`' 到 `INSTALLED_APPS` 设置中。 11 | 3. 添加'`django.contrib.redirects.middleware.RedirectFallbackMiddleware`' 到`MIDDLEWARE_CLASSES` 设置中。 12 | 4. 运行命令`manage.py migrate`。 13 | 14 | ## 它是如何工作的 ## 15 | 16 | `manage.py migrate` 在数据库中创建一张`django_redirect` 表。它是一张简单的查询表,具有`site_id`、`old_path` 和`new_path` 字段。 17 | 18 | `RedirectFallbackMiddleware` 完成所有的工作。每当Django 的应用引发一个404 错误,该中间件将到重定向数据库中检查请求的URL。它会根据`old_path` 和`SITE_ID` 设置的站点ID 查找重定向的路径。 19 | 20 | + 如果找到匹配的记录且`new_path `不为空,它将使用301(“Moved Permanently”)重定向到`new_path` 。你可以子类化`RedirectFallbackMiddleware` 并设置 `response_redirect_class` 为`django.http.HttpResponseRedirect` 来使用302 Moved Temporarily 重定向。 21 | + 如果找到匹配的记录而`new_path` 为空,它将发送一个410 (“Gone”) HTTP 头和空(没有内容的)响应。 22 | + 如果没有找到匹配的记录,请求将继续正常处理。 23 | 24 | 这个中间件只针对404 错误启用 —— 不能用于500 或其它状态码。 25 | 26 | 注意`MIDDLEWARE_CLASSES` 的顺序很重要。通常可以将`RedirectFallbackMiddleware` 放在列表的最后,因为它最后执行。 27 | 28 | 更多的信息可以阅读[中间件的文档](http://python.usyiyi.cn/django/topics/http/middleware.html)。 29 | 30 | ## 如何添加、修改和删除重定向 ## 31 | 32 | ### 通过Admin 接口 ### 33 | 34 | 如果你已经启用Django 自动生成的`Admin` 接口,你应该可以在`Admin` 的主页看到“Redirects”部分。编辑这些重定向,就像编辑系统中的其它对象一样。 35 | 36 | ### 通过Python API ### 37 | 38 | `class models.Redirect` 39 | 40 | 重定向通过一个标准的Django 模型表示,位于`django/contrib/redirects/models.py`。你可以通过Django 的数据库API 访问重定向对象。 41 | 42 | ## 中间件 ## 43 | 44 | `class middleware.RedirectFallbackMiddleware` 45 | 46 | 你可以通过创建`RedirectFallbackMiddleware` 的子类并覆盖`response_gone_class` 和/或`response_redirect_class` 来修改中间件使用的`HttpResponse`类。 47 | 48 | `response_gone_class` 49 | 50 | ``` 51 | New in Django 1.7. 52 | ``` 53 | 54 | `HttpResponse` 类,用于找不到请求路径的`Redirect`或找到的`new_path` 值为空的时候。 55 | 56 | 默认为`HttpResponseGone`。 57 | 58 | `response_redirect_class` 59 | 60 | ``` 61 | New in Django 1.7. 62 | ``` 63 | 64 | 处理重定向的`HttpResponse` 类。 65 | 66 | 默认为`HttpResponsePermanentRedirect`。 67 | 68 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Redirects](https://docs.djangoproject.com/en/1.8/ref/contrib/redirects/)。 69 | > 70 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 71 | > 72 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 73 | -------------------------------------------------------------------------------- /14_6_System check framework.md: -------------------------------------------------------------------------------- 1 | # 系统检查框架 2 | 3 | New in Django 1.7\. 4 | 5 | 系统检查框架是为了验证Django项目的一系列静态检查。它可以检测到普遍的问题,并且提供如何修复的提示。这个框架可以被扩展,所以你可以轻易地添加你自己的检查。 6 | 7 | 检查可以由[`check`](../ref/django-admin.html#django-admin-check)命令显式触发。检查会在大多数命令之前隐式触发,包括[`runserver`](../ref/django-admin.html#django-admin-runserver) 和 [`migrate`](../ref/django-admin.html#django-admin-migrate)。由于性能因素,检查不作为在部署中使用的WSGI栈的一部分运行。如果你需要在你的部署服务器上运行系统检查,显式使用[`check`](../ref/django-admin.html#django-admin-check)来触发它们。 8 | 9 | 严重的错误会完全阻止Django命令(像[`runserver`](../ref/django-admin.html#django-admin-runserver))的运行。少数问题会通过控制台来报告。如果你检查了警告的原因,并且愿意无视它,你可以使用你项目设置文件中的[`SILENCED_SYSTEM_CHECKS`](../ref/settings.html#std:setting-SILENCED_SYSTEM_CHECKS) 设置,来隐藏特定的警告。 10 | 11 | [_系统检查参考_](../ref/checks.html)中列出了所有Django可执行的所有检查。 12 | 13 | ## 编写你自己的检查 14 | 15 | 这个框架十分灵活,允许你编写函数,执行任何其他类型的所需检查。下面是一个桩(stub)检查函数的例子: 16 | 17 | ``` 18 | from django.core.checks import register 19 | 20 | @register() 21 | def example_check(app_configs, **kwargs): 22 | errors = [] 23 | # ... your check logic here 24 | return errors 25 | 26 | ``` 27 | 28 | 检查函数_必须_接受 `app_configs`参数;这个参数是要被检查的应用列表。如果是None,检查会运行在项目中_所有_安装的应用上。`**kwargs`参数用于进一步的扩展。 29 | 30 | ### 消息 31 | 32 | 这个函数必须返回消息的列表。如果检查的结果中没有发现问题,检查函数必须返回一个空列表。 33 | 34 | _class _`CheckMessage`(_level_, _msg_, _hint_, _obj=None_, _id=None_) 35 | 36 | 由检查方法产生的警告和错误必须是[`CheckMessage`](#django.core.checks.CheckMessage "django.core.checks.CheckMessage")的示例。[`CheckMessage`](#django.core.checks.CheckMessage "django.core.checks.CheckMessage")的实例封装了一个可报告的错误或者警告。它同时也提供了可应用到消息的上下文或者提示,以及一个用于过滤的唯一的标识符。 37 | 38 | 它的概念非常类似于[_消息框架_](../ref/contrib/messages.html)或者 [_日志框架_](logging.html)中的消息。消息使用表明其严重性的`level` 来标记。 39 | 40 | 构造器的参数是: 41 | 42 | `level` 43 | 44 | The severity of the message. Use one of the 45 | predefined values: `DEBUG`, `INFO`, `WARNING`, `ERROR`, 46 | `CRITICAL`. If the level is greater or equal to `ERROR`, then Django 47 | will prevent management commands from executing. Messages with 48 | level lower than `ERROR` (i.e. warnings) are reported to the console, 49 | but can be silenced. 50 | 51 | `msg` 52 | 53 | A short (less than 80 characters) string describing the problem. The string 54 | should _not_ contain newlines. 55 | 56 | `hint` 57 | 58 | A single-line string providing a hint for fixing the problem. If no hint 59 | can be provided, or the hint is self-evident from the error message, the 60 | hint can be omitted, or a value of `None` can be used. 61 | 62 | `obj` 63 | 64 | Optional. An object providing context for the message (for example, the 65 | model where the problem was discovered). The object should be a model, field, 66 | or manager or any other object that defines `__str__` method (on 67 | Python 2 you need to define `__unicode__` method). The method is used while 68 | reporting all messages and its result precedes the message. 69 | 70 | `id` 71 | 72 | Optional string. A unique identifier for the issue. Identifiers should 73 | follow the pattern `applabel.X001`, where `X` is one of the letters 74 | `CEWID`, indicating the message severity (`C` for criticals, 75 | `E` for errors and so). The number can be allocated by the application, 76 | but should be unique within that application. 77 | 78 | 也有一些快捷方式,使得创建通用级别的消息变得简单。当使用这些方法时你可以忽略`level`参数,因为它由类名称暗示。 79 | 80 | _class _`Debug`(_msg_, _hint_, _obj=None_, _id=None_) 81 | 82 | _class _`Info`(_msg_, _hint_, _obj=None_, _id=None_) 83 | 84 | _class _`Warning`(_msg_, _hint_, _obj=None_, _id=None_) 85 | 86 | _class _`Error`(_msg_, _hint_, _obj=None_, _id=None_) 87 | 88 | _class _`Critical`(_msg_, _hint_, _obj=None_, _id=None_) 89 | 90 | 消息是可比较的。你可以轻易地编写测试: 91 | 92 | ``` 93 | from django.core.checks import Error 94 | errors = checked_object.check() 95 | expected_errors = [ 96 | Error( 97 | 'an error', 98 | hint=None, 99 | obj=checked_object, 100 | id='myapp.E001', 101 | ) 102 | ] 103 | self.assertEqual(errors, expected_errors) 104 | 105 | ``` 106 | 107 | ### 注册和标记检查 108 | 109 | 最后,你的检查函数必须使用系统检查登记处来显式注册。 110 | 111 | `register`(_*tags)(function_) 112 | 113 | 你可以向 `register`传递任意数量的标签来标记你的检查。Tagging checks is useful since it allows you to run only a certain group of checks. For example, to register a compatibility check, you would make the following call: 114 | 115 | ``` 116 | from django.core.checks import register, Tags 117 | 118 | @register(Tags.compatibility) 119 | def my_check(app_configs, **kwargs): 120 | # ... perform compatibility checks and collect errors 121 | return errors 122 | 123 | ``` 124 | 125 | New in Django 1.8\. 126 | 127 | 你可以注册“部署的检查”,它们只和产品配置文件相关,像这样: 128 | 129 | ``` 130 | @register(Tags.security, deploy=True) 131 | def my_check(app_configs, **kwargs): 132 | ... 133 | 134 | ``` 135 | 136 | 这些检查只在 [`--deploy`](../ref/django-admin.html#django-admin-option---deploy) 选项传递给[`check`](../ref/django-admin.html#django-admin-check) 命令的情况下运行。 137 | 138 | 你也可以通过向`register`传递一个可调用对象(通常是个函数)作为第一个函数,将 `register`作为函数使用,而不是一个装饰器。 139 | 140 | 下面的代码和上面等价: 141 | 142 | ``` 143 | def my_check(app_configs, **kwargs): 144 | ... 145 | register(my_check, Tags.security, deploy=True) 146 | 147 | ``` 148 | 149 | Changed in Django 1.8: 150 | 151 | 添加了将注册用作函数的功能。 152 | 153 | ### 字段、模型和管理器检查 154 | 155 | 在一些情况下,你并不需要注册检查函数 -- 你可以直接使用现有的注册。 156 | 157 | 字段、方法和模型管理器都实现了`check()` 方法,它已经使用检查框架注册。如果你想要添加额外的检查,你可以扩展基类中的实现,进行任何你需要的额外检查,并且将任何消息附加到基类生成的消息中。强烈推荐你将每个检查分配到单独的方法中。 158 | 159 | 考虑一个例子,其中你要实现一个叫做`RangedIntegerField`的自定义字段。这个字段向`IntegerField`的构造器中添加`min` 和 `max` 参数。你可能想添加一个检查,来确保用户提供了小于等于最大值的最小值。下面的代码段展示了如何实现这个检查: 160 | 161 | ``` 162 | from django.core import checks 163 | from django.db import models 164 | 165 | class RangedIntegerField(models.IntegerField): 166 | def __init__(self, min=None, max=None, **kwargs): 167 | super(RangedIntegerField, self).__init__(**kwargs) 168 | self.min = min 169 | self.max = max 170 | 171 | def check(self, **kwargs): 172 | # Call the superclass 173 | errors = super(RangedIntegerField, self).check(**kwargs) 174 | 175 | # Do some custom checks and add messages to `errors`: 176 | errors.extend(self._check_min_max_values(**kwargs)) 177 | 178 | # Return all errors and warnings 179 | return errors 180 | 181 | def _check_min_max_values(self, **kwargs): 182 | if (self.min is not None and 183 | self.max is not None and 184 | self.min > self.max): 185 | return [ 186 | checks.Error( 187 | 'min greater than max.', 188 | hint='Decrease min or increase max.', 189 | obj=self, 190 | id='myapp.E001', 191 | ) 192 | ] 193 | # When no error, return an empty list 194 | return [] 195 | 196 | ``` 197 | 198 | 如果你想要向模型管理器添加检查,应该在你的[`Manager`](db/managers.html#django.db.models.Manager "django.db.models.Manager")的子类上执行同样的方法。 199 | 200 | 如果你想要向模型类添加检查,方法也_大致_相同:唯一的不同是检查是类方法,并不是实例方法: 201 | 202 | ``` 203 | class MyModel(models.Model): 204 | @classmethod 205 | def check(cls, **kwargs): 206 | errors = super(MyModel, cls).check(**kwargs) 207 | # ... your own checks ... 208 | return errors 209 | 210 | ``` 211 | 212 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[System check framework](https://docs.djangoproject.com/en/1.8/topics/checks/)。 213 | > 214 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 215 | > 216 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 217 | -------------------------------------------------------------------------------- /14_Other core functionalities.md: -------------------------------------------------------------------------------- 1 | # 其它核心功能 # 2 | 3 | 学习django框架的其他核心功能: 4 | -------------------------------------------------------------------------------- /1_1.md: -------------------------------------------------------------------------------- 1 | # 从零开始 -------------------------------------------------------------------------------- /1_1_2_Quick install guide.md: -------------------------------------------------------------------------------- 1 | # 快速安装指南 # 2 | 3 | 在你开始使用 Django 之前,你需要先安装它。我们有一个 *完整安装指南* 它涵盖了所有的安装步骤和可能遇到的问题;本指南将会给你一个最简单、简洁的安装指引。 4 | 5 | ## 安装 Python ## 6 | 7 | 作为一个 Web 框架,Django 需要使用 Python 。它适用 2.6.5 到 2.7 的所有 Python 版本。它还具有 3.2 和 3.3 版本的实验性支持。所有这些 Python 版本都包含一个轻量级的数据库名叫 SQLite 。因此你现在还不需要建立一个数据库。 8 | 9 | 在 http://www.python.org 获取 Python 。如果你使用 Linux 或者 Mac OS X,那很可能已经安装了 Python 。 10 | 11 | > **在 Jython 使用 Django** 12 | > 13 | > 如果你使用 *Jython* (一个在 Java 平台上实现的 Python ),你需要遵循一些额外的步骤。查看 *在 Jyton 上运行 Python* 获取详细信息。 14 | > 15 | 16 | 在你的终端命令行(shell)下输入 python 来验证是否已经安装 Python ; 你将看到如下类似的提示信息: 17 | 18 | ``` 19 | Python 3.3.3 (default, Nov 26 2013, 13:33:18) 20 | [GCC 4.8.2] on linux 21 | Type "help", "copyright", "credits" or "license" for more information. 22 | >>> 23 | ``` 24 | 25 | ## 建立一个数据库 ## 26 | 27 | 若你需要一个“大”数据库引擎,例如:PostgreSQL ,MySQL ,或 Oracle ,那此步骤是需要的。 想要安装这样一个数据库,请参考 数据库安装信息. 28 | 29 | ## 删除旧版本的 Django ## 30 | 31 | 如果你是从旧版本的 Django 升级安装,你将需要 *在安装新版本之前先卸载旧版本的 Django*. 32 | 33 | ## 安装 Django ## 34 | 35 | 你可以使用下面这简单的三个方式来安装 Django: 36 | 37 | + 安装 *你的操作系统所提供供的发行包* 。对于操作系统提供了 Django 安装包的人来说,这是最快捷的安装方法。 38 | + *安装官方正式发布的版本* 。这是对于想要安装一个稳定版本而不介意运行一个稍旧版本的 Django 的人来说是最好的方式。 39 | + *安装最新的开发版本* 。这对于那些想要尝试最新最棒的特性而不担心运行崭新代码的用户来说是最好的。 40 | 41 | > **总是参考你所使用的对应版本的 Django 文档!** 42 | > 43 | > 如果采用了前两种方式进行安装,你需要注意在文档中标明**在开发版中新增**的标记。这个标记表明这个特性仅适用开发版的 Django ,而他们可能不在官方版本发布。 44 | 45 | ## 验证安装 ## 46 | 47 | 为了验证 Django 被成功的安装到 Python 中,在你的终端命令行 (shell) 下输入 python 。 然后在 Python 提示符下,尝试导入 Django: 48 | 49 | ``` 50 | >>> import django 51 | >>> print(django.get_version()) 52 | 1.8 53 | ``` 54 | 55 | 你可能已安装了其他版本的 Django 。 56 | 57 | ## 安装完成! ## 58 | 59 | 安装完成 – 现在你可以 *学习入门教程*. 60 | 61 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Installation](https://docs.djangoproject.com/en/1.8/intro/install/)。 62 | > 63 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 64 | > 65 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 66 | -------------------------------------------------------------------------------- /1_2.md: -------------------------------------------------------------------------------- 1 | # 教程 -------------------------------------------------------------------------------- /1_2_4_Part 4 Forms and generic views.md: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | 3 | # 编写你的第一个 Django 程序 第4部分 # 4 | 5 | 本教程上接 教程 第3部分 。我们将 继续开发 Web-poll 应用并且关注在处理简单的窗体和优化我们的代码。 6 | 7 | ## 编写一个简单的窗体 ## 8 | 9 | 让我们把在上一篇教程中编写的 poll 的 detail 模板更新下,在模板中包含 HTML 的
组件: 10 | 11 | ``` 12 |

{{ poll.question }}

13 | 14 | {% if error_message %}

{{ error_message }}

{% endif %} 15 | 16 | 17 | {% csrf_token %} 18 | {% for choice in poll.choice_set.all %} 19 | 20 |
21 | {% endfor %} 22 | 23 |
24 | ``` 25 | 简单的总结下: 26 | 27 | + 上面的模板中为每个投票选项设置了一个单选按钮。每个单选按钮的 value 是投票选项对应的 ID 。每个单选按钮的 name 都是 ``“choice”``。这意味着,当有人选择了一个单选按钮并提交了表单,将会发送 的 POST 数据是 ``choice=3``。这是 HTML 表单中的基本概念。 28 | + 我们将 form 的 action 设置为 `{% url 'polls:vote' poll.id %},以及设置了 `method="post"` 。使用 method="post" ( 而不是 method="get") 是非常重要的,因为这种提交表单的方式会改变服务器端的数据。 当你创建一个表单为了修改服务器端的数据时,请使用 method="post" 。这不是 Django 特定的技巧;这是优秀的 Web 开发实践。 29 | + forloop.counter 表示 for 标签在循环中已经循环过的次数 30 | + 由于我们要创建一个POST form ( 具有修改数据的功能 ),我们需要担心跨站点请求伪造 ( Cross Site Request Forgeries )。 值得庆幸的是,你不必太担心这一点,因为 Django 自带了一个非常容易使用的系统来防御它。 总之,所有的 POST form 针对内部的 URLs 时都应该使用 `{% csrf_token %}` 模板标签。 31 | 32 | 现在,让我们来创建一个 Django 视图来处理提交的数据。 记得吗?在 教程 第3部分 中,我们为 polls 应用创建了一个 URLconf 配置中包含有这一行代码: 33 | 34 | ``` 35 | url(r'^(?P\d+)/vote/$', views.vote, name='vote'), 36 | ``` 37 | 38 | 我们还创建了一个虚拟实现的 vote() 函数。让我们创建一个真实版本吧。在 polls/views.py 中添加如下代码: 39 | 40 | ``` 41 | from django.shortcuts import get_object_or_404, render 42 | from django.http import HttpResponseRedirect, HttpResponse 43 | from django.core.urlresolvers import reverse 44 | from polls.models import Choice, Poll 45 | # ... 46 | def vote(request, poll_id): 47 | p = get_object_or_404(Poll, pk=poll_id) 48 | try: 49 | selected_choice = p.choice_set.get(pk=request.POST['choice']) 50 | except (KeyError, Choice.DoesNotExist): 51 | # Redisplay the poll voting form. 52 | return render(request, 'polls/detail.html', { 53 | 'poll': p, 54 | 'error_message': "You didn't select a choice.", 55 | }) 56 | else: 57 | selected_choice.votes += 1 58 | selected_choice.save() 59 | # Always return an HttpResponseRedirect after successfully dealing 60 | # with POST data. This prevents data from being posted twice if a 61 | # user hits the Back button. 62 | return HttpResponseRedirect(reverse('polls:results', args=(p.id,))) 63 | ``` 64 | 65 | 在这代码中有些内容还未在本教程中提到过: 66 | 67 | request.POST 是一个类似字典的对象,可以让你 通过关键字名称来获取提交的数据。在本例中, request.POST['choice'] 返回了所选择的投票项目的 ID ,以字符串的形式。 request.POST 的值永远是字符串形式的。 68 | 69 | 请注意 Django 也同样的提供了通过 request.GET 获取 GET 数据的方法 – 但是在代码中我们明确的使用了 request.POST 方法,以确保数据是通过 POST 方法来修改的。 70 | 71 | 如果 choice 未在 POST 数据中提供 request.POST['choice'] 将抛出 KeyError 当未给定 choice 对象时上面的代码若检测到抛出的是 KeyError 异常就会向 poll 显示一条错误信息。 72 | 73 | 在增加了投票选项的统计数后,代码返回一个 HttpResponseRedirect 对象而不是常见的 HttpResponse 对象。 HttpResponseRedirect 对象需要一个参数:用户将被重定向的 URL (请继续看下去在这情况下我们是如何构造 URL ) 。 74 | 75 | 就像上面用 Python 作的注释那样,当成功的处理了 POST 数据后你应该总是返回一个 HttpResponseRedirect 对象。 这个技巧不是特定于 Django 的;它是优秀的 Web 开发实践。 76 | 77 | 在本例中,我们在 HttpResponseRedirect 的构造方法中使用了 reverse() 函数。 此函数有助于避免在视图中硬编码 URL 的功能。它指定了我们想要的跳转的视图函数名以及视图函数中 URL 模式相应的可变参数。在本例中,我们使用了教程 第3部分中的 URLconf 配置, reverse() 将会返回类似如下所示的字符串 78 | 79 | ``` 80 | '/polls/3/results/' 81 | ``` 82 | 83 | ... 在此 3 就是 p.id 的值。该重定向 URL 会调用 'results' 视图并显示最终页面。 84 | 85 | 正如在教程 第3部分提到的,``request`` 是一个 HttpRequest 对象。想了解 HttpRequest 对象更多的内容,请参阅 request 和 response 文档 。 86 | 87 | 当有人投票后,``vote()`` 视图会重定向到投票结果页。让我们来编写这个视图 88 | 89 | ``` 90 | def results(request, poll_id): 91 | poll = get_object_or_404(Poll, pk=poll_id) 92 | return render(request, 'polls/results.html', {'poll': poll}) 93 | ``` 94 | 95 | 这几乎和 教程 第3部分 中的 detail() 视图完全一样。 唯一的区别就是模板名称。 稍后我们会解决这个冗余问题。 96 | 97 | 现在,创建一个 polls/results.html 模板: 98 | 99 | ``` 100 |

{{ poll.question }}

101 | 102 |
    103 | {% for choice in poll.choice_set.all %} 104 |
  • {{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
  • 105 | {% endfor %} 106 |
107 | 108 | Vote again? 109 | ``` 110 | 111 | 现在,在浏览器中访问 /polls/1/ 并完成投票。每次投票后你将会看到结果页数据都有更新。 如果你没有选择投票选项就提交了,将会看到错误的信息。 112 | 113 | ## 使用通用视图:优化代码 ## 114 | 115 | detail() ( 在 教程 第3部分 中) 和 results() 视图 都很简单 – 并且还有上面所提到的冗余问题。``index()`` 用于显示 polls 列表的 index() 视图 (也在教程 第3部分中),也是存在类似的问题。 116 | 117 | 这些视图代表了基本的 Web 开发中一种常见的问题: 根据 URL 中的参数从数据库中获取数据,加载模板并返回渲染后的内容。由于这类现象很 常见,因此 Django 提供了一种快捷方式,被称之为“通用视图”系统。 118 | 119 | 通用视图抽象了常见的模式,以至于你不需要编写 Python 代码来编写一个应用。 120 | 121 | 让我们把 poll 应用修改成使用通用视图系统的应用,这样我们就能删除删除一些我们自己的代码了。 我们将采取以下步骤来进行修改: 122 | 123 | + 修改 URLconf 。 124 | + 删除一些旧的,不必要的视图。 125 | + 修正 URL 处理到对应的新视图。 126 | 127 | 请继续阅读了解详细的信息。 128 | 129 | > 为什么要重构代码? 130 | > 131 | > 通常情况下,当你编写一个 Django 应用时,你会评估下通用视图是否适合解决你的问题, 如果适合你就应该从一开始就使用它,而不是进行到一半才重构你的代码。 但是本教程直到现在都故意集中介绍“硬编码”视图,是为了专注于核心概念上。 132 | > 133 | > 就像你在使用计算器前需要知道基本的数学知识一样。 134 | 135 | ## 修改 URLconf ## 136 | 137 | 首先,打开 polls/urls.py 的 URLconf 配置文件并修改成如下所示样子 138 | 139 | ``` 140 | from django.conf.urls import patterns, url 141 | from django.views.generic import DetailView, ListView 142 | from polls.models import Poll 143 | 144 | urlpatterns = patterns('', 145 | url(r'^$', 146 | ListView.as_view( 147 | queryset=Poll.objects.order_by('-pub_date')[:5], 148 | context_object_name='latest_poll_list', 149 | template_name='polls/index.html'), 150 | name='index'), 151 | url(r'^(?P\d+)/$', 152 | DetailView.as_view( 153 | model=Poll, 154 | template_name='polls/detail.html'), 155 | name='detail'), 156 | url(r'^(?P\d+)/results/$', 157 | DetailView.as_view( 158 | model=Poll, 159 | template_name='polls/results.html'), 160 | name='results'), 161 | url(r'^(?P\d+)/vote/$', 'polls.views.vote', name='vote'), 162 | ) 163 | ``` 164 | 165 | ## 修改 views ## 166 | 167 | 在这我们将使用两个通用视图: ListView 和 DetailView 。这两个视图分别用于显示两种抽象概念 “显示一系列对象的列表” 和 “显示一个特定类型的对象的详细信息页”。 168 | 169 | + 每个视图都需要知道使用哪个模型数据。因此需要提供将要使用的 model 参数。 170 | + DetailView 通用视图期望从 URL 中捕获名为 "pk" 的主键值,因此我们将 poll_id 改为 pk 。 171 | 172 | 默认情况下, DetailView 通用视图使用名为 <应用名>/<模型名>_detail.html 的模板。在我们的例子中,将使用名为 "polls/poll_detail.html" 的模板。 template_name 参数是告诉 Django 使用指定的模板名,而不是使用自动生成的默认模板名。 我们也指定了 results 列表视图的 template_name – 这确保了 results 视图和 detail 视图渲染时会有不同的外观,虽然它们有一个 DetailView 隐藏在幕后。 173 | 174 | 同样的,~django.views.generic.list.ListView 通用视图使用的默认模板名为 <应用名>/<模型名>_list.html ;我们指定了 template_name 参数告诉 ListView 使用已经存在的 "polls/index.html" 模板。 175 | 176 | 在之前的教程中,模板提供的上下文中包含了 poll 和 latest_poll_list 上下文变量。在 DetailView 中 poll 变量是自动提供的 – 因为我们使用了一个 Django 模型 (Poll) ,Django 能够为上下文变量确定适合的名称。 另外 ListView 自动生成的上下文变量名是 poll_list 。若要覆盖此变量我们需要提供 context_object_name 选项, 我们想要使用 latest_poll_list 来替代它。作为一种替代方式,你可以改变你的模板来 匹配新的默认的上下文变量 – 但它是一个非常容易地告诉 Django 使用你想要的变量的方式。 177 | 178 | 现在你可以在 polls/views.py 中删除 index() , detail() 和 results() 视图了。 我们不需要它们了 – 它们已替换为通用视图了。你也可以删除不再需要的 HttpResponse 导入包了。 179 | 180 | 运行服务器,并且使用下基于通用视图的新投票应用。 181 | 182 | 有关通用视图的完整详细信息,请参阅 通用视图文档. 183 | 184 | 当你熟悉了窗体和通用视图后,请阅读 教程 第5部分 来学习测试我们的投票应用。 185 | 186 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Part 4: Forms and generic views](https://docs.djangoproject.com/en/1.8/intro/tutorial04/)。 187 | > 188 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 189 | > 190 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 191 | 192 | {% endraw %} 193 | -------------------------------------------------------------------------------- /1_2_6_Part 6 Static files.md: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | 3 | # 编写你的第一个Django应用,第6部分 # 4 | 5 | 本教程上接教程 5。 我们已经建立一个测试过的网页投票应用,现在我们将添加一张样式表和一张图片。 6 | 7 | 除了由服务器生成的HTML文件外,网页应用一般需要提供其它必要的文件 —— 比如图片文件、JavaScript脚本和CSS样式表 —— 来为用户呈现出一个完整的网站。 在Django中,我们将这些文件称为“静态文件”。 8 | 9 | 对于小型项目,这不是个大问题,因为你可以将它们放在你的网页服务器可以访问到的地方。 然而,在大一点的项目中 —— 尤其是那些由多个应用组成的项目 —— 处理每个应用提供的多个静态文件集合开始变得很难。 10 | 11 | 这正是django.contrib.staticfiles的用途:它收集每个应用(和任何你指定的地方)的静态文件到一个单独的位置,这个位置在线上可以很容易维护。 12 | 13 | ## 自定义你的应用的外观 ## 14 | 15 | 首先在你的polls中创建一个static目录。Django将在那里查找静态文件,与Django如何polls/templates/内部的模板类似。 16 | 17 | Django 的 STATICFILES_FINDERS 设置包含一个查找器列表,它们知道如何从各种源找到静态文件。 其中默认的一个是AppDirectoriesFinder,它在每个INSTALLED_APPS下查找“static”子目录,就像刚刚在polls中创建的一样。管理站点也为它的静态文件使用相同的目录结构。 18 | 19 | 在你刚刚创建的static目录中,创建另外一个目录polls并在它下面创建一个文件style.css。换句话讲,你的样式表应该位于polls/static/polls/style.css。因为AppDirectoriesFinder 静态文件查找器的工作方式,你可以通过polls/style.css在Django中访问这个静态文件,与你如何访问模板的路径类似。 20 | 21 | > 静态文件的命名空间 22 | > 23 | > 与模板类似,我们可以家那个我们的静态文件直接放在polls/static(而不是创建另外一个polls 子目录),但实际上这是一个坏主意。Django将使用它所找到的第一个文件名符合要求的静态文件,如果在你的不同应用中存在两个同名的静态文件,Django将无法区分它们。 我们需要告诉Django该使用其中的哪一个,最简单的方法就是为它们添加命名空间。 也就是说,将这些静态文件放进以它们所在的应用的名字命名的另外一个目录下。 24 | 25 | 将下面的代码放入样式表中 (polls/static/polls/style.css): 26 | 27 | ``` 28 | polls/static/polls/style.css 29 | li a { 30 | color: green; 31 | } 32 | ``` 33 | 34 | 下一步,在polls/templates/polls/index.html的顶端添加如下内容 : 35 | 36 | ``` 37 | polls/templates/polls/index.html 38 | {% load staticfiles %} 39 | 40 | 41 | ``` 42 | 43 | `{% load staticfiles %}` 从staticfiles模板库加载`{% static %}` 模板标签。`{% static %}`模板标签会生成静态文件的绝对URL。 44 | 45 | 这就是你在开发过程中,所需要对静态文件做的所有处理。 重新加载 http://localhost:8000/polls/ ,你应该会看到Question的超链接变成了绿色(Django的风格!),这意味着你的样式表被成功导入。 46 | 47 | ## 添加一张背景图片 ## 48 | 49 | 下一步,我们将创建一个子目录来存放图片。 在polls/static/polls/目录中创建一个 images 子目录。在这个目录中,放入一张图片background.gif。换句话,将你的图片放在 polls/static/polls/images/background.gif。 50 | 51 | 然后,向你的样式表添加(polls/static/polls/style.css): 52 | 53 | ``` 54 | polls/static/polls/style.css 55 | body { 56 | background: white url("images/background.gif") no-repeat right bottom; 57 | } 58 | ``` 59 | 60 | 重新加载 http://localhost:8000/polls/ ,你应该在屏幕的右下方看到载入的背景图片。 61 | 62 | > 警告: 63 | > 64 | > 当然,`{% static %}`模板标签不能用在静态文件(比如样式表)中,因为他们不是由Django生成的。 你应该永远使用相对路径来相互链接静态文件,因为这样你可以改变STATIC_URL ( static模板标签用它来生成URLs)而不用同时修改一大堆静态文件的路径。 65 | 66 | 这些知识基础。关于静态文件设置的更多细节和框架中包含的其它部分,参见静态文件 howto 和静态文件参考。部署静态文件讨论如何在真实的服务器上使用静态文件。 67 | 68 | ## 下一步? ## 69 | 70 | 新手教程到此结束。 在这期间,你可能想要在如何查看文档中了解文档的结构和查找相关信息方法。 71 | 72 | 如果你熟悉Python 打包的技术,并且对如何将投票应用制作成一个“可重用的应用”感兴趣,请看高级教程:如何编写可重用的应用。 73 | 74 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Part 6: Static files](https://docs.djangoproject.com/en/1.8/intro/tutorial06/)。 75 | > 76 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 77 | > 78 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 79 | 80 | {% endraw %} 81 | -------------------------------------------------------------------------------- /1_3.md: -------------------------------------------------------------------------------- 1 | # 高级教程 -------------------------------------------------------------------------------- /1_3_1_How to write reusable apps.md: -------------------------------------------------------------------------------- 1 | # 高级教程:如何编写可重用的应用 # 2 | 3 | 本高级教程上接教程 6。我们将把我们的网页投票转换成一个独立的Python包,这样你可以在其它项目中重用或者分享给其它人。 4 | 5 | 如果你最近没有完成教程1–6,我们建议你阅读它们使得你的示例项目与下面描述的相匹配。 6 | 7 | ## 可重用很重要 ## 8 | 9 | 设计、构建、测试和维护一个网页应用有许多工作要做。许多Python 和 Django 项目都有常见的共同问题。如果我们可以节省一些这些重复的工作会不会很棒? 10 | 11 | 可重用性是Python 中一种生活的态度。Python包索引 (PyPI) 具有广泛的包,你可以在你自己的Python程序中使用。调查一下Django Packages中已经存在的可重用的应用,你可以结合它们到你的项目。Django 自身也只是一个Python 包。这意味着你可以获取已经存在的Python包和Django应用并将它们融合到你自己的网页项目。你只需要编写你项目的独特的部分。 12 | 13 | 比如说,你正在开始一个新的项目,需要一个像我们正在编写的投票应用。你如何让该应用可重用?幸运的是,你已经在正确的道路上。在教程 3中,我们看到我们可以如何使用include将投票应用从项目级别的URLconf 解耦。在本教程中,我们将更进一步,让你的应用在新的项目中容易地使用并随时可以发布给其它人安装和使用。 14 | 15 | > 包?应用? 16 | > 17 | > Python 包 提供的方式是分组相关的Python 代码以容易地重用。一个包包含一个或多个Python代码(也叫做“模块”)。 18 | > 19 | > 包可以通过import foo.bar 或from foo import bar 导入。如果一个目录(例如polls)想要形成一个包,它必须包含一个特殊的文件__init__.py,即使这个文件为空。 20 | > 21 | > 一个Django 应用 只是一个Python包,它特意用于Django项目中。一个应用可以使用常见的Django 约定,例如具有models、tests、urls和views 子模块。 22 | > 23 | > 后面我们使用打包这个词来描述将一个Python包变得让其他人易于安装的过程。我们知道,这可能有点绕人。 24 | 25 | ## 你的项目和你的可重用的应用 ## 26 | 27 | 经过前面的教程之后,我们的项目应该看上去像这样: 28 | 29 | ``` 30 | mysite/ 31 | manage.py 32 | mysite/ 33 | __init__.py 34 | settings.py 35 | urls.py 36 | wsgi.py 37 | polls/ 38 | __init__.py 39 | admin.py 40 | migrations/ 41 | __init__.py 42 | 0001_initial.py 43 | models.py 44 | static/ 45 | polls/ 46 | images/ 47 | background.gif 48 | style.css 49 | templates/ 50 | polls/ 51 | detail.html 52 | index.html 53 | results.html 54 | tests.py 55 | urls.py 56 | views.py 57 | templates/ 58 | admin/ 59 | base_site.html 60 | ``` 61 | 62 | 你在教程 2中创建了mysite/templates ,在教程 3中创建了polls/templates。 现在你可能更加清晰为什么我们为项目和应用选择单独的模板目录:属于投票应用的部分全部在polls中。它使得该应用自包含且更加容易丢到一个新的项目中。 63 | 64 | 现在可以拷贝polls目录到一个新的Django项目并立即使用。然后它还不能充分准备好到可以立即发布。由于这点,我们需要打包这个应用来让它对其他人易于安装。 65 | 66 | ## 安装一些前提条件 ## 67 | 68 | Python 打包的目前状态因为有多种工具而混乱不堪。对于本教程,我们打算使用setuptools来构建我们的包。它是推荐的打包工具(已经与distribute 分支合并)。我们还将使用pip来安装和卸载它。现在你应该安装这两个包。如果你需要帮助,你可以参考如何使用pip安装Django。你可以使用同样的方法安装setuptools。 69 | 70 | ## 打包你的应用 ## 71 | 72 | Python packaging refers to preparing your app in a specific format that can be easily installed and used. Django 自己是以非常相似的方式打包起来的。对于一个像polls这样的小应用,这个过程不是太难。 73 | 74 | 首先,在你的Django项目之外,为polls创建一个父目录。称这个目录为django-polls。 75 | 76 | > 为你的应用选择一个名字 77 | > 78 | > 让为你的包选择一个名字时,检查一下PyPI中的资源以避免与已经存在的包有名字冲突。当创建一个要发布的包时,在你的模块名字前面加上django-通常很有用。 这有助于其他正在查找Django应用的人区分你的应用是专门用于Django的。 79 | > 80 | > 应用的标签(应用的包的点分路径的最后部分)在INSTALLED_APPS中必须唯一。避免使用与Django的contrib 包 中任何一个使用相同的标签,例如auth、admin和messages。 81 | 82 | 将polls 目录移动到django-polls目录。 83 | 84 | 创建一个包含一些内容的文件django-polls/README.rst: 85 | 86 | ``` 87 | django-polls/README.rst 88 | ===== 89 | Polls 90 | ===== 91 | 92 | Polls is a simple Django app to conduct Web-based polls. For each 93 | question, visitors can choose between a fixed number of answers. 94 | 95 | Detailed documentation is in the "docs" directory. 96 | 97 | Quick start 98 | ----------- 99 | 100 | 1. Add "polls" to your INSTALLED_APPS setting like this:: 101 | 102 | INSTALLED_APPS = ( 103 | ... 104 | 'polls', 105 | ) 106 | 107 | 2. Include the polls URLconf in your project urls.py like this:: 108 | 109 | url(r'^polls/', include('polls.urls')), 110 | 111 | 3. Run `python manage.py migrate` to create the polls models. 112 | 113 | 4. Start the development server and visit http://127.0.0.1:8000/admin/ 114 | to create a poll (you'll need the Admin app enabled). 115 | 116 | 5. Visit http://127.0.0.1:8000/polls/ to participate in the poll. 117 | ``` 118 | 119 | 创建一个django-polls/LICENSE文件。选择License超出本教程的范围,但值得一说的是公开发布的代码如果没有License是毫无用处的。Django和许多与Django兼容的应用以BSD License 发布;然而,你可以随便挑选自己的License。只需要知道你的License的选则将影响谁能够使用你的代码。 120 | 121 | 下一步我们将创建一个setup.py 文件,它提供如何构建和安装该应用的详细信息。该文件完整的解释超出本教程的范围,setuptools 文档 有很好的解释。创建一个文件django-polls/setup.py,其内容如下: 122 | 123 | ``` 124 | django-polls/setup.py 125 | import os 126 | from setuptools import setup 127 | 128 | with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: 129 | README = readme.read() 130 | 131 | # allow setup.py to be run from any path 132 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 133 | 134 | setup( 135 | name='django-polls', 136 | version='0.1', 137 | packages=['polls'], 138 | include_package_data=True, 139 | license='BSD License', # example license 140 | description='A simple Django app to conduct Web-based polls.', 141 | long_description=README, 142 | url='http://www.example.com/', 143 | author='Your Name', 144 | author_email='yourname@example.com', 145 | classifiers=[ 146 | 'Environment :: Web Environment', 147 | 'Framework :: Django', 148 | 'Intended Audience :: Developers', 149 | 'License :: OSI Approved :: BSD License', # example license 150 | 'Operating System :: OS Independent', 151 | 'Programming Language :: Python', 152 | # Replace these appropriately if you are stuck on Python 2. 153 | 'Programming Language :: Python :: 3', 154 | 'Programming Language :: Python :: 3.2', 155 | 'Programming Language :: Python :: 3.3', 156 | 'Topic :: Internet :: WWW/HTTP', 157 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 158 | ], 159 | ) 160 | ``` 161 | 162 | 默认只有Python模块和包会包含进包中。如果需要包含额外的文件,我们需要创建一个MANIFEST.in文件。上一步提到的setuptools 文档对这个文件有更详细的讨论。如果要包含模板、README.rst和我们的LICENSE 文件,创建一个文件django-polls/MANIFEST.in,其内容如下: 163 | 164 | ``` 165 | django-polls/MANIFEST.in 166 | include LICENSE 167 | include README.rst 168 | recursive-include polls/static * 169 | recursive-include polls/templates * 170 | ``` 171 | 172 | 将详细的文档包含进你的应用中,它是可选的,但建议你这样做。创建一个空的目录django-polls/docs用于将来存放文档。向django-polls/MANIFEST.in添加另外一行: 173 | 174 | ``` 175 | recursive-include docs * 176 | ``` 177 | 178 | 注意docs不会包含进你的包中除非你添加一些文件到它下面。许多Django应用还通过类似readthedocs.org这样的站点提供它们的在线文档. 179 | 180 | 试着通过python setup.py sdist 构建你的包(从django-polls的内部运行)。这创建一个dist目录并构建一个新包django-polls-0.1.tar.gz。 181 | 182 | 更多关于打包的信息,参见Python 的 打包和分发项目的教程。 183 | 184 | ## 使用你自己的包 ## 185 | 186 | 因为,我们将polls 目录移到项目的目录之外,它不再工作了。我们将通过安装我们的新的django-polls包来修复它。 187 | 188 | > 安装成某个用户的库 189 | > 190 | > 以下的步骤将安装django-polls 成某个用户的库。根据用户安装相比系统范围的安装具有许多优点,例如用于没有管理员权限的系统上以及防止你的包影响系统的服务和机器上的其它用户。 191 | > 192 | > 注意根据用户的安装仍然可以影响以该用户身份运行的系统工具,所以virtualenv 是更健壮的解决办法(见下文)。 193 | 194 | 安装这个包,使用pip(你已经安装好它了,对吧?): 195 | 196 | ``` 197 | pip install --user django-polls/dist/django-polls-0.1.tar.gz 198 | ``` 199 | 200 | 如果幸运,你的Django 项目现在应该可以再次正确工作。请重新运行服务器以证实这点。 201 | 202 | 若要卸载这个包,使用pip: 203 | 204 | ``` 205 | pip uninstall django-polls 206 | ``` 207 | 208 | ## 发布你的应用: ## 209 | 210 | 既然我们已经打包并测试过django-polls,是时候与世界共享它了!要不是它仅仅是个例子,你现在可以: 211 | 212 | + 将这个包用邮件发送给朋友。 213 | + 上传这个包到你的网站上。 214 | + 上传这个包到一个公开的仓库,例如Python 包索引 (PyPI)。packaging.python.org has a good tutorial for doing this. 215 | 216 | ## 使用 virtualenv 安装Python 包 ## 217 | 218 | 前面,我们将poll 安装成一个用户的库。它有一些缺点: 219 | 220 | + 修改这个用户的库可能影响你的系统上的其它Python 软件。 221 | + 你将不可以运行这个包的多个版本(或者具有相同名字的其它包)。 222 | 223 | 特别是一旦你维护几个Django项目,这些情况就会出现。如果确实出现,最好的解决办法是使用virtualenv。这个工具允许你维护多个分离的Python环境,每个都具有它自己的库和包的命名空间。 224 | 225 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[How to write reusable apps](https://docs.djangoproject.com/en/1.8/intro/reusable-apps/)。 226 | > 227 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 228 | > 229 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 230 | -------------------------------------------------------------------------------- /1_First steps.md: -------------------------------------------------------------------------------- 1 | # 新手入门 # 2 | 3 | 初次接触 Django 或编程吗? 从这里开始吧! 4 | -------------------------------------------------------------------------------- /2_1.md: -------------------------------------------------------------------------------- 1 | # 模型 -------------------------------------------------------------------------------- /2_1_3_Meta options.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 模型元选项 # 6 | 7 | 这篇文档阐述了所有可用的元选项,你可以在你模型的Meta类中设置他们。 8 | 9 | ## 可用的元选项 ## 10 | 11 | ### abstract ### 12 | 13 | **Options.abstract** 14 | 15 | 如果 abstract = True, 就表示模型是 抽象基类 (abstract base class). 16 | 17 | ### app_label ### 18 | 19 | **Options.app_label** 20 | 21 | 如果你的模型定义在默认的 models.py 之外(比如,你现在用的模型在 myapp.models 子模块当中),你必须告诉 Django 该模型属于哪个应用: 22 | 23 | ``` 24 | app_label = 'myapp' 25 | ``` 26 | 27 | ``` 28 | Django 1.7中新增: 29 | 30 | 一个应用中,定义在models 模块以外的模型,不再需要app_label。 31 | ``` 32 | 33 | ### db_table ### 34 | 35 | **Options.db_table** 36 | 37 | 该模型所用的数据表的名称: 38 | 39 | ``` 40 | db_table = 'music_album' 41 | ``` 42 | 43 | #### 数据表名称 #### 44 | 45 | 为了节省时间,Django 会根据模型类的名称和包含它的app名称自动指定数据表名称,一个模型的数据表名称,由这个模型的“应用标签”(在 manage.py startapp中使用的名称)之间加上下划线组成。 46 | 47 | 举个例子, bookstore应用(使用 manage.py startapp bookstore 创建),里面有个名为 Book的模型,那数据表的名称就是 bookstore_book 。 48 | 49 | 使用 Meta类中的 db_table 参数来覆写数据表的名称。 50 | 51 | 数据表名称可以是 SQL 保留字,也可以包含不允许出现在 Python 变量中的特殊字符,这是因为 Django 会自动给列名和表名添加引号。 52 | 53 | > 在 MySQL中使用小写字母为表命名 54 | > 55 | > 当你通过db_table覆写表名称时,强烈推荐使用小写字母给表命名,特别是如果你用了MySQL作为后端。详见MySQL注意事项 。 56 | 57 | > Oracle中表名称的引号处理 58 | > 59 | > 为了遵从Oracle中30个字符的限制,以及一些常见的约定,Django会缩短表的名称,而且会把它全部转为大写。在db_table的值外面加上引号来避免这种情况: 60 | > 61 | ``` 62 | db_table = '"name_left_in_lowercase"' 63 | ``` 64 | > 65 | > 这种带引号的名称也可以用于Django所支持的其他数据库后端,但是除了Oracle,引号不起任何作用。详见 Oracle 注意事项 。 66 | 67 | ### db_tablespace ### 68 | 69 | **Options.db_tablespace** 70 | 71 | 当前模型所使用的数据库表空间 的名字。默认值是项目设置中的DEFAULT_TABLESPACE,如果它存在的话。如果后端并不支持表空间,这个选项可以忽略。 72 | 73 | ### default_related_name ### 74 | 75 | **Options.default_related_name** 76 | 77 | ``` 78 | Django 1.8中新增: 79 | ``` 80 | 81 | 这个名字会默认被用于一个关联对象到当前对象的关系。默认为 _set。 82 | 83 | 由于一个字段的反转名称应该是唯一的,当你给你的模型设计子类时,要格外小心。为了规避名称冲突,名称的一部分应该含有'%(app_label)s'和'%(model_name)s',它们会被应用标签的名称和模型的名称替换,二者都是小写的。详见抽象模型的关联名称。 84 | 85 | ### get_latest_by ### 86 | 87 | **Options.get_latest_by** 88 | 89 | 模型中某个可排序的字段的名称,比如DateField、DateTimeField或者IntegerField。它指定了Manager的latest()和earliest()中使用的默认字段。 90 | 91 | 例如: 92 | 93 | ``` 94 | get_latest_by = "order_date" 95 | ``` 96 | 97 | 详见latest() 文档。 98 | 99 | ### managed ### 100 | 101 | **Options.managed** 102 | 103 | 默认为True,意思是Django在migrate命令中创建合适的数据表,并且会在 flush 管理命令中移除它们。换句话说,Django会管理这些数据表的生命周期。 104 | 105 | 如果是False,Django 就不会为当前模型创建和删除数据表。如果当前模型表示一个已经存在的,通过其它方法建立的数据库视图或者数据表,这会相当有用。这是设置为managed=False时唯一的不同之处。. 模型处理的其它任何方面都和平常一样。这包括: 106 | 107 | + 如果你不声明它的话,会向你的模型中添加一个自增主键。为了避免给后面的代码读者带来混乱,强烈推荐你在使用未被管理的模型时,指定数据表中所有的列。 108 | + 如果一个带有managed=False的模型含有指向其他未被管理模型的ManyToManyField,那么多对多连接的中介表也不会被创建。但是,一个被管理模型和一个未被管理模型之间的中介表会被创建。 109 | 110 | 如果你需要修改这一默认行为,创建中介表作为显式的模型(设置为managed),并且使用ManyToManyField.through为你的自定义模型创建关联。 111 | 112 | 对于带有managed=False的模型的测试,你要确保在测试启动时建立正确的表。 113 | 114 | 如果你对修改模型类在Python层面的行为感兴趣,你可以设置 managed=False ,并且创建一个已经存在模型的部分。但是这种情况下使用代理模型才是更好的方法。 115 | 116 | ### order_with_respect_to ### 117 | 118 | **Options.order_with_respect_to** 119 | 120 | 按照给定的字段把这个对象标记为”可排序的“。这一属性通常用到关联对象上面,使它在父对象中有序。比如,如果Answer和 Question相关联,一个问题有至少一个答案,并且答案的顺序非常重要,你可以这样做: 121 | 122 | ``` 123 | from django.db import models 124 | 125 | class Question(models.Model): 126 | text = models.TextField() 127 | # ... 128 | 129 | class Answer(models.Model): 130 | question = models.ForeignKey(Question) 131 | # ... 132 | 133 | class Meta: 134 | order_with_respect_to = 'question' 135 | ``` 136 | 137 | 当order_with_respect_to 设置之后,模型会提供两个用于设置和获取关联对象顺序的方法:get_RELATED_order() 和set_RELATED_order(),其中RELATED是小写的模型名称。例如,假设一个 Question 对象有很多相关联的Answer对象,返回的列表中含有相关联Answer对象的主键: 138 | 139 | ``` 140 | >>> question = Question.objects.get(id=1) 141 | >>> question.get_answer_order() 142 | [1, 2, 3] 143 | ``` 144 | 145 | 与Question对象相关联的Answer对象的顺序,可以通过传入一格包含Answer 主键的列表来设置: 146 | 147 | ``` 148 | >>> question.set_answer_order([3, 1, 2]) 149 | ``` 150 | 151 | 相关联的对象也有两个方法, get_next_in_order() 和get_previous_in_order(),用于按照合适的顺序访问它们。假设Answer对象按照 id来排序: 152 | 153 | ``` 154 | >>> answer = Answer.objects.get(id=2) 155 | >>> answer.get_next_in_order() 156 | 157 | >>> answer.get_previous_in_order() 158 | 159 | ``` 160 | 161 | > 修改 order_with_respect_to 162 | > 163 | > order_with_respect_to属性会添加一个额外的字段(/数据表中的列)叫做_order,所以如果你在首次迁移之后添加或者修改了order_with_respect_to属性,要确保执行和应用了合适的迁移操作。 164 | 165 | ### ordering ### 166 | 167 | **Options.ordering** 168 | 169 | 对象默认的顺序,获取一个对象的列表时使用: 170 | 171 | ``` 172 | ordering = ['-order_date'] 173 | ``` 174 | 175 | 它是一个字符串的列表或元组。每个字符串是一个字段名,前面带有可选的“-”前缀表示倒序。前面没有“-”的字段表示正序。使用"?"来表示随机排序。 176 | 177 | 例如,要按照pub_date字段的正序排序,这样写: 178 | 179 | ``` 180 | ordering = ['pub_date'] 181 | ``` 182 | 183 | 按照pub_date字段的倒序排序,这样写: 184 | 185 | ``` 186 | ordering = ['-pub_date'] 187 | ``` 188 | 189 | 先按照pub_date的倒序排序,再按照 author 的正序排序,这样写: 190 | 191 | ``` 192 | ordering = ['-pub_date', 'author'] 193 | ``` 194 | 195 | > 警告 196 | > 197 | > 排序并不是没有任何代价的操作。你向ordering属性添加的每个字段都会产生你数据库的开销。你添加的每个外键也会隐式包含它的默认顺序。 198 | 199 | ### permissions ### 200 | 201 | **Options.permissions** 202 | 203 | 设置创建对象时权限表中额外的权限。增加、删除和修改权限会自动为每个模型创建。这个例子指定了一种额外的权限,can_deliver_pizzas: 204 | 205 | ``` 206 | permissions = (("can_deliver_pizzas", "Can deliver pizzas"),) 207 | ``` 208 | 209 | 它是一个包含二元组的元组或者列表,格式为 (permission_code, human_readable_permission_name)。 210 | 211 | ### default_permissions ### 212 | 213 | **Options.default_permissions** 214 | 215 | ``` 216 | Django 1.7中新增: 217 | ``` 218 | 219 | 默认为('add', 'change', 'delete')。你可以自定义这个列表,比如,如果你的应用不需要默认权限中的任何一项,可以把它设置成空列表。在模型被migrate命令创建之前,这个属性必须被指定,以防一些遗漏的属性被创建。 220 | 221 | ### proxy ### 222 | 223 | **Options.proxy** 224 | 225 | 如果proxy = True, 作为该模型子类的另一个模型会被视为代理模型。 226 | 227 | ### select_on_save ### 228 | 229 | **Options.select_on_save** 230 | 231 | 该选项决定了Django是否采用1.6之前的 django.db.models.Model.save()算法。旧的算法使用SELECT来判断是否存在需要更新的行。而新式的算法直接尝试使用 UPDATE。在一些小概率的情况中,一个已存在的行的UPDATE操作并不对Django可见。比如PostgreSQL的ON UPDATE触发器会返回NULL。这种情况下,新式的算法会在最后执行 INSERT 操作,即使这一行已经在数据库中存在。 232 | 233 | 通常这个属性不需要设置。默认为False。 234 | 235 | 关于旧式和新式两种算法,请参见django.db.models.Model.save()。 236 | 237 | ### unique_together ### 238 | 239 | **Options.unique_together** 240 | 241 | 用来设置的不重复的字段组合: 242 | 243 | ``` 244 | unique_together = (("driver", "restaurant"),) 245 | ``` 246 | 247 | 它是一个元组的元组,组合起来的时候必须是唯一的。它在Django后台中被使用,在数据库层上约束数据(比如,在 CREATE TABLE 语句中包含 UNIQUE语句)。 248 | 249 | 为了方便起见,处理单一字段的集合时,unique_together 可以是一维的元组: 250 | 251 | ``` 252 | unique_together = ("driver", "restaurant") 253 | ``` 254 | 255 | ManyToManyField不能包含在unique_together中。(这意味着什么并不清楚!)如果你需要验证ManyToManyField关联的唯一性,试着使用信号或者显式的贯穿模型(explicit through model)。 256 | 257 | ``` 258 | Django 1.7中修改: 259 | 当unique_together的约束被违反时,模型校验期间会抛出ValidationError异常。 260 | ``` 261 | 262 | ### index_together ### 263 | 264 | **Options.index_together** 265 | 266 | 用来设置带有索引的字段组合: 267 | 268 | ``` 269 | index_together = [ 270 | ["pub_date", "deadline"], 271 | ] 272 | ``` 273 | 274 | 列表中的字段将会建立索引(例如,会在CREATE INDEX语句中被使用)。 275 | 276 | ``` 277 | Django 1.7中修改: 278 | ``` 279 | 280 | 为了方便起见,处理单一字段的集合时,index_together可以是一个一维的列表。 281 | 282 | ``` 283 | index_together = ["pub_date", "deadline"] 284 | ``` 285 | 286 | ### verbose_name ### 287 | 288 | **Options.verbose_name** 289 | 290 | 对象的一个易于理解的名称,为单数: 291 | 292 | ``` 293 | verbose_name = "pizza" 294 | ``` 295 | 296 | 如果此项没有设置,Django会把类名拆分开来作为自述名,比如CamelCase 会变成camel case, 297 | 298 | ### verbose_name_plural ### 299 | 300 | **Options.verbose_name_plural** 301 | 302 | 该对象复数形式的名称: 303 | 304 | ``` 305 | verbose_name_plural = "stories" 306 | ``` 307 | 308 | 如果此项没有设置,Django 会使用 verbose_name + "s"。 -------------------------------------------------------------------------------- /2_1_4_Model class.md: -------------------------------------------------------------------------------- 1 | # Model 类参考 # 2 | 3 | 这篇文档覆盖`Model` 类的特性。关于模型的更多信息,参见[Model 完全参考指南](http://python.usyiyi.cn/django/ref/models/index.html)。 4 | 5 | ## 属性 ## 6 | 7 | ### objects ### 8 | 9 | `Model.objects` 10 | 11 | 每个非抽象的`Model` 类必须给自己添加一个`Manager`实例。Django 确保在你的模型类中至少有一个默认的`Manager`。如果你没有添加自己的`Manager`,Django 将添加一个属性`objects`,它包含默认的`Manager` 实例。如果你添加自己的`Manager`实例的属性,默认值则不会出现。思考一下下面的例子: 12 | 13 | ``` 14 | from django.db import models 15 | 16 | class Person(models.Model): 17 | # Add manager with another name 18 | people = models.Manager() 19 | ``` 20 | 21 | 关于模型管理器的更多信息,参见[Managers](http://python.usyiyi.cn/django/topics/db/managers.html) 和 [Retrieving objects](http://python.usyiyi.cn/django/topics/db/queries.html#retrieving-objects)。 22 | 23 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Model class](https://docs.djangoproject.com/en/1.8/ref/models/class/)。 24 | > 25 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 26 | > 27 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 28 | -------------------------------------------------------------------------------- /2_2.md: -------------------------------------------------------------------------------- 1 | # 查询集 -------------------------------------------------------------------------------- /2_2_3_Lookup expressions.md: -------------------------------------------------------------------------------- 1 | # 查找 API 参考 # 2 | 3 | ``` 4 | New in Django 1.7. 5 | ``` 6 | 7 | 这篇文档是查找 API 的参考,Django 用这些API 构建数据库查询的`WHERE` 子句。若要学习如何使用 查找,参见[执行查询](http://python.usyiyi.cn/django/topics/db/queries.html);若要了解如何创建 新的查找,参见[自定义查找](http://python.usyiyi.cn/django/howto/custom-lookups.html)。 8 | 9 | 查找 API 由两个部分组成:`RegisterLookupMixin` 类,它用于注册查找;[查询表达式API](http://python.usyiyi.cn/django/ref/models/lookups.html#query-expression),它是一个方法集,类必须实现它们才可以注册成一个查找。 10 | 11 | Django 有两个类遵循查询表达式API,且Django 所有内建的查找都继承自它们: 12 | 13 | + `Lookup`:用于查找一个字段(例如`field_name__exact` 中的`exact`) 14 | + `Transform`:用于转换一个字段 15 | 16 | 查找表达式由三部分组成: 17 | 18 | + 字段部分(例如, `Book.objects.filter(author__best_friends__first_name...)`; 19 | + 转换部分(可以省略)(例如, `__lower__first3chars__reversed`); 20 | + 查找部分(例如,`__icontains`),如果省略则默认为`__exact`。 21 | 22 | ## 注册 API ## 23 | 24 | Django 使用`RegisterLookupMixin` 来为类提供接口,注册它自己的查找。两个最突出的例子是`Field`(所有模型字段的基类)和 `Aggregate`(Django 所有聚合函数的基类)。 25 | 26 | `class lookups.RegisterLookupMixin` 27 | 28 | 一个mixin,实现一个类上的查找API。 29 | 30 | `classmethod register_lookup(lookup)` 31 | 32 | 在类中注册一个新的查找。例如,`DateField.register_lookup(YearExact)` 将在`DateField `上注册一个 `YearExact`查找。它会覆盖已存在的同名查找。 33 | 34 | `get_lookup(lookup_name)` 35 | 36 | 返回类中注册的名为`lookup_name` 的 `Lookup`。默认的实现会递归查询所有的父类,并检查它们中的任何一个是否具有名称为`lookup_name`的查找,并返回第一个匹配。 37 | 38 | `get_transform(transform_name)` 39 | 40 | 返回一个名为`transform_name` 的`Transform`。默认的实现会递归查找所有的父类,并检查它们中的任何一个是否具有名称为`transform_name`的查找,并返回第一个匹配。 41 | 42 | 一个类如果想要成为查找,它必须实现查询表达式API。`Lookup` 和`Transform`一开始就遵循这个API。 43 | 44 | ## 查询表达式API ## 45 | 46 | 查询表达式API是一个通用的方法集,在查询表达式中可以使用定义了这些方法的类,来将它们自身转换为SQL表达式。直接的字段引用,聚合,以及`Transform`类都是遵循这个API的示例。当一个对象实现以下方法时,就被称为遵循查询表达式API: 47 | 48 | `as_sql(self, compiler, connection)` 49 | 50 | 负责从表达式中产生查询字符串和参数。`compiler`是一个`SQLCompiler`对象,它拥有可以编译其它表达式的`compile()`方法。`connection`是用于执行查询的连接。 51 | 52 | 调用`expression.as_sql()`一般是不对的 -- 而是应该调用`compiler.compile(expression)`。 `compiler.compile()`方法应该在调用表达式的供应商特定方法时格外小心。 53 | 54 | `as_vendorname(self, compiler, connection)` 55 | 56 | 和`as_sql()`的工作方式类似。当一个表达式经过`compiler.compile()`编译之后, Django会首先尝试调用`as_vendorname()`,其中`vendorname`是用于执行查询的后端供应商。对于Django内建的后端,`vendorname`是`postgresql`,`oracle`,`sqlite`,或者`mysql`之一。 57 | 58 | `get_lookup(lookup_name)` 59 | 60 | 必须返回名称为`lookup_name`的查找。例如,通过返回`self.output_field.get_lookup(lookup_name)`来实现。 61 | 62 | `get_transform(transform_name)` 63 | 64 | 必须返回名称为`transform_name的`查找。例如,通过返回`self.output_field.get_transform(transform_name)`来实现。 65 | 66 | `output_field` 67 | 68 | 定义`get_lookup()`方法所返回的类的类型。必须为`Field`的实例。 69 | 70 | ## Transform 类参考 ## 71 | 72 | `class Transform` 73 | 74 | `Transform`是用于实现字段转换的通用类。一个显然的例子是`__year`会把`DateField`转换为`IntegerField`。 75 | 76 | 在表达式中执行查找的标记是`Transform__` (例如 `date__year`)。 77 | 78 | 这个类遵循查询表达式API,也就是说你可以使用 `____`。 79 | 80 | `bilateral` 81 | 82 | ``` 83 | New in Django 1.8. 84 | ``` 85 | 86 | 一个布尔值,表明是否对`lhs`和 `rhs`都应用这个转换。如果对两侧都应用转换,应用在`rhs`的顺序和在查找表达式中的出现顺序相同。默认这个属性为`False`。使用方法的实例请见自定义查找。 87 | 88 | `lhs` 89 | 90 | 在左边,也就是被转换的东西。必须遵循查询表达式API。 91 | 92 | `lookup_name` 93 | 94 | 查找的名称,用于在解析查询表达式的时候识别它。 95 | 96 | `output_field` 97 | 98 | 为这个类定义转换后的输出。必须为`Field`的实例。默认情况下和`lhs.output_field`相同。 99 | 100 | `as_sql()` 101 | 102 | 需要被覆写;否则抛出`NotImplementedError`异常。 103 | 104 | `get_lookup(lookup_name)` 105 | 106 | 和`get_lookup()`相同。 107 | 108 | `get_transform(transform_name)` 109 | 110 | 和`get_transform()`相同。 111 | 112 | ## Lookup 类参考 ## 113 | 114 | `class Lookup` 115 | 116 | `Lookup`是实现查找的通用的类。查找是一个查询表达式,它的左边是`lhs`,右边是`rhs`;`lookup_name`用于构造`lhs`和`rhs`之间的比较,来产生布尔值,例如`lhs in rhs`或者`lhs > rhs`。 117 | 118 | 在表达式中执行查找的标记是`__=`。 119 | 120 | 这个类并不遵循查询表达式API,因为在它构造的时候出现了`=`:查找总是在查找表达式的最后。 121 | 122 | `lhs` 123 | 124 | 在左边,也就是被查找的东西。这个对象必须遵循查询表达式API。 125 | 126 | `rhs` 127 | 128 | 在右边,也就是用来和`lhs`比较的东西。它可以是个简单的值,也可以是在SQL中编译的一些东西,比如 `F()` 对象或者`QuerySet`。 129 | 130 | `lookup_name` 131 | 132 | 查找的名称,用于在解析查询表达式的时候识别它。 133 | 134 | `process_lhs(compiler, connection[, lhs=None])` 135 | 136 | 返回元组`(lhs_string, lhs_params)`,和`compiler.compile(lhs)`所返回的一样。这个方法可以被覆写,来调整`lhs`的处理方式。 137 | 138 | `compiler`是一个`SQLCompiler`对象,可以像 `compiler.compile(lhs)`这样使用来编译`lhs`。`connection`可以用于编译供应商特定的SQL语句。`lhs`如果不为`None`, 会代替`self.lhs`作为处理后的`lhs`使用。 139 | 140 | `process_rhs(compiler, connection)` 141 | 142 | 对于右边的东西,和`process_lhs()`的行为相同。 143 | 144 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Lookup expressions](https://docs.djangoproject.com/en/1.8/ref/models/lookups/)。 145 | > 146 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 147 | > 148 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 149 | -------------------------------------------------------------------------------- /2_3.md: -------------------------------------------------------------------------------- 1 | # 模型的实例 -------------------------------------------------------------------------------- /2_3_2_Accessing related objects.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 关联对象参考 # 6 | 7 | **class RelatedManager** 8 | 9 | "关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。它存在于下面两种情况: 10 | 11 | ForeignKey关系的“另一边”。像这样: 12 | 13 | ``` 14 | from django.db import models 15 | 16 | class Reporter(models.Model): 17 | # ... 18 | pass 19 | 20 | class Article(models.Model): 21 | reporter = models.ForeignKey(Reporter) 22 | ``` 23 | 24 | 在上面的例子中,管理器reporter.article_set拥有下面的方法。 25 | 26 | ManyToManyField关系的两边: 27 | 28 | ``` 29 | class Topping(models.Model): 30 | # ... 31 | pass 32 | 33 | class Pizza(models.Model): 34 | toppings = models.ManyToManyField(Topping) 35 | ``` 36 | 37 | 这个例子中,topping.pizza_set 和pizza.toppings都拥有下面的方法。 38 | 39 | **add(obj1[, obj2, ...])** 40 | 41 | 把指定的模型对象添加到关联对象集中。 42 | 43 | 例如: 44 | 45 | ``` 46 | >>> b = Blog.objects.get(id=1) 47 | >>> e = Entry.objects.get(id=234) 48 | >>> b.entry_set.add(e) # Associates Entry e with Blog b. 49 | ``` 50 | 51 | 在上面的例子中,对于ForeignKey关系,e.save()由关联管理器调用,执行更新操作。然而,在多对多关系中使用add()并不会调用任何 save()方法,而是由QuerySet.bulk_create()创建关系。如果你需要在关系被创建时执行一些自定义的逻辑,请监听m2m_changed信号。 52 | 53 | **create(\*\*kwargs)** 54 | 55 | 创建一个新的对象,保存对象,并将它添加到关联对象集之中。返回新创建的对象: 56 | 57 | ``` 58 | >>> b = Blog.objects.get(id=1) 59 | >>> e = b.entry_set.create( 60 | ... headline='Hello', 61 | ... body_text='Hi', 62 | ... pub_date=datetime.date(2005, 1, 1) 63 | ... ) 64 | 65 | # No need to call e.save() at this point -- it's already been saved. 66 | ``` 67 | 68 | 这完全等价于(不过更加简洁于): 69 | 70 | ``` 71 | >>> b = Blog.objects.get(id=1) 72 | >>> e = Entry( 73 | ... blog=b, 74 | ... headline='Hello', 75 | ... body_text='Hi', 76 | ... pub_date=datetime.date(2005, 1, 1) 77 | ... ) 78 | >>> e.save(force_insert=True) 79 | ``` 80 | 81 | 要注意我们并不需要指定模型中用于定义关系的关键词参数。在上面的例子中,我们并没有传入blog参数给create()。Django会明白新的 Entry对象blog 应该添加到b中。 82 | 83 | **remove(obj1[, obj2, ...])** 84 | 85 | 从关联对象集中移除执行的模型对象: 86 | 87 | ``` 88 | >>> b = Blog.objects.get(id=1) 89 | >>> e = Entry.objects.get(id=234) 90 | >>> b.entry_set.remove(e) # Disassociates Entry e from Blog b. 91 | ``` 92 | 93 | 和add()相似,上面的例子中,e.save()可会执行更新操作。但是,多对多关系上的remove(),会使用QuerySet.delete()删除关系,意思是并不会有任何模型调用save()方法:如果你想在一个关系被删除时执行自定义的代码,请监听m2m_changed信号。 94 | 95 | 对于ForeignKey对象,这个方法仅在null=True时存在。如果关联的字段不能设置为None (NULL),则这个对象在添加到另一个关联之前不能移除关联。在上面的例子中,从b.entry_set()移除e等价于让e.blog = None,由于blog的ForeignKey没有设置null=True,这个操作是无效的。 96 | 97 | 对于ForeignKey对象,该方法接受一个bulk参数来控制它如果执行操作。如果为True(默认值),QuerySet.update()会被使用。而如果bulk=False,会在每个单独的模型实例上调用save()方法。这会触发pre_save和post_save,它们会消耗一定的性能。 98 | 99 | **clear()** 100 | 101 | 从关联对象集中移除一切对象。 102 | 103 | ``` 104 | >>> b = Blog.objects.get(id=1) 105 | >>> b.entry_set.clear() 106 | ``` 107 | 108 | 注意这样不会删除对象 —— 只会删除他们之间的关联。 109 | 110 | 就像 remove() 方法一样,clear()只能在 null=True的ForeignKey上被调用,也可以接受bulk关键词参数。 111 | 112 | > 注意 113 | > 114 | > 注意对于所有类型的关联字段,add()、create()、remove()和clear()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。 115 | > 116 | > 同样,如果你再多对多关系中使用了中间模型,一些关联管理的方法会被禁用。 117 | 118 | ## 直接赋值 ## 119 | 120 | 通过赋值一个新的可迭代的对象,关联对象集可以被整体替换掉。 121 | 122 | ``` 123 | >>> new_list = [obj1, obj2, obj3] 124 | >>> e.related_set = new_list 125 | ``` 126 | 127 | 如果外键关系满足null=True,关联管理器会在添加new_list中的内容之前,首先调用clear()方法来解除关联集中一切已存在对象的关联。否则, new_list中的对象会在已存在的关联的基础上被添加。 -------------------------------------------------------------------------------- /2_4.md: -------------------------------------------------------------------------------- 1 | # 迁移 -------------------------------------------------------------------------------- /2_4_3_SchemaEditor.md: -------------------------------------------------------------------------------- 1 | # 模式编辑器 # 2 | 3 | `class BaseDatabaseSchemaEditor[source]` 4 | 5 | Django的迁移系统分为两个部分;计算和储存应该执行什么操作的逻辑 (`django.db.migrations`) ,以及用于把“创建模型”或者“删除字段”变成SQL语句的数据库抽象层 -- 后者是模式编辑器的功能。 6 | 7 | 你可能并不想像一个普通的开发者使用Django那样,直接和模型编辑器进行交互,但是如果你编写自己的迁移系统,或者有更进一步的需求,这样会比编写SQL语句更方便。 8 | 9 | 每个Django的数据库后端都提供了它们自己的模式编辑器,并且总是可以通过`connection.schema_editor()`上下文管理器来访问。 10 | 11 | ``` 12 | with connection.schema_editor() as schema_editor: 13 | schema_editor.delete_model(MyModel) 14 | ``` 15 | 16 | 它必须通过上下文管理器来使用,因为这样可以管理一些类似于事务和延迟SQL(比如创建`ForeignKey`约束)的东西。 17 | 18 | 它会暴露所有可能的操作作为方法,这些方法应该按照执行修改的顺序调用。可能一些操作或者类型并不可用于所有数据库 -- 例如,MyISAM引擎不支持外键约束。 19 | 20 | 如果你在为Django编写一个三方的数据库后端,你需要提供`SchemaEditor`实现来使用1.7的迁移功能 -- 然而,只要你的数据库在SQL的使用和关系设计上遵循标准,你就应该能够派生Django内建的`SchemaEditor`之一,然后简单调整一下语法。同时也要注意,有一些新的数据库特性是迁移所需要的:`can_rollback_ddl`和`supports_combined_alters`都很重要。 21 | 22 | ## 方法 ## 23 | 24 | ### execute ### 25 | 26 | `BaseDatabaseSchemaEditor.execute(sql, params=[])[source]` 27 | 28 | 执行传入的 SQL语句,如果提供了参数则会带上它们。这是对普通数据库游标的一个简单封装,如果用户希望的话,它可以从`.sql`文件中获取SQL。 29 | 30 | ### create_model ### 31 | 32 | `BaseDatabaseSchemaEditor.create_model(model)[source]` 33 | 34 | 为提供的模型在数据库中创建新的表,带有所需的任何唯一性约束或者索引。 35 | 36 | ### delete_model ### 37 | 38 | `BaseDatabaseSchemaEditor.delete_model(model)[source]` 39 | 40 | 删除数据库中的模型的表,以及它带有的任何唯一性约束或者索引。 41 | 42 | ### alter_unique_together ### 43 | 44 | `BaseDatabaseSchemaEditor.alter_unique_together(model, old_unique_together, new_unique_together)[source]` 45 | 46 | 修改模型的`unique_together`值;这会向模型表中添加或者删除唯一性约束,使它们匹配新的值。 47 | 48 | ### alter_index_together ### 49 | 50 | `BaseDatabaseSchemaEditor.alter_index_together(model, old_index_together, new_index_together)[source]` 51 | 52 | 修改模型的 `index_together`值;这会向模型表中添加或者删除索引,使它们匹配新的值。 53 | 54 | ### alter_db_table ### 55 | 56 | `BaseDatabaseSchemaEditor.alter_db_table(model, old_db_table, new_db_table)[source]` 57 | 58 | 重命名模型的表,从`old_db_table`变成`new_db_table`。 59 | 60 | ### alter_db_tablespace ### 61 | 62 | `BaseDatabaseSchemaEditor.alter_db_tablespace(model, old_db_tablespace, new_db_tablespace)[source]` 63 | 64 | 把模型的表从一个表空间移动到另一个中。 65 | 66 | ### add_field ### 67 | 68 | `BaseDatabaseSchemaEditor.add_field(model, field)[source]` 69 | 70 | 向模型的表中添加一列(或者有时几列),表示新增的字段。如果该字段带有`db_index=True`或者 `unique=True`,同时会添加索引或者唯一性约束。 71 | 72 | 如果字段为`ManyToManyField并`且缺少 `through`值,会创建一个表来表示关系,而不是创建一列。如果提供了`through`值,就什么也不做。 73 | 74 | 如果字段为`ForeignKey`,同时会向列上添加一个外键约束。 75 | 76 | ### remove_field ### 77 | 78 | `BaseDatabaseSchemaEditor.remove_field(model, field)[source]` 79 | 80 | 从模型的表中移除代表字段的列,以及列上的任何唯一性约束,外键约束,或者索引。 81 | 82 | 如果字段是`ManyToManyField`并且缺少`through`值,会移除创建用来跟踪关系的表。如果提供了`through`值,就什么也不做。 83 | 84 | ### alter_field ### 85 | 86 | `BaseDatabaseSchemaEditor.alter_field(model, old_field, new_field, strict=False)[source]` 87 | 88 | 这会将模型的字段从旧的字段转换为新的。这包括列名称的修改(`db_column`属性)、字段类型的修改(如果修改了字段类)、字段`NULL`状态的修改、添加或者删除字段层面的唯一性约束和索引、修改主键、以及修改`ForeignKey`约束的目标。 89 | 90 | 最普遍的一个不能实现的转换,是把`ManyToManyField`变成一个普通的字段,反之亦然;Django不能在不丢失数据的情况下执行这个转换,所以会拒绝这样做。作为替代,应该单独调用`remove_field()`和`add_field()`。 91 | 92 | 如果数据库满足`supports_combined_alters`,Django会尽可能在单次数据库调用中执行所有这些操作。否则对于每个变更,都会执行一个单独的`ALTER`语句,但是如果不需要做任何改变,则不执行`ALTER`(就像South经常做的那样)。 93 | 94 | ## 属性 ## 95 | 96 | 除非另有规定,所有属性都应该是只读的。 97 | 98 | ### connection ### 99 | 100 | `SchemaEditor.connection` 101 | 102 | 一个到数据库的连接对象。`alias`是`connection`的一个实用的属性,它用于决定要访问的数据库的名字。 103 | 104 | 当你[在多种数据库之间执行迁移](http://python.usyiyi.cn/django/howto/writing-migrations.html#data-migrations-and-multiple-databases)的时候,这是非常有用的。 105 | 106 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[SchemaEditor](https://docs.djangoproject.com/en/1.8/ref/schema-editor/)。 107 | > 108 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 109 | > 110 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 111 | -------------------------------------------------------------------------------- /2_4_4_Writing migrations.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 编写数据库迁移 # 6 | 7 | 这一节介绍你可能遇到的在不同情况下如何分析和编写数据库迁移. 有关迁移的入门资料,请查看 the topic guide. 8 | 9 | ## 数据迁移和多数据库 ## 10 | 11 | 在使用多个数据库时,需要解决是否针对某个特定数据库运行迁移。例如,你可能 只 想在某个特定数据库上运行迁移。 12 | 13 | 为此你可以在RunPython中通过查看schema_editor.connection.alias 属性来检查数据库连接别名: 14 | 15 | ``` 16 | from django.db import migrations 17 | 18 | def forwards(apps, schema_editor): 19 | if not schema_editor.connection.alias == 'default': 20 | return 21 | # Your migration code goes here 22 | 23 | class Migration(migrations.Migration): 24 | 25 | dependencies = [ 26 | # Dependencies to other migrations 27 | ] 28 | 29 | operations = [ 30 | migrations.RunPython(forwards), 31 | ] 32 | ``` 33 | 34 | ``` 35 | Django 1.8 中新增。 36 | ``` 37 | 38 | 你也可以提供一个提示作为 **hints参数传递到数据库路由的allow_migrate() 方法: 39 | 40 | ``` 41 | myapp/dbrouters.py 42 | class MyRouter(object): 43 | 44 | def allow_migrate(self, db, app_label, model_name=None, **hints): 45 | if 'target_db' in hints: 46 | return db == hints['target_db'] 47 | return True 48 | ``` 49 | 50 | 然后,要在你的迁移中利用,执行以下操作: 51 | 52 | ``` 53 | from django.db import migrations 54 | 55 | def forwards(apps, schema_editor): 56 | # Your migration code goes here 57 | 58 | class Migration(migrations.Migration): 59 | 60 | dependencies = [ 61 | # Dependencies to other migrations 62 | ] 63 | 64 | operations = [ 65 | migrations.RunPython(forwards, hints={'target_db': 'default'}), 66 | ] 67 | ``` 68 | 69 | 如果你的RunPython或者RunSQL操作只对一个模型有影响,最佳实践是将model_name作为提示传递,使其尽可能对路由可见。这对可复用的和第三方应用极其重要。 70 | 71 | ## 添加唯一字段的迁移 ## 72 | 73 | 如果你应用了一个“朴素”的迁移,向表中一个已存在的行中添加了一个唯一的非空字段,会产生错误,因为位于已存在行中的值只会生成一次。所以需要移除唯一性的约束。 74 | 75 | 所以,应该执行下面的步骤。在这个例子中,我们会以默认值添加一个非空的UUIDField字段。你可以根据你的需要修改各个字段。 76 | 77 | + 把default=...和unique=True参数添加到你模型的字段中。在这个例子中,我们默认使用uuid.uuid4。 78 | + 运行 makemigrations 命令。 79 | + 编辑创建的迁移文件。 80 | 81 | 生成的迁移类看上去像这样: 82 | 83 | ``` 84 | class Migration(migrations.Migration): 85 | 86 | dependencies = [ 87 | ('myapp', '0003_auto_20150129_1705'), 88 | ] 89 | 90 | operations = [ 91 | migrations.AddField( 92 | model_name='mymodel', 93 | name='uuid', 94 | field=models.UUIDField(max_length=32, unique=True, default=uuid.uuid4), 95 | ), 96 | ] 97 | ``` 98 | 99 | 你需要做三处更改: 100 | 101 | + 从已生成的迁移类中复制,添加第二个AddField操作,并改为AlterField。 102 | + 在第一个AddField操作中,把unique=True改为 null=True,这会创建一个中间的null字段。 103 | + 在两个操作之间,添加一个RunPython或RunSQL操作为每个已存在的行生成一个唯一值(例如UUID)。 104 | 105 | 最终的迁移类应该看起来是这样: 106 | 107 | ``` 108 | # -*- coding: utf-8 -*- 109 | from __future__ import unicode_literals 110 | 111 | from django.db import migrations, models 112 | import uuid 113 | 114 | def gen_uuid(apps, schema_editor): 115 | MyModel = apps.get_model('myapp', 'MyModel') 116 | for row in MyModel.objects.all(): 117 | row.uuid = uuid.uuid4() 118 | row.save() 119 | 120 | class Migration(migrations.Migration): 121 | 122 | dependencies = [ 123 | ('myapp', '0003_auto_20150129_1705'), 124 | ] 125 | 126 | operations = [ 127 | migrations.AddField( 128 | model_name='mymodel', 129 | name='uuid', 130 | field=models.UUIDField(default=uuid.uuid4, null=True), 131 | ), 132 | # omit reverse_code=... if you don't want the migration to be reversible. 133 | migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop), 134 | migrations.AlterField( 135 | model_name='mymodel', 136 | name='uuid', 137 | field=models.UUIDField(default=uuid.uuid4, unique=True), 138 | ), 139 | ] 140 | ``` 141 | 142 | 现在你可以像平常一样使用migrate命令应用迁移。 143 | 144 | 注意如果你在这个迁移运行时让对象被创建,就会产生竞争条件(race condition)。在AddField之后, RunPython之前创建的对象会覆写他们原始的uuid。 -------------------------------------------------------------------------------- /2_5.md: -------------------------------------------------------------------------------- 1 | # 高级 -------------------------------------------------------------------------------- /2_5_10_Database Functions.md: -------------------------------------------------------------------------------- 1 | # 数据库函数 # 2 | 3 | ``` 4 | New in Django 1.8. 5 | ``` 6 | 7 | 下面记述的类为用户提供了一些方法,来在Django中使用底层数据库提供的函数用于注解、聚合或者过滤器等操作。函数也是表达式,所以可以像聚合函数一样混合使用它们。 8 | 9 | 我们会在每个函数的实例中使用下面的模型: 10 | 11 | ``` 12 | class Author(models.Model): 13 | name = models.CharField(max_length=50) 14 | age = models.PositiveIntegerField(null=True, blank=True) 15 | alias = models.CharField(max_length=50, null=True, blank=True) 16 | goes_by = models.CharField(max_length=50, null=True, blank=True) 17 | ``` 18 | 19 | 我们并不推荐在CharField上允许null=True,以后那位这会允许字段有两个“空值”,但是对于下面的Coalesce示例来说它很重要。 20 | 21 | ## Coalesce ## 22 | 23 | `class Coalesce(*expressions, **extra)[source]` 24 | 25 | 接受一个含有至少两个字段名称或表达式的列表,返回第一个非空的值(注意空字符串不被认为是一个空值)。每个参与都必须是相似的类型,所以掺杂了文本和数字的列表会导致数据库错误。 26 | 27 | 使用范例: 28 | 29 | ``` 30 | >>> # Get a screen name from least to most public 31 | >>> from django.db.models import Sum, Value as V 32 | >>> from django.db.models.functions import Coalesce 33 | >>> Author.objects.create(name='Margaret Smith', goes_by='Maggie') 34 | >>> author = Author.objects.annotate( 35 | ... screen_name=Coalesce('alias', 'goes_by', 'name')).get() 36 | >>> print(author.screen_name) 37 | Maggie 38 | 39 | >>> # Prevent an aggregate Sum() from returning None 40 | >>> aggregated = Author.objects.aggregate( 41 | ... combined_age=Coalesce(Sum('age'), V(0)), 42 | ... combined_age_default=Sum('age')) 43 | >>> print(aggregated['combined_age']) 44 | 0 45 | >>> print(aggregated['combined_age_default']) 46 | None 47 | ``` 48 | 49 | ## Concat ## 50 | 51 | `class Concat(*expressions, **extra)[source]` 52 | 53 | 接受一个含有至少两个文本字段的或表达式的列表,返回连接后的文本。每个参数都必须是文本或者字符类型。如果你想把一个`TextField()`和一个`CharField()`连接, 一定要告诉Django`output_field`应该为`TextField()`类型。在下面连接`Value`的例子中,这也是必需的。 54 | 55 | 这个函数不会返回`null`。在后端中,如果一个`null`参数导致了整个表达式都是`null`,Django会确保把每个`null`的部分转换成一个空字符串。 56 | 57 | 使用范例: 58 | 59 | ``` 60 | >>> # Get the display name as "name (goes_by)" 61 | >>> from django.db.models import CharField, Value as V 62 | >>> from django.db.models.functions import Concat 63 | >>> Author.objects.create(name='Margaret Smith', goes_by='Maggie') 64 | >>> author = Author.objects.annotate( 65 | ... screen_name=Concat('name', V(' ('), 'goes_by', V(')'), 66 | ... output_field=CharField())).get() 67 | >>> print(author.screen_name) 68 | Margaret Smith (Maggie) 69 | ``` 70 | 71 | ## Length ## 72 | 73 | `class Length(expression, **extra)[source]` 74 | 75 | 接受一个文本字段或表达式,返回值的字符个数。如果表达式是`null`,长度也会是`null`。 76 | 77 | 使用范例: 78 | 79 | ``` 80 | >>> # Get the length of the name and goes_by fields 81 | >>> from django.db.models.functions import Length 82 | >>> Author.objects.create(name='Margaret Smith') 83 | >>> author = Author.objects.annotate( 84 | ... name_length=Length('name'), 85 | ... goes_by_length=Length('goes_by')).get() 86 | >>> print(author.name_length, author.goes_by_length) 87 | (14, None) 88 | ``` 89 | 90 | ## Lower ## 91 | 92 | `class Lower(expression, **extra)[source]` 93 | 94 | 接受一个文本字符串或表达式,返回它的小写表示形式。 95 | 96 | 使用范例: 97 | 98 | ``` 99 | >>> from django.db.models.functions import Lower 100 | >>> Author.objects.create(name='Margaret Smith') 101 | >>> author = Author.objects.annotate(name_lower=Lower('name')).get() 102 | >>> print(author.name_lower) 103 | margaret smith 104 | ``` 105 | 106 | ## Substr ## 107 | 108 | `class Substr(expression, pos, length=None, **extra)[source]` 109 | 110 | 返回这个字段或者表达式的,以`pos`位置开始,长度为`length`的子字符串。位置从下标为1开始,所以必须大于0。如果`length`是`None`,会返回剩余的字符串。 111 | 112 | 使用范例: 113 | 114 | ``` 115 | >>> # Set the alias to the first 5 characters of the name as lowercase 116 | >>> from django.db.models.functions import Substr, Lower 117 | >>> Author.objects.create(name='Margaret Smith') 118 | >>> Author.objects.update(alias=Lower(Substr('name', 1, 5))) 119 | 1 120 | >>> print(Author.objects.get(name='Margaret Smith').alias) 121 | marga 122 | ``` 123 | 124 | ## Upper ## 125 | 126 | `class Upper(expression, **extra)[source]` 127 | 128 | 接受一个文本字符串或表达式,返回它的大写表示形式。 129 | 130 | 使用范例: 131 | 132 | ``` 133 | >>> from django.db.models.functions import Upper 134 | >>> Author.objects.create(name='Margaret Smith') 135 | >>> author = Author.objects.annotate(name_upper=Upper('name')).get() 136 | >>> print(author.name_upper) 137 | MARGARET SMITH 138 | ``` 139 | 140 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Database Functions](https://docs.djangoproject.com/en/1.8/ref/models/database-functions/)。 141 | > 142 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 143 | > 144 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 145 | -------------------------------------------------------------------------------- /2_5_9_Conditional Expressions.md: -------------------------------------------------------------------------------- 1 | # 条件表达式 # 2 | 3 | ``` 4 | New in Django 1.8. 5 | ``` 6 | 7 | 条件表达式允许你在过滤器、注解、聚合和更新操作中使用 `if ... elif ... else`的逻辑。条件[表达式](http://python.usyiyi.cn/django/ref/models/expressions.html)为表中的每一行计算一系列的条件,并且返回匹配到的结果表达式。条件表达式也可以像其它 表达式一样混合和嵌套。 8 | 9 | ## 条件表达式类 ## 10 | 11 | 我们会在后面的例子中使用下面的模型: 12 | 13 | ``` 14 | from django.db import models 15 | 16 | class Client(models.Model): 17 | REGULAR = 'R' 18 | GOLD = 'G' 19 | PLATINUM = 'P' 20 | ACCOUNT_TYPE_CHOICES = ( 21 | (REGULAR, 'Regular'), 22 | (GOLD, 'Gold'), 23 | (PLATINUM, 'Platinum'), 24 | ) 25 | name = models.CharField(max_length=50) 26 | registered_on = models.DateField() 27 | account_type = models.CharField( 28 | max_length=1, 29 | choices=ACCOUNT_TYPE_CHOICES, 30 | default=REGULAR, 31 | ) 32 | ``` 33 | 34 | ### When ### 35 | 36 | `class When(condition=None, then=None, **lookups)[source]` 37 | 38 | `When()`对象用于封装条件和它的结果,为了在条件表达式中使用。使用`When()`对象和使用`filter()` 方法类似。条件可以使用[字段查找](http://python.usyiyi.cn/django/ref/models/querysets.html#field-lookups) 或者 `Q` 来指定。结果通过使用`then`关键字来提供。 39 | 40 | 一些例子: 41 | 42 | ``` 43 | >>> from django.db.models import When, F, Q 44 | >>> # String arguments refer to fields; the following two examples are equivalent: 45 | >>> When(account_type=Client.GOLD, then='name') 46 | >>> When(account_type=Client.GOLD, then=F('name')) 47 | >>> # You can use field lookups in the condition 48 | >>> from datetime import date 49 | >>> When(registered_on__gt=date(2014, 1, 1), 50 | ... registered_on__lt=date(2015, 1, 1), 51 | ... then='account_type') 52 | >>> # Complex conditions can be created using Q objects 53 | >>> When(Q(name__startswith="John") | Q(name__startswith="Paul"), 54 | ... then='name') 55 | ``` 56 | 57 | 要注意这些值中的每一个都可以是表达式。 58 | 59 | > 注意 60 | > 61 | > 由于then 关键字参数为 When()的结果而保留,如果Model有名称为 then的字段,会有潜在的冲突。这可以用以下两种办法解决: 62 | 63 | ``` 64 | >>> from django.db.models import Value 65 | >>> When(then__exact=0, then=1) 66 | >>> When(Q(then=0), then=1) 67 | ``` 68 | 69 | ### Case ### 70 | 71 | `class Case(*cases, **extra)[source]` 72 | 73 | `Case()`表达式就像是Python中的`if ... elif ... else`语句。每个提供的`When()`中的`condition` 按照顺序计算,直到得到一个真值。返回匹配`When() `对象的`result`表达式。 74 | 75 | 一个简单的例子: 76 | 77 | ``` 78 | >>> 79 | >>> from datetime import date, timedelta 80 | >>> from django.db.models import CharField, Case, Value, When 81 | >>> Client.objects.create( 82 | ... name='Jane Doe', 83 | ... account_type=Client.REGULAR, 84 | ... registered_on=date.today() - timedelta(days=36)) 85 | >>> Client.objects.create( 86 | ... name='James Smith', 87 | ... account_type=Client.GOLD, 88 | ... registered_on=date.today() - timedelta(days=5)) 89 | >>> Client.objects.create( 90 | ... name='Jack Black', 91 | ... account_type=Client.PLATINUM, 92 | ... registered_on=date.today() - timedelta(days=10 * 365)) 93 | >>> # Get the discount for each Client based on the account type 94 | >>> Client.objects.annotate( 95 | ... discount=Case( 96 | ... When(account_type=Client.GOLD, then=Value('5%')), 97 | ... When(account_type=Client.PLATINUM, then=Value('10%')), 98 | ... default=Value('0%'), 99 | ... output_field=CharField(), 100 | ... ), 101 | ... ).values_list('name', 'discount') 102 | [('Jane Doe', '0%'), ('James Smith', '5%'), ('Jack Black', '10%')] 103 | ``` 104 | 105 | `Case()` 接受任意数量的`When()`对象作为独立的参数。其它选项使用关键字参数提供。如果没有条件为`TRUE`,表达式会返回提供的`default`关键字参数。如果没有提供`default`参数,会使用`Value(None)`。 106 | 107 | 如果我们想要修改之前的查询,来获取基于`Client`跟着我们多长时间的折扣,我们应该这样使用查找: 108 | 109 | ``` 110 | >>> a_month_ago = date.today() - timedelta(days=30) 111 | >>> a_year_ago = date.today() - timedelta(days=365) 112 | >>> # Get the discount for each Client based on the registration date 113 | >>> Client.objects.annotate( 114 | ... discount=Case( 115 | ... When(registered_on__lte=a_year_ago, then=Value('10%')), 116 | ... When(registered_on__lte=a_month_ago, then=Value('5%')), 117 | ... default=Value('0%'), 118 | ... output_field=CharField(), 119 | ... ) 120 | ... ).values_list('name', 'discount') 121 | [('Jane Doe', '5%'), ('James Smith', '0%'), ('Jack Black', '10%')] 122 | ``` 123 | 124 | > 注意 125 | > 126 | > 记住条件按照顺序来计算,所以上面的例子中,即使第二个条件匹配到了 Jane Doe 和 Jack Black,我们也得到了正确的结果。这就像Python中的if ... elif ... else语句一样。 127 | 128 | ## 高级查询 ## 129 | 130 | 条件表达式可以用于注解、聚合、查找和更新。它们也可以和其它表达式混合和嵌套。这可以让你构造更强大的条件查询。 131 | 132 | ### 条件更新 ### 133 | 134 | 假设我们想要为客户端修改`account_type`来匹配它们的注册日期。我们可以使用条件表达式和`update()`放啊来实现: 135 | 136 | ``` 137 | >>> a_month_ago = date.today() - timedelta(days=30) 138 | >>> a_year_ago = date.today() - timedelta(days=365) 139 | >>> # Update the account_type for each Client from the registration date 140 | >>> Client.objects.update( 141 | ... account_type=Case( 142 | ... When(registered_on__lte=a_year_ago, 143 | ... then=Value(Client.PLATINUM)), 144 | ... When(registered_on__lte=a_month_ago, 145 | ... then=Value(Client.GOLD)), 146 | ... default=Value(Client.REGULAR) 147 | ... ), 148 | ... ) 149 | >>> Client.objects.values_list('name', 'account_type') 150 | [('Jane Doe', 'G'), ('James Smith', 'R'), ('Jack Black', 'P')] 151 | ``` 152 | 153 | ### 条件聚合 ### 154 | 155 | 如果我们想要弄清楚每个`account_type`有多少客户端,要怎么做呢?我们可以在[聚合函数](http://python.usyiyi.cn/django/ref/models/querysets.html#aggregation-functions)中嵌套条件表达式来实现: 156 | 157 | ``` 158 | >>> # Create some more Clients first so we can have something to count 159 | >>> Client.objects.create( 160 | ... name='Jean Grey', 161 | ... account_type=Client.REGULAR, 162 | ... registered_on=date.today()) 163 | >>> Client.objects.create( 164 | ... name='James Bond', 165 | ... account_type=Client.PLATINUM, 166 | ... registered_on=date.today()) 167 | >>> Client.objects.create( 168 | ... name='Jane Porter', 169 | ... account_type=Client.PLATINUM, 170 | ... registered_on=date.today()) 171 | >>> # Get counts for each value of account_type 172 | >>> from django.db.models import IntegerField, Sum 173 | >>> Client.objects.aggregate( 174 | ... regular=Sum( 175 | ... Case(When(account_type=Client.REGULAR, then=1), 176 | ... output_field=IntegerField()) 177 | ... ), 178 | ... gold=Sum( 179 | ... Case(When(account_type=Client.GOLD, then=1), 180 | ... output_field=IntegerField()) 181 | ... ), 182 | ... platinum=Sum( 183 | ... Case(When(account_type=Client.PLATINUM, then=1), 184 | ... output_field=IntegerField()) 185 | ... ) 186 | ... ) 187 | {'regular': 2, 'gold': 1, 'platinum': 3} 188 | ``` 189 | 190 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Conditional Expressions](https://docs.djangoproject.com/en/1.8/ref/models/conditional-expressions/)。 191 | > 192 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 193 | > 194 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 195 | -------------------------------------------------------------------------------- /2_6.md: -------------------------------------------------------------------------------- 1 | # 其它 -------------------------------------------------------------------------------- /2_6_2_Legacy databases.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 将遗留数据库整合到Django # 6 | 7 | 虽然Django最适合用来开发新的应用,但也可以将它整合到遗留的数据库中。Django包含了很多工具,尽可能自动化解决这类问题。 8 | 9 | 这篇文章假设你了解Django的基础部分,它们在教程中提及。 10 | 11 | 一旦你的Django环境建立好之后,你可以按照这个大致的流程,整合你的现有数据库。 12 | 13 | ## 向Django提供你的数据库参数 ## 14 | 15 | 你需要告诉Django你的数据库连接参数,以及数据库的名称。请修改DATABASES设置,为'默认' 连接的以下键赋值: 16 | 17 | + NAME 18 | + ENGINE 19 | + USER 20 | + PASSWORD 21 | + HOST 22 | + PORT 23 | 24 | ## 自动生成模型 ## 25 | 26 | Django自带叫做inspectdb的工具,可以按照现有的数据库创建模型。你可以运行以下命令,并查看输出: 27 | 28 | ``` 29 | $ python manage.py inspectdb 30 | ``` 31 | 32 | 通过重定向Unix标准输出流来保存文件: 33 | 34 | ``` 35 | $ python manage.py inspectdb > models.py 36 | ``` 37 | 38 | 这个特性是一个快捷方式,并不是一个确定的模型生成器。详见inspectdb文档 。 39 | 40 | 一旦你创建好了你的模型,把文件命名为models.py,然后把它放到你应用的Python包中。然后把应用添加到你的INSTALLED_APPS 设置中。 41 | 42 | 默认情况下,inspectdb创建未被管理的模型。这就是说,模型的Meta类中的managed = False告诉Django不要管理每个表的创建、修改和删除: 43 | 44 | ``` 45 | class Person(models.Model): 46 | id = models.IntegerField(primary_key=True) 47 | first_name = models.CharField(max_length=70) 48 | class Meta: 49 | managed = False 50 | db_table = 'CENSUS_PERSONS' 51 | ``` 52 | 53 | 如果你希望Django管理表的生命周期,你需要把managed选项改为 True(或者简单地把它移除,因为True是默认值)。 54 | 55 | ## 安装Django核心表 ## 56 | 57 | 接下来,运行migrate命令来安装所有所需的额外的数据库记录,比如后台权限和内容类型: 58 | 59 | ``` 60 | $ python manage.py migrate 61 | ``` 62 | 63 | ## 测试和调整 ## 64 | 65 | 上面就是所有基本的步骤了 —— 到目前为止你会想要调整Django自动生成的模型,直到他们按照你想要的方式工作。尝试通过Django数据库API访问你的数据,并且尝试使用Django后台页面编辑对象,以及相应地编辑模型文件。 -------------------------------------------------------------------------------- /2_6_3_Providing initial data.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 为模型提供初始数据 # 6 | 7 | 当你首次建立一个应用的时候,为你的数据库预先安装一些硬编码的数据,是很有用处的。 有几种方法可以让Django自动创建这些数据:你可以通过fixtures提供初始数据,或者提供一个包含初始数据的sql文件。 8 | 9 | 通常来讲,使用fixtrue更加简洁,因为它是数据库无关的,而使用sql初始化更加灵活。 10 | 11 | ## 提供初始数据的fixtures ## 12 | 13 | fixture是数据的集合,让Django了解如何导入到数据库中。创建fixture的最直接的方式,是使用manage.py dumpdata命令,如果数据库中已经有了一些数据。或者你可以手写fixtures。fixtures支持JSON、XML或者YAML(需要安装PyYAML)文档。序列化文档中详细阐述了每一种所支持的序列化格式。 14 | 15 | 下面这个例子展示了一个简单的Person 模型的fixtrue,看起来很像JSON: 16 | 17 | ``` 18 | [ 19 | { 20 | "model": "myapp.person", 21 | "pk": 1, 22 | "fields": { 23 | "first_name": "John", 24 | "last_name": "Lennon" 25 | } 26 | }, 27 | { 28 | "model": "myapp.person", 29 | "pk": 2, 30 | "fields": { 31 | "first_name": "Paul", 32 | "last_name": "McCartney" 33 | } 34 | } 35 | ] 36 | ``` 37 | 38 | 下面是它的YAML格式: 39 | 40 | ``` 41 | - model: myapp.person 42 | pk: 1 43 | fields: 44 | first_name: John 45 | last_name: Lennon 46 | - model: myapp.person 47 | pk: 2 48 | fields: 49 | first_name: Paul 50 | last_name: McCartney 51 | ``` 52 | 53 | 你可以把这些数据储存在你应用的fixtures目录中。 54 | 55 | 加载数据很简单:只要调用manage.py loaddata <fixturename>就好了,其中<fixturename>是你所创建的fixture文件的名字。每次你运行loaddata的时候,数据都会从fixture读出,并且重复加载进数据库。注意这意味着,如果你修改了fixtrue创建的某一行,然后再次运行了 loaddata,你的修改将会被抹掉。 56 | 57 | ## 自动加载初始数据的fixtures ## 58 | 59 | ``` 60 | 1.7中废除: 61 | 62 | 如果一个应用使用了迁移,将不会自动加载fixtures。由于Django 1.9中,迁移将会是必要的,这一行为经权衡之后被废除。 如果你想在一个应用中加载初始数据,考虑在数据迁移中加载它们。 63 | ``` 64 | 65 | 如果你创建了一个命名为 initial_data.[xml/yaml/json]的fixtrue,在你每次运行migrate命令时,fixtrue都会被加载。这非常方面,但是要注意:记住数据在你每次运行migrate命令后都会被刷新。So don’t use initial_data for data you’ll want to edit. 66 | 67 | ## Django在哪里寻找fixture文件 ## 68 | 69 | 通常,Django 在每个应用的fixtures目录中寻找fixture文件。你可以设置FIXTURE_DIRS选项为一个额外目录的列表,Django会从里面寻找。 70 | 71 | 运行manage.py loaddata命令的时候,你也可以指定一个fixture文件的目录,它会覆盖默认设置中的目录。 72 | 73 | > 另见 74 | > 75 | > fixtrues也被用于测试框架来搭建一致性的测试环境。 76 | 77 | ## 提供初始SQL数据 ## 78 | 79 | ``` 80 | 1.7中废除: 81 | 82 | 如果一个应用使用迁移,初始SQL数据将不会加载(包括后端特定的SQL数据)。由于Django 1.9中,迁移将会是必须的,这一行为经权衡后被废除。如果你想在应用中使用初始SQL数据,考虑在数据迁移中使用它们。 83 | ``` 84 | 85 | Django为数据库无关的SQL提供了一个钩子,当你运行migrate命令时,CREATE TABLE语句执行之后就会执行它。你可以使用这个钩子来建立默认的记录,或者创建SQL函数、视图、触发器以及其它。 86 | 87 | 钩子十分简单:Django会在你应用的目录中寻找叫做sql/<modelname>.sql的文件,其中 <modelname>是小写的模型名称。 88 | 89 | 所以如果在myapp应用中存在Person模型,你应该在myapp目录的文件sql/person.sql中添加数据库无关的SQL。下面的例子展示了文件可能会包含什么: 90 | 91 | ``` 92 | INSERT INTO myapp_person (first_name, last_name) VALUES ('John', 'Lennon'); 93 | INSERT INTO myapp_person (first_name, last_name) VALUES ('Paul', 'McCartney'); 94 | ``` 95 | 96 | 每个提供的SQL文件,都应该含有用于插入数据的有效的SQL语句(例如,格式适当的INSERT语句,用分号分隔)。 97 | 98 | 这些SQL文件可被manage.py中的 sqlcustom和sqlall命令阅读。详见manage.py文档。 99 | 100 | 注意如果你有很多SQL数据文件,他们执行的顺序是不确定的。唯一可以确定的是,在你的自定义数据文件被执行之前,所有数据表都被创建好了。 101 | 102 | > 初始SQL数据和测试 103 | > 104 | > 这一技巧不能以测试目的用于提供初始数据。Django的测试框架在每次测试后都会刷新测试数据库的内容。所以,任何使用自定义SQL钩子添加的数据都会丢失。 105 | > 106 | > 如果你需要在测试用例中添加数据,你应该在测试fixture中添加它,或者在测试用例的setUp()中添加。 107 | 108 | 109 | ## 数据库后端特定的SQL数据 ## 110 | 111 | 没有钩子提供给后端特定的SQL数据。例如,你有分别为PostgreSQL和SQLite准备的初始数据文件。对于每个应用,Django都会寻找叫做<app_label>/sql/<modelname>.<backend>.sql的文件,其中<app_label>是小写的模型名称,<modelname>是小写的模型名称,<backend>是你的设置文件中由ENGINE提供的模块名称的最后一部分(例如,如果你定义了一个数据库,ENGINE的值为django.db.backends.sqlite3,Django会寻找<app_label>/sql/<modelname>.sqlite3.sql)。 112 | 113 | 后端特定的SQL数据会先于后端无关的SQL数据执行。例如,如果你的应用包含了sql/person.sql 和sql/person.sqlite3.sql文件,而且你已经安装了SQLite应用,Django会首先执行 sql/person.sqlite3.sql的内容,其次才是sql/person.sql。 -------------------------------------------------------------------------------- /2_6_4_Optimize database access.md: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | 3 | 6 | 7 | # 数据库访问优化 # 8 | 9 | Django的数据库层提供了很多方法来帮助开发者充分的利用他们的数据库。这篇文档收集了相关文档的一些链接,添加了大量提示,并且按照优化数据库使用的步骤的概要来组织。 10 | 11 | ## 性能优先 ## 12 | 13 | 作为通用的编程实践,性能的重要性不用多说。弄清楚你在执行什么查询以及你的开销花在哪里。你也可能想使用外部的项目,像django-debug-toolbar,或者直接监控数据库的工具。 14 | 15 | 记住你可以优化速度、内存占用,甚至二者一起,这取决于你的需求。一些针对其中一个的优化会对另一个不利,但有时会对二者都有帮助。另外,数据库进程做的工作,可能和你在Python代码中做的相同工作不具有相同的开销。决定你的优先级是什么,是你自己的事情,你必须要权衡利弊,按需使用它们,因为这取决于你的应用和服务器。 16 | 17 | 对于下面提到的任何事情,要记住在任何修改后验证一下,确保修改是有利的,并且足够有利,能超过你代码中可读性的下降。下面的所有建议都带有警告,在你的环境中大体原则可能并不适用,或者会起到相反的效果。 18 | 19 | ## 使用标准数据库优化技巧 ## 20 | 21 | ...包括: 22 | 23 | + 索引。在你决定哪些索引应该添加 之后,这一条具有最高优先级。使用Field.db_index或者Meta.index_together在Dhango中添加它们。考虑在你经常使用filter()、exclude()、order_by()和其它方法查询的字段上面添加索引,因为索引有助于加速查找。注意,设计最好的索引方案是一个复杂的、数据库相关的话题,它取决于你应用的细节。持有索引的副作用可能会超过查询速度上的任何收益。 24 | + 合理使用字段类型。 25 | 26 | 我们假设你已经完成了上面这些显而易见的事情。这篇文档剩下的部分,着重于讲解如何以不做无用功的方式使用Django。这篇文档也没有强调用在开销大的操作上其它的优化技巧,像general purpose caching。 27 | 28 | ## 理解查询集 ## 29 | 30 | 理解查询集(QuerySets) 是通过简单的代码获取较好性能至关重要的一步。特别是: 31 | 32 | ### 理解查询集计算 ### 33 | 34 | 要避免性能问题,理解以下几点非常重要: 35 | 36 | + QuerySets是延迟的。 37 | + 什么时候它们被计算出来。 38 | + 数据在内存中如何存储。 39 | 40 | ### 理解缓存属性 ### 41 | 42 | 和整个QuerySet的缓存相同,ORM对象的属性的结果中也存在缓存。通常来说,不可调用的属性会被缓存。例如下面的博客模型示例: 43 | 44 | ``` 45 | >>> entry = Entry.objects.get(id=1) 46 | >>> entry.blog # Blog object is retrieved at this point 47 | >>> entry.blog # cached version, no DB access 48 | ``` 49 | 50 | 但是通常来讲,可调用的属性每一次都会访问数据库。 51 | 52 | ``` 53 | >>> entry = Entry.objects.get(id=1) 54 | >>> entry.authors.all() # query performed 55 | >>> entry.authors.all() # query performed again 56 | ``` 57 | 58 | 要小心当你阅读模板代码的时候 —— 模板系统不允许使用圆括号,但是会自动调用callable对象,会隐藏上述区别。 59 | 60 | 要小心使用你自定义的属性 —— 实现所需的缓存取决于你,例如使用cached_property装饰符。 61 | 62 | ### 使用with模板标签 ### 63 | 64 | 要利用QuerySet的缓存行为,你或许需要使用with模板标签。 65 | 66 | ### 使用iterator() ### 67 | 68 | 当你有很多对象时,QuerySet的缓存行为会占用大量的内存。这种情况下,采用iterator()解决。 69 | 70 | ## 在数据库中而不是Python中做数据库的工作 ## 71 | 72 | 比如: 73 | 74 | + 在最基础的层面上,使用过滤器和反向过滤器对数据库进行过滤。 75 | + 使用F 表达式在相同模型中基于其他字段进行过滤。 76 | + 使用数据库中的注解和聚合。 77 | 78 | 如果上面那些都不够用,你可以自己生成SQL语句: 79 | 80 | ### 使用QuerySet.extra() ### 81 | 82 | extra()是一个移植性更差,但是功能更强的方法,它允许一些SQL语句显式添加到查询中。如果这些还不够强大: 83 | 84 | ### 使用原始的SQL ### 85 | 86 | 编写你自己的自定义SQL语句,来获取数据或者填充模型。使用django.db.connection.queries来了解Django为你编写了什么,以及从这里开始。 87 | 88 | ## 用唯一的被或索引的列来检索独立对象 ## 89 | 90 | 有两个原因在get()中,用带有unique或者db_index的列检索独立对象。首先,由于查询经过了数据库的索引,所以会更快。其次,如果很多对象匹配查询,查询会更慢一些;列上的唯一性约束确保这种情况永远不会发生。 91 | 92 | 所以,使用博客模型的例子: 93 | 94 | ``` 95 | >>> entry = Entry.objects.get(id=10) 96 | ``` 97 | 98 | 会快于: 99 | 100 | ``` 101 | >>> entry = Entry.object.get(headline="News Item Title") 102 | ``` 103 | 104 | 因为id被数据库索引,而且是唯一的。 105 | 106 | 下面这样做会十分缓慢: 107 | 108 | ``` 109 | >>> entry = Entry.objects.get(headline__startswith="News") 110 | ``` 111 | 112 | 首先, headline没有被索引,它会使查询变得很慢: 113 | 114 | 其次,这次查找并不确保返回唯一的对象。如果查询匹配到多于一个对象,它会在数据库中遍历和检索所有这些对象。如果记录中返回了成百上千个对象,代价是非常大的。如果数据库运行在分布式服务器上,网络开销和延迟也是一大因素,代价会是它们的组合。 115 | 116 | ## 一次性检索你需要的任何东西 ## 117 | 118 | 在不同的位置多次访问数据库,一次获取一个数据集,通常来说不如在一次查询中获取它们更高效。如果你在一个循环中执行查询,这尤其重要。有可能你会做很多次数据库查询,但只需要一次就够了。所以: 119 | 120 | ### 使用QuerySet.select_related()和prefetch_related() ### 121 | 122 | 充分了解并使用select_related()和prefetch_related(): 123 | 124 | + 在视图的代码中, 125 | + 以及在适当的管理器和默认管理器中。要意识到你的管理器什么时候被使用和不被使用;有时这很复杂,所以不要有任何假设。 126 | 127 | ## 不要获取你不需要的东西 ## 128 | 129 | ### 使用QuerySet.values()和values_list() ### 130 | 131 | 当你仅仅想要一个带有值的字典或者列表,并不需要使用ORM模型对象时,可以适当使用values()。对于在模板代码中替换模型对象,这样会非常有用 —— 只要字典中带有的属性和模板中使用的一致,就没问题。 132 | 133 | ### 使用QuerySet.defer()和only() ### 134 | 135 | 如果一些数据库的列你并不需要(或者大多数情况下并不需要),使用defer()和only()来避免加载它们。注意如果你确实要用到它们,ORM会在另外的查询之中获取它们。如果你不能够合理地使用这些函数,不如不用。 136 | 137 | 另外,当建立起一个带有延迟字段的模型时,要意识到一些(小的、额外的)消耗会在Django内部产生。不要不分析数据库就盲目使用延迟字段,因为数据库必须从磁盘中读取大多数非text和VARCHAR数据,在结果中作为单独的一行,即使其中的列很少。 defer()和only()方法在你可以避免加载大量文本数据,或者可能要花大量时间处理而返回给Python的字段时,特别有帮助。像往常一样,应该先写出个大概,之后再优化。 138 | 139 | ### 使用QuerySet.count() ### 140 | 141 | ...如果你想要获取大小,不要使用 len(queryset)。 142 | 143 | ### 使用QuerySet.exists() ### 144 | 145 | ...如果你想要知道是否存在至少一个结果,不要使用if queryset。 146 | 147 | 但是: 148 | 149 | ### 不要过度使用 count() 和 exists() ### 150 | 151 | 如果你需要查询集中的其他数据,就把它加载出来。 152 | 153 | 例如,假设Email模型有一个body属性,并且和User有多对多的关联,下面的的模板代码是最优的: 154 | 155 | ``` 156 | {% if display_inbox %} 157 | {% with emails=user.emails.all %} 158 | {% if emails %} 159 |

You have {{ emails|length }} email(s)

160 | {% for email in emails %} 161 |

{{ email.body }}

162 | {% endfor %} 163 | {% else %} 164 |

No messages today.

165 | {% endif %} 166 | {% endwith %} 167 | {% endif %} 168 | ``` 169 | 170 | 这是因为: 171 | 172 | + 因为查询集是延迟加载的,如果‘display_inbox’为False,不会查询数据库。 173 | + 使用with意味着我们为了以后的使用,把user.emails.all储存在一个变量中,允许它的缓存被复用。 174 | + {% if emails %}的那一行调用了QuerySet.__bool__(),它导致user.emails.all()查询在数据库上执行,并且至少在第一行以一个ORM对象的形式返回。如果没有任何结果,会返回False,反之为True。 175 | + {{ emails|length }}调用了QuerySet.__len__()方法,填充了缓存的剩余部分,而且并没有执行另一次查询。 176 | + for循环的迭代器访问了已经缓存的数据。 177 | 178 | 总之,这段代码做了零或一次查询。唯一一个慎重的优化就是with标签的使用。在任何位置使用QuerySet.exists()或者QuerySet.count()都会导致额外的查询。 179 | 180 | ### 使用QuerySet.update()和delete() ### 181 | 182 | 通过QuerySet.update()使用批量的SQL UPDATE语句,而不是获取大量对象,设置一些值再单独保存。与此相似,在可能的地方使用批量deletes。 183 | 184 | 但是要注意,这些批量的更新方法不会在单独的实例上面调用save()或者delete()方法,意思是任何你向这些方法添加的自定义行为都不会被执行,包括由普通数据库对象的信号驱动的任何方法。 185 | 186 | ### 直接使用外键的值 ### 187 | 188 | 如果你仅仅需要外键当中的一个值,要使用对象上你已经取得的外键的值,而不是获取整个关联对象再得到它的主键。例如,执行: 189 | 190 | ``` 191 | entry.blog_id 192 | ``` 193 | 194 | 而不是: 195 | 196 | ``` 197 | entry.blog.id 198 | ``` 199 | 200 | ### 不要做无谓的排序 ### 201 | 202 | 排序并不是没有代价的;每个需要排序的字段都是数据库必须执行的操作。如果一个模型具有默认的顺序(Meta.ordering),并且你并不需要它,通过在查询集上无参调用order_by() 来移除它。 203 | 204 | 向你的数据库添加索引可能有助于提升排序性能。 205 | 206 | ## 整体插入 ## 207 | 208 | 创建对象时,尽可能使用bulk_create()来减少SQL查询的数量。例如: 209 | 210 | ``` 211 | Entry.objects.bulk_create([ 212 | Entry(headline="Python 3.0 Released"), 213 | Entry(headline="Python 3.1 Planned") 214 | ]) 215 | ``` 216 | 217 | ...更优于: 218 | 219 | ``` 220 | Entry.objects.create(headline="Python 3.0 Released") 221 | Entry.objects.create(headline="Python 3.1 Planned") 222 | ``` 223 | 224 | 注意该方法有很多注意事项,所以确保它适用于你的情况。 225 | 226 | 这也可以用在ManyToManyFields中,所以: 227 | 228 | ``` 229 | my_band.members.add(me, my_friend) 230 | ``` 231 | 232 | ...更优于: 233 | 234 | ``` 235 | my_band.members.add(me) 236 | my_band.members.add(my_friend) 237 | ``` 238 | 239 | ...其中Bands和Artists具有多对多关联。 240 | 241 | {% endraw %} 242 | -------------------------------------------------------------------------------- /2_The model layer.md: -------------------------------------------------------------------------------- 1 | # 模型层 # 2 | 3 | Django 提供了一个抽象层(模型),对您的Web 应用中的数据进行构建及操作。通过以下内容来了解更多: 4 | -------------------------------------------------------------------------------- /3_1.md: -------------------------------------------------------------------------------- 1 | # 基础 -------------------------------------------------------------------------------- /3_1_2_View functions.md: -------------------------------------------------------------------------------- 1 | # 编写视图 # 2 | 3 | 一个视图函数,或者简短来说叫做视图,是一个简单的Python函数,它接受web请求,并且返回web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了能够把代码放在某个地方,惯例是把视图放在叫做views.py的文件中,然后把它放到你的项目或者应用目录里。 4 | 5 | ## 一个简单的视图 ## 6 | 7 | 下面是一个返回当前日期和时间作为HTML文档的视图: 8 | 9 | ``` 10 | from django.http import HttpResponse 11 | import datetime 12 | 13 | def current_datetime(request): 14 | now = datetime.datetime.now() 15 | html = "It is now %s." % now 16 | return HttpResponse(html) 17 | ``` 18 | 19 | 让我们逐行阅读上面的代码: 20 | 21 | + 首先,我们从 django.http模块导入了HttpResponse类,以及Python的datetime库。 22 | + 接着,我们定义了current_datetime函数。它是一个视图函数。每个视图函数都应接收HttpRequest对象作为第一个参数,一般叫做request。 23 | + 注意视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够精确地反映出它的功能。 24 | + 这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都要返回HttpResponse对象。(有例外,我们接下来会讲。) 25 | 26 | > Django中的时区 27 | > 28 | > Django中包含一个TIME_ZONE设置,默认为America/Chicago。可能并不是你住的地方,所以你可能会在设置文件里修改它。 29 | 30 | ## 把你的URL映射到视图 ## 31 | 32 | 所以,再重复一遍,这个视图函数返回了一个包含当前日期和时间的HTML页面。你需要创建URLconf来展示在特定的URL这一视图; 详见URL 分发器。 33 | 34 | ## 返回错误 ## 35 | 36 | 在Django中返回HTTP错误是相当容易的。有一些HttpResponse的子类代表不是200(“OK”)的HTTP状态码。你可以在request/response文档中找到所有可用的子类。你可以返回那些子类的一个实例,而不是普通的HttpResponse ,来表示一个错误。例如: 37 | 38 | ``` 39 | from django.http import HttpResponse, HttpResponseNotFound 40 | 41 | def my_view(request): 42 | # ... 43 | if foo: 44 | return HttpResponseNotFound('

Page not found

') 45 | else: 46 | return HttpResponse('

Page was found

') 47 | ``` 48 | 49 | 由于一些状态码不太常用,所以不是每个状态码都有一个特化的子类。然而,如HttpResponse文档中所说的那样,你也可以向HttpResponse的构造器传递HTTP状态码,来创建你想要的任何状态码的返回类。例如: 50 | 51 | ``` 52 | from django.http import HttpResponse 53 | 54 | def my_view(request): 55 | # ... 56 | 57 | # Return a "created" (201) response code. 58 | return HttpResponse(status=201) 59 | ``` 60 | 61 | 由于404错误是最常见的HTTP错误,所以处理这一错误的方式非常便利。 62 | 63 | ## Http404异常 ## 64 | 65 | **class django.http.Http404** 66 | 67 | 当你返回一个像HttpResponseNotFound这样的错误时,它会输出这个错误页面的HTML作为结果: 68 | 69 | ``` 70 | return HttpResponseNotFound('

Page not found

') 71 | ``` 72 | 73 | 为了便利起见,也因为你的站点有个一致的404页面是个好主意,Django提供了Http404异常。如果你在视图函数中的任何地方抛出Http404异常,Django都会捕获它,并且带上HTTP404错误码返回你应用的标准错误页面。 74 | 75 | 像这样: 76 | 77 | ``` 78 | from django.http import Http404 79 | from django.shortcuts import render_to_response 80 | from polls.models import Poll 81 | 82 | def detail(request, poll_id): 83 | try: 84 | p = Poll.objects.get(pk=poll_id) 85 | except Poll.DoesNotExist: 86 | raise Http404("Poll does not exist") 87 | return render_to_response('polls/detail.html', {'poll': p}) 88 | ``` 89 | 90 | 为了尽可能利用 Http404,你应该创建一个用来在404错误产生时展示的模板。这个模板应该叫做404.html,并且在你的模板树中位于最顶层。 91 | 92 | 如果你在抛出Http404异常时提供了一条消息,当DEBUG为True时它会出现在标准404模板的展示中。你可以将这些消息用于调试;但他们通常不适用于404模板本身。 93 | 94 | ## 自定义错误视图 ## 95 | 96 | Django中默认的错误视图对于大多数web应用已经足够了,但是如果你需要任何自定义行为,重写它很容易。只要在你的URLconf中指定下面的处理器(在其他任何地方设置它们不会有效)。 97 | 98 | handler404覆盖了page_not_found()视图: 99 | 100 | ``` 101 | handler404 = 'mysite.views.my_custom_page_not_found_view' 102 | ``` 103 | 104 | handler500覆盖了server_error()视图: 105 | 106 | ``` 107 | handler500 = 'mysite.views.my_custom_error_view' 108 | ``` 109 | 110 | handler403覆盖了permission_denied()视图: 111 | 112 | ``` 113 | handler403 = 'mysite.views.my_custom_permission_denied_view' 114 | ``` 115 | 116 | handler400覆盖了bad_request()视图: 117 | 118 | ``` 119 | handler400 = 'mysite.views.my_custom_bad_request_view' 120 | ``` -------------------------------------------------------------------------------- /3_1_4_Decorators.md: -------------------------------------------------------------------------------- 1 | # 视图装饰器 # 2 | 3 | Django为视图提供了数个装饰器,用以支持相关的HTTP服务。 4 | 5 | ## 允许的HTTP 方法 ## 6 | 7 | `django.views.decorators.http` 包里的装饰器可以基于请求的方法来限制对视图的访问。若条件不满足会返回 `django.http.HttpResponseNotAllowed`。 8 | 9 | `require_http_methods`(request_method_list)[source] 10 | 11 | 限制视图只能服务规定的http方法。用法: 12 | 13 | ``` 14 | from django.views.decorators.http import require_http_methods 15 | 16 | @require_http_methods(["GET", "POST"]) 17 | def my_view(request): 18 | # I can assume now that only GET or POST requests make it this far 19 | # ... 20 | pass 21 | ``` 22 | 23 | 注意,方法名必须大写。 24 | 25 | `require_GET()` 26 | 27 | 只允许视图接受GET方法的装饰器。 28 | 29 | `require_POST()` 30 | 31 | 只允许视图接受POST方法的装饰器。 32 | 33 | `require_safe()` 34 | 35 | 只允许视图接受 GET 和 HEAD 方法的装饰器。 这些方法通常被认为是安全的,因为方法不该有请求资源以外的目的。 36 | 37 | > 注 38 | > 39 | > Django 会自动清除对HEAD 请求的响应中的内容而只保留头部,所以在你的视图中你处理HEAD 请求的方式可以完全与GET 请求一致。因为某些软件,例如链接检查器,依赖于HEAD 请求,所以你可能应该使用`require_safe` 而不是`require_GET`。 40 | 41 | ## 可控制的视图处理 ## 42 | 43 | `django.views.decorators.http` 中的以下装饰器可以用来控制特定视图的缓存行为。 44 | 45 | `condition`(etag_func=None, last_modified_func=None)[source] 46 | 47 | 48 | `etag`(etag_func)[source] 49 | 50 | `last_modified`(last_modified_func)[source] 51 | 52 | 这些装饰器可以用于生成ETag 和Last-Modified 头部;参考 conditional view processing. 53 | 54 | ## GZip 压缩 ## 55 | 56 | `django.views.decorators.gzip` 里的装饰器基于每个视图控制其内容压缩。 57 | 58 | `gzip_page()` 59 | 60 | 如果浏览器允许gzip 压缩,这个装饰器将对内容进行压缩。它设置相应的`Vary`头部,以使得缓存根据`Accept-Encoding`头来存储信息。 61 | 62 | ## Vary 头部 ## 63 | 64 | `django.views.decorators.vary` 可以用来基于特定的请求头部来控制缓存。 65 | 66 | `vary_on_cookie`(func)[source] 67 | 68 | `vary_on_headers`(\*headers)[source] 69 | 70 | 到当构建缓存的键时,Vary 头部定义一个缓存机制应该考虑的请求头。 71 | 72 | 参见[使用vary 头部](http://python.usyiyi.cn/django/topics/cache.html#using-vary-headers)。 73 | 74 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Decorators](https://docs.djangoproject.com/en/1.8/topics/http/decorators/)。 75 | > 76 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 77 | > 78 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 79 | -------------------------------------------------------------------------------- /3_2.md: -------------------------------------------------------------------------------- 1 | # 参考 -------------------------------------------------------------------------------- /3_2_1_Built-in Views.md: -------------------------------------------------------------------------------- 1 | # 内建的视图 # 2 | 3 | 有几个Django 的内建视图在编写视图 中讲述,文档的其它地方也会有所讲述。 4 | 5 | ## 开发环境中的文件服务器 ## 6 | 7 | **static.serve(request, path, document_root, show_indexes=False)** 8 | 9 | 在本地的开发环境中,除了你的项目中的静态文件,可能还有一些文件,出于方便,你希望让Django 来作为服务器。serve() 视图可以用来作为任意目录的服务器。(该视图不能用于生产环境,应该只用于开发时辅助使用;在生产环境中你应该使用一个真实的前端Web 服务器来服务这些文件)。 10 | 11 | 最常见的例子是用户上传文档到`MEDIA_ROOT` 中。`django.contrib.staticfiles` 用于静态文件且没有对用户上传的文件做处理,但是你可以通过在URLconf 中添加一些内容来让Django 作为MEDIA_ROOT 的服务器: 12 | 13 | ``` 14 | from django.conf import settings 15 | from django.views.static import serve 16 | 17 | # ... the rest of your URLconf goes here ... 18 | 19 | if settings.DEBUG: 20 | urlpatterns += [ 21 | url(r'^media/(?P.*)$', serve, { 22 | 'document_root': settings.MEDIA_ROOT, 23 | }), 24 | ] 25 | ``` 26 | 27 | 注意,这里的代码片段假设你的`MEDIA_URL`的值为`'/media/'`。它将调用`serve()` 视图,传递来自URLconf 的路径和(必选的)`document_root` 参数。 28 | 29 | 因为定义这个URL 模式显得有些笨拙,Django 提供一个小巧的URL 辅助函数`static()`,它接收`MEDIA_URL`这样的参数作为前缀和视图的路径如`'django.views.static.serve'`。其它任何函数参数都将透明地传递给视图。 30 | 31 | ## 错误视图 ## 32 | 33 | Django 原生自带几个默认视图用于处理HTTP 错误。若要使用你自定义的视图覆盖它们,请参见自定义错误视图。 34 | 35 | ### 404 (page not found) 视图 ### 36 | 37 | **defaults.page_not_found(request, template_name='404.html')** 38 | 39 | 当你在一个视图中引发Http404 时,Django 将加载一个专门的视图用于处理404 错误。默认为`django.views.defaults.page_not_found()` 视图,它产生一个非常简单的“Not Found” 消息或者渲染`404.html`模板,如果你在根模板目录下创建了它的话。 40 | 41 | 默认的404 视图将传递一个变量给模板:`request_path`,它是导致错误的URL。 42 | 43 | 关于404 视图需要注意的3点: 44 | 45 | + 如果Django 在检测URLconf 中的每个正则表达式后没有找到匹配的内容也将调用404 视图。 46 | + 404 视图会被传递一个`RequestContext`并且可以访问模板上下文处理器提供的变量(例如`MEDIA_URL`)。 47 | + 如果DEBUG 设置为`True`(在你的`settings` 模块中),那么将永远不会调用404 视图,而是显示你的URLconf 并带有一些调试信息。 48 | 49 | ### 500 (server error) 视图 ### 50 | 51 | **defaults.server_error(request, template_name='500.html')** 52 | 53 | 类似地,在视图代码中出现运行时错误,Django 将执行特殊情况下的行为。如果一个视图导致异常,Django 默认情况下将调用`django.views.defaults.server_error` 视图,它产生一个非常简单的“Server Error” 消息或者渲染`500.html`,如果你在你的根模板目录下定义了它的话。 54 | 55 | 默认的500 视图不会传递变量给`500.html` 模板,且使用一个空`Context` 来渲染以减少再次出现错误的可能性。 56 | 57 | 如果`DEBUG` 设置为`True`(在你的`settings` 模块中),那么将永远不会调用500 视图,而是显示回溯并带有一些调试信息。 58 | 59 | ### 403 (HTTP Forbidden) 视图 ### 60 | 61 | **defaults.permission_denied(request, template_name='403.html')** 62 | 63 | 与404 和500 视图一样,Django 具有一个处理403 Forbidden 错误的视图。如果一个视图导致一个403 视图,那么Django 将默认调用`django.views.defaults.permission_denied`视图。 64 | 65 | 该视图加载并渲染你的根模板目录下的`403.html`,如果这个文件不存在则根据RFC 2616(HTTP 1.1 Specification)返回“403 Forbidden”文本。 66 | 67 | `django.views.defaults.permission_denied` 通过`PermissionDenied` 异常触发。若要拒绝访问一个视图,你可以这样视图代码: 68 | 69 | ``` 70 | from django.core.exceptions import PermissionDenied 71 | 72 | def edit(request, pk): 73 | if not request.user.is_staff: 74 | raise PermissionDenied 75 | # ... 76 | ``` 77 | 78 | ### 400 (bad request) 视图 ### 79 | 80 | **defaults.bad_request(request, template_name='400.html')** 81 | 82 | 当Django 中引发一个`SuspiciousOperation` 时,它可能通过Django 的一个组件处理(例如重设会话的数据)。如果没有特殊处理,Django 将认为当前的请求时一个'bad request' 而不是一个server error。 83 | 84 | `django.views.defaults.bad_request` 和server_error 视图非常相似,除了返回400 状态码来表示错误来自客户端的操作。 85 | 86 | `bad_request` 视图同样只是在`DEBUG` 为`False` 时使用。 87 | 88 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Built-in Views](https://docs.djangoproject.com/en/1.8/ref/views/)。 89 | > 90 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 91 | > 92 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 93 | -------------------------------------------------------------------------------- /3_2_3_TemplateResponse objects.md: -------------------------------------------------------------------------------- 1 | # TemplateResponse 和 SimpleTemplateResponse # 2 | 3 | 标准的`HttpResponse` 对象是静态的结构。在构造的时候提供给它们一个渲染之前的内容,但是当内容改变时它们却不能很容易地完成相应的改变。 4 | 5 | 然而,有时候允许装饰器或者中间件在响应被构造之后修改它是很有用的。例如,你可能想改变使用的模板,或者添加额外的数据到上下文中。 6 | 7 | `TemplateResponse` 提供了实现这一点的方法。与基本的HttpResponse 对象不同,`TemplateResponse` 对象会记住视图提供的模板和上下文的详细信息来计算响应。响应的最终结果在后来的响应处理过程中直到需要时才计算。 8 | 9 | ## SimpleTemplateResponse 对象 ## 10 | 11 | `class SimpleTemplateResponse[source]` 12 | 13 | ### 属性 ### 14 | 15 | `SimpleTemplateResponse.template_name` 16 | 17 | 渲染的模板的名称。接收一个与后端有关的模板对象(例如`get_template()` 返回的对象)、模板的名称或者一个模板名称列表。 18 | 19 | 例如:`['foo.html', 'path/to/bar.html']` 20 | 21 | ``` 22 | Deprecated since version 1.8: 23 | 24 | template_name 以前只接受一个 Template。 25 | ``` 26 | 27 | `SimpleTemplateResponse.context_data` 28 | 29 | 渲染模板时用到的上下文数据。它必须是一个`dict`。 30 | 31 | 例如:`{'foo': 123}` 32 | 33 | ``` 34 | Deprecated since version 1.8: 35 | 36 | context_data 以前只接受一个 Context。 37 | ``` 38 | 39 | `SimpleTemplateResponse.rendered_content[source]` 40 | 41 | 使用当前的模板和上下文数据渲染出来的响应内容。 42 | 43 | `SimpleTemplateResponse.is_rendered[source]` 44 | 45 | 一个布尔值,表示响应内容是否已经渲染。 46 | 47 | ### 方法 ### 48 | 49 | `SimpleTemplateResponse.__init__(template, context=None, content_type=None, status=None, charset=None, using=None)[source]` 50 | 51 | 使用给定的模板、上下文、Content-Type、HTTP 状态和字符集初始化一个`SimpleTemplateResponse` 对象。 52 | 53 | `template` 54 | 55 | 一个与后端有关的模板对象(例如`get_template()` 返回的对象)、模板的名称或者一个模板名称列表。 56 | 57 | ``` 58 | Deprecated since version 1.8: 59 | 60 | template 以前只接受一个Template。 61 | ``` 62 | 63 | `context` 64 | 65 | 一个`dict`,包含要添加到模板上下文中的值。它默认是一个空的字典。 66 | 67 | ``` 68 | Deprecated since version 1.8: 69 | 70 | context 以前只接受一个Context。 71 | ``` 72 | 73 | `content_type` 74 | 75 | HTTP `Content-Type `头部包含的值,包含MIME 类型和字符集的编码。 如果指定`content_type`,则使用它的值。 否则,使用`DEFAULT_CONTENT_TYPE`。 76 | 77 | `status` 78 | 79 | 响应的HTTP 状态码。 80 | 81 | `charset` 82 | 83 | 响应编码使用的字符集。 如果没有给出则从`content_type`中提取,如果提取不成功则使用 `DEFAULT_CHARSET` 设置。 84 | 85 | `using` 86 | 87 | 加载模板使用的模板引擎的名称。 88 | 89 | ``` 90 | Changed in Django 1.8: 91 | 92 | 添加charset 和using 参数。 93 | ``` 94 | 95 | `SimpleTemplateResponse.resolve_context(context)[source]` 96 | 97 | 预处理即将用于渲染模板的上下文数据。接受包含上下文数据的一个`dict`。默认返回同一个`dict`。 98 | 99 | 若要自定义上下文,请覆盖这个方法。 100 | 101 | ``` 102 | Changed in Django 1.8: 103 | 104 | resolve_context 返回一个dict。它以前返回一个Context。 105 | ``` 106 | 107 | ``` 108 | Deprecated since version 1.8: 109 | 110 | resolve_context 不再接受Context。 111 | ``` 112 | 113 | `SimpleTemplateResponse.resolve_template(template)[source]` 114 | 115 | 解析渲染用到的模板实例。接收一个与后端有关的模板对象(例如`get_template()` 返回的对象)、模板的名称或者一个模板名称列表。 116 | 117 | 返回将被渲染的模板对象。 118 | 119 | 若要自定义模板的加载,请覆盖这个方法。 120 | 121 | ``` 122 | Changed in Django 1.8: 123 | 124 | resolve_template 返回一个与后端有关的模板对象。它以前返回一个Template。 125 | ``` 126 | 127 | ``` 128 | Deprecated since version 1.8: 129 | 130 | resolve_template 不再接受一个 Template。 131 | ``` 132 | 133 | `SimpleTemplateResponse.add_post_render_callback()[source]` 134 | 135 | 添加一个渲染之后调用的回调函数。这个钩子可以用来延迟某些特定的处理操作(例如缓存)到渲染之后。 136 | 137 | 如果`SimpleTemplateResponse` 已经渲染,那么回调函数将立即执行。 138 | 139 | 调用的时只传递给回调函数一个参数 —— 渲染后的 `SimpleTemplateResponse` 实例。 140 | 141 | 如果回调函数返回非`None` 值,它将用作响应并替换原始的响应对象(以及传递给下一个渲染之后的回调函数,以此类推)。 142 | 143 | `SimpleTemplateResponse.render()[source]` 144 | 145 | 设置`response.content` 为`SimpleTemplateResponse.rendered_content` 的结果,执行所有渲染之后的回调函数,最后返回生成的响应对象。 146 | 147 | `render()` 只在第一次调用它时其作用。以后的调用将返回第一次调用的结果。 148 | 149 | ## TemplateResponse 对象 ## 150 | 151 | `class TemplateResponse[source]` 152 | 153 | `TemplateResponse` 是`SimpleTemplateResponse` 的子类,而且能知道当前的`HttpRequest`。 154 | 155 | ### 方法 ### 156 | 157 | `TemplateResponse.__init__(request, template, context=None, content_type=None, status=None, current_app=None, charset=None, using=None)[source]` 158 | 159 | 使用给定的模板、上下文、`Content-Type`、HTTP 状态和字符集实例化一个`TemplateResponse` 对象。 160 | 161 | `request` 162 | 163 | An `HttpRequest` instance. 164 | 165 | `template` 166 | 167 | 一个与后端有关的模板对象(例如`get_template()` 返回的对象)、模板的名称或者一个模板名称列表。 168 | 169 | ``` 170 | Deprecated since version 1.8: 171 | 172 | template 以前只接受一个Template。 173 | ``` 174 | 175 | `context` 176 | 177 | 一个`dict`,包含要添加到模板上下文中的值。 它默认是一个空的字典。 178 | 179 | ``` 180 | Deprecated since version 1.8: 181 | 182 | context 以前只接受一个Context。 183 | ``` 184 | 185 | `content_type` 186 | 187 | HTTP `Content-Type` 头部包含的值,包含MIME 类型和字符集的编码。如果指定`content_type`,则使用它的值。否则,使用`DEFAULT_CONTENT_TYPE`。 188 | 189 | `status` 190 | 191 | 响应的HTTP 状态码。 192 | 193 | `current_app` 194 | 195 | 包含当前视图的应用。更多信息,参见带命名空间的URL 解析策略。 196 | 197 | ``` 198 | Deprecated since version 1.8: 199 | 200 | 废弃current_app 参数。你应该去设置request.current_app。 201 | ``` 202 | 203 | `charset` 204 | 205 | 响应编码使用的字符集。如果没有给出则从content_type中提取,如果提取不成功则使用 DEFAULT_CHARSET 设置。 206 | 207 | `using` 208 | 209 | 加载模板使用的模板引擎的名称。 210 | 211 | ``` 212 | Changed in Django 1.8: 213 | 214 | 添加charset 和using 参数。 215 | ``` 216 | 217 | ## 渲染的过程 ## 218 | 219 | 在`TemplateResponse` 实例返回给客户端之前,它必须被渲染。渲染的过程采用模板和上下文变量的中间表示形式,并最终将它转换为可以发送给客户端的字节流。 220 | 221 | 三种情况下会渲染`TemplateResponse`: 222 | 223 | + 当使用SimpleTemplateResponse.render() 方法显示渲染TemplateResponse 实例的时候。 224 | + 当通过给response.content 赋值显式设置响应内容的时候。 225 | + 在应用模板响应中间件之后,应用响应中间件之前。 226 | 227 | `TemplateResponse` 只能渲染一次。`SimpleTemplateResponse.render()` 的第一次调用设置响应的内容;以后的响应不会改变响应的内容。 228 | 229 | 然而,当显式给`response.content` 赋值时,修改会始终生效。如果你想强制重新渲染内容,你可以重新计算渲染的内容并手工赋值给响应的内容: 230 | 231 | ``` 232 | # Set up a rendered TemplateResponse 233 | >>> from django.template.response import TemplateResponse 234 | >>> t = TemplateResponse(request, 'original.html', {}) 235 | >>> t.render() 236 | >>> print(t.content) 237 | Original content 238 | 239 | # Re-rendering doesn't change content 240 | >>> t.template_name = 'new.html' 241 | >>> t.render() 242 | >>> print(t.content) 243 | Original content 244 | 245 | # Assigning content does change, no render() call required 246 | >>> t.content = t.rendered_content 247 | >>> print(t.content) 248 | New content 249 | ``` 250 | 251 | ### 渲染后的回调函数 ### 252 | 253 | 某些操作 —— 例如缓存 —— 不可以在没有渲染的模板上执行。它们必须在完整的渲染后的模板上执行。 254 | 255 | 如果你正在使用中间件,解决办法很容易。中间件提供多种在从视图退出时处理响应的机会。如果你向响应中间件添加一些行为,它们将保证在模板渲染之后执行。 256 | 257 | 然而,如果正在使用装饰器,就不会有这样的机会。装饰器中定义的行为会立即执行。 258 | 259 | 为了补偿这一点(以及其它类似的使用情形)`TemplateResponse` 允许你注册在渲染完成时调用的回调函数。使用这个回调函数,你可以延迟某些关键的处理直到你可以保证渲染后的内容是可以访问的。 260 | 261 | 要定义渲染后的回调函数,只需定义一个接收一个响应作为参数的函数并将这个函数注册到模板响应中: 262 | 263 | ``` 264 | from django.template.response import TemplateResponse 265 | 266 | def my_render_callback(response): 267 | # Do content-sensitive processing 268 | do_post_processing() 269 | 270 | def my_view(request): 271 | # Create a response 272 | response = TemplateResponse(request, 'mytemplate.html', {}) 273 | # Register the callback 274 | response.add_post_render_callback(my_render_callback) 275 | # Return the response 276 | return response 277 | ``` 278 | 279 | `my_render_callback()` 将在`mytemplate.html` 渲染之后调用,并将被传递一个`TemplateResponse` 实例作为参数。 280 | 281 | 如果模板已经渲染,回调函数将立即执行。 282 | 283 | ## 使用TemplateResponse 和SimpleTemplateResponse ## 284 | 285 | `TemplateResponse` 对象和普通的`django.http.HttpResponse` 一样可以用于任何地方。它可以用来作为`render()` 和`render_to_response()` 的另外一种选择。 286 | 287 | 例如,下面这个简单的视图使用一个简单模板和包含查询集的上下文返回一个`TemplateResponse`: 288 | 289 | ``` 290 | from django.template.response import TemplateResponse 291 | 292 | def blog_index(request): 293 | return TemplateResponse(request, 'entry_list.html', {'entries': Entry.objects.all()}) 294 | ``` 295 | 296 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[TemplateResponse objects](https://docs.djangoproject.com/en/1.8/ref/template-response/)。 297 | > 298 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 299 | > 300 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 301 | -------------------------------------------------------------------------------- /3_3.md: -------------------------------------------------------------------------------- 1 | # 文件上传 -------------------------------------------------------------------------------- /3_3_1_Overview.md: -------------------------------------------------------------------------------- 1 | # 文件上传 # 2 | 当Django在处理文件上传的时候,文件数据被保存在`request. FILES` (更多关于 `request` 对象的信息 请查看 [请求和响应对象](http://python.usyiyi.cn/django/ref/request-response.html))。这篇文档阐述了文件如何上传到内存和硬盘,以及如何自定义默认的行为。 3 | 4 | > 警告 5 | > 6 | > 允许任意用户上传文件是存在安全隐患的。更多细节请在[用户上传的内容](http://python.usyiyi.cn/django/topics/security.html#user-uploaded-content-security)中查看有关安全指导的话题。 7 | 8 | 9 | ## 基本的文件上传 ## 10 | 11 | 考虑一个简单的表单,它含有一个`FileField`: 12 | 13 | ``` 14 | # In forms.py... 15 | from django import forms 16 | 17 | class UploadFileForm(forms.Form): 18 | title = forms.CharField(max_length=50) 19 | file = forms.FileField() 20 | ``` 21 | 22 | 处理这个表单的视图会在`request`中接受到上传文件的数据。`FILES`是个字典,它包含每个`FileField`的键 (或者 `ImageField`,`FileField`的子类)。这样的话就可以用`request.FILES['file']`来存放表单中的这些数据了。 23 | 24 | 注意`request.FILES`会仅仅包含数据,如果请求方法为`POST`,并且发送请求的`
`拥有`enctype="multipart/form-data"`属性。否则`request.FILES`为空。 25 | 26 | 大多数情况下,你会简单地从`request`向表单中传递数据,就像[绑定上传文件到表单](http://python.usyiyi.cn/django/ref/forms/api.html#binding-uploaded-files)描述的那样。这样类似于: 27 | 28 | ``` 29 | from django.http import HttpResponseRedirect 30 | from django.shortcuts import render_to_response 31 | from .forms import UploadFileForm 32 | 33 | # Imaginary function to handle an uploaded file. 34 | from somewhere import handle_uploaded_file 35 | 36 | def upload_file(request): 37 | if request.method == 'POST': 38 | form = UploadFileForm(request.POST, request.FILES) 39 | if form.is_valid(): 40 | handle_uploaded_file(request.FILES['file']) 41 | return HttpResponseRedirect('/success/url/') 42 | else: 43 | form = UploadFileForm() 44 | return render_to_response('upload.html', {'form': form}) 45 | ``` 46 | 47 | 注意我们必须向表单的构造器中传递`request.FILES`。这是文件数据绑定到表单的方法。 48 | 49 | 这里是一个普遍的方法,可能你会采用它来处理上传文件: 50 | 51 | ``` 52 | def handle_uploaded_file(f): 53 | with open('some/file/name.txt', 'wb+') as destination: 54 | for chunk in f.chunks(): 55 | destination.write(chunk) 56 | ``` 57 | 58 | 遍历`UploadedFile.chunks()`,而不是使用`read()`,能确保大文件并不会占用系统过多的内存。 59 | 60 | `UploadedFile `对象也拥有一些其他的方法和属性;完整参考请见UploadedFile。 61 | 62 | ### 使用模型处理上传文件 ### 63 | 64 | 如果你在`Model`上使用`FileField`保存文件,使用`ModelForm`可以让这个操作更加容易。调用`form.save()`的时候,文件对象会保存在相应的`FileField`的`upload_to`参数指定的地方。 65 | 66 | ``` 67 | from django.http import HttpResponseRedirect 68 | from django.shortcuts import render 69 | from .forms import ModelFormWithFileField 70 | 71 | def upload_file(request): 72 | if request.method == 'POST': 73 | form = ModelFormWithFileField(request.POST, request.FILES) 74 | if form.is_valid(): 75 | # file is saved 76 | form.save() 77 | return HttpResponseRedirect('/success/url/') 78 | else: 79 | form = ModelFormWithFileField() 80 | return render(request, 'upload.html', {'form': form}) 81 | ``` 82 | 83 | 如果你手动构造一个对象,你可以简单地把文件对象从`request.FILE`赋值给模型: 84 | 85 | ``` 86 | from django.http import HttpResponseRedirect 87 | from django.shortcuts import render 88 | from .forms import UploadFileForm 89 | from .models import ModelWithFileField 90 | 91 | def upload_file(request): 92 | if request.method == 'POST': 93 | form = UploadFileForm(request.POST, request.FILES) 94 | if form.is_valid(): 95 | instance = ModelWithFileField(file_field=request.FILES['file']) 96 | instance.save() 97 | return HttpResponseRedirect('/success/url/') 98 | else: 99 | form = UploadFileForm() 100 | return render(request, 'upload.html', {'form': form}) 101 | ``` 102 | 103 | ## 上传处理器 ## 104 | 105 | 当用户上传一个文件的时候,Django会把文件数据传递给上传处理器 – 一个小型的类,会在文件数据上传时处理它。上传处理器在`FILE_UPLOAD_HANDLERS`中定义,默认为: 106 | 107 | ``` 108 | ("django.core.files.uploadhandler.MemoryFileUploadHandler", 109 | "django.core.files.uploadhandler.TemporaryFileUploadHandler",) 110 | ``` 111 | 112 | `MemoryFileUploadHandler` 和`TemporaryFileUploadHandler`一起提供了Django的默认文件上传行为,将小文件读取到内存中,大文件放置在磁盘中。 113 | 114 | 你可以编写自定义的处理器,来定制Django如何处理文件。例如,你可以使用自定义处理器来限制用户级别的配额,在运行中压缩数据,渲染进度条,甚至是向另一个储存位置直接发送数据,而不把它存到本地。关于如何自定义或者完全替换处理器的行为,详见[编写自定义的上传处理器](http://python.usyiyi.cn/django/ref/files/uploads.html#custom-upload-handlers)。 115 | 116 | ### 上传数据在哪里储存 ### 117 | 118 | 在你保存上传文件之前,数据需要储存在某个地方。 119 | 120 | 通常,如果上传文件小于2.5MB,Django会把整个内容存到内存。这意味着,文件的保存仅仅涉及到从内存读取和写到磁盘,所以非常快。 121 | 122 | 但是,如果上传的文件很大,Django会把它写入一个临时文件,储存在你系统的临时目录中。在类Unix的平台下,你可以认为Django生成了一个文件,名称类似于`/tmp/tmpzfp6I6.upload`。如果上传的文件足够大,你可以观察到文件大小的增长,由于Django向磁盘写入数据。 123 | 124 | 这些特定值 – 2.5 MB,`/tmp`,以及其它 -- 都仅仅是"合理的默认值",它们可以自定义,这会在下一节中描述。 125 | 126 | ### 更改上传处理器的行为 ### 127 | 128 | Django的文件上传处理器的行为由一些设置控制。详见文件上传设置。 129 | 130 | ### 在运行中更改上传处理器 ### 131 | 132 | 有时候一些特定的视图需要不同的上传处理器。在这种情况下,你可以通过修改`request.upload_handlers`,为每个请求覆盖上传处理器。通常,这个列表会包含`FILE_UPLOAD_HANDLERS`提供的上传处理器,但是你可以把它修改为其它列表。 133 | 134 | 例如,假设你编写了`ProgressBarUploadHandler`,它会在上传过程中向某类AJAX控件提供反馈。你可以像这样将它添加到你的上传处理器中: 135 | 136 | ``` 137 | request.upload_handlers.insert(0, ProgressBarUploadHandler()) 138 | ``` 139 | 140 | 在这中情况下你可能想要使用`list.insert()`(而不是`append()`),因为进度条处理器需要在任何其他处理器 之前执行。要记住,多个上传处理器是按顺序执行的。 141 | 142 | 如果你想要完全替换上传处理器,你可以赋值一个新的列表: 143 | 144 | ``` 145 | request.upload_handlers = [ProgressBarUploadHandler()] 146 | ``` 147 | 148 | > 注意 149 | > 150 | > 你只可以在访问`request.POST`或者`request.FILES`之前修改上传处理器-- 在上传处理工作执行之后再修改上传处理就毫无意义了。如果你在读取`request.FILES`之后尝试修改`request.upload_handlers`,Django会抛出异常。 151 | > 152 | > 所以,你应该在你的视图中尽早修改上传处理器。 153 | > 154 | > `CsrfViewMiddleware` 也会访问`request.POST`,它是默认开启的。意思是你需要在你的视图中使用`csrf_exempt()`,来允许你修改上传处理器。接下来在真正处理请求的函数中,需要使用`csrf_protect()`。注意这意味着处理器可能会在CSRF验证完成之前开始接收上传文件。例如: 155 | 156 | ``` 157 | from django.views.decorators.csrf import csrf_exempt, csrf_protect 158 | 159 | @csrf_exempt 160 | def upload_file_view(request): 161 | request.upload_handlers.insert(0, ProgressBarUploadHandler()) 162 | return _upload_file_view(request) 163 | 164 | @csrf_protect 165 | def _upload_file_view(request): 166 | ... # Process request 167 | ``` 168 | 169 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Overview](https://docs.djangoproject.com/en/1.8/topics/http/file-uploads/)。 170 | > 171 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 172 | > 173 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 174 | -------------------------------------------------------------------------------- /3_3_2_File objects.md: -------------------------------------------------------------------------------- 1 | # File对象 # 2 | 3 | `django.core.files`模块及其子模块包含了一些用于基本文件处理的内建类。 4 | 5 | ## File类 ## 6 | 7 | `class File(file_object)` 8 | 9 | `File` 类是Python [file 对象](https://docs.python.org/3/glossary.html#term-file-object)的一个简单封装,并带有Django特定的附加功能。需要表示文件的时候,Django内部会使用这个类。 10 | 11 | File对象拥有下列属性和方法: 12 | 13 | `name` 14 | 15 | 含有`MEDIA_ROOT`相对路径的文件名称。 16 | 17 | `size` 18 | 19 | 文件的字节数。 20 | 21 | `file` 22 | 23 | 这个类所封装的,原生的[file 对象](https://docs.python.org/3/glossary.html#term-file-object)。 24 | 25 | `mode` 26 | 27 | 文件的读写模式。 28 | 29 | `open([mode=None])` 30 | 31 | 打开或者重新打开文件(同时会执行`File.seek(0)`)。 `mode`参数的值和Python内建的`open()`相同。 32 | 33 | 重新打开一个文件时,无论文件原先以什么模式打开,`mode`都会覆盖;`None`的意思是以原先的模式重新打开。 34 | 35 | `read([num_bytes=None])` 36 | 37 | 读取文件内容。可选的`size`参数是要读的字节数;没有指定的话,文件会一直读到结尾。 38 | 39 | `__iter__()` 40 | 41 | 迭代整个文件,并且每次生成一行。 42 | 43 | ``` 44 | Changed in Django 1.8: 45 | 46 | File现在使用[通用的换行符](https://www.python.org/dev/peps/pep-0278)。以下字符会识别为换行符:Unix换行符'\n',WIndows换行符'\r\n',以及Macintosh旧式换行符'\r'。 47 | ``` 48 | 49 | `chunks([chunk_size=None])` 50 | 51 | 迭代整个文件,并生成指定大小的一部分内容。`chunk_size`默认为64 KB。 52 | 53 | 处理大文件时这会非常有用,因为这样可以把他们从磁盘中读取出来,而避免将整个文件存到内存中。 54 | 55 | `multiple_chunks([chunk_size=None])` 56 | 57 | 如果文件足够大,需要按照提供的`chunk_size`切分成几个部分来访问到所有内容,则返回`True` 。 58 | 59 | `write([content])` 60 | 61 | 将指定的内容字符串写到文件。取决于底层的储存系统,写入的内容在调用`close()`之前可能不会完全提交。 62 | 63 | `close()` 64 | 65 | 关闭文件。 66 | 67 | 除了这些列出的方法,`File`暴露了 `file` 对象的以下属性和方法:`encoding`, `fileno`, `flush`, `isatty`, `newlines`, `read`, `readinto`, `readlines`, `seek`, `softspace`, `tell`, `truncate`, `writelines`, `xreadlines`。 68 | 69 | ## ContentFile类 ## 70 | 71 | `class ContentFile(File)[source]` 72 | 73 | `ContentFile`类继承自`File`,但是并不像`File`那样,它操作字符串的内容(也支持字节集),而不是一个实际的文件。例如: 74 | 75 | ``` 76 | from __future__ import unicode_literals 77 | from django.core.files.base import ContentFile 78 | 79 | f1 = ContentFile("esta sentencia está en español") 80 | f2 = ContentFile(b"these are bytes") 81 | ``` 82 | 83 | ## ImageFile类 ## 84 | 85 | `class ImageFile(file_object)[source]` 86 | 87 | Django特地为图像提供了这个内建类。`django.core.files.images.ImageFile`继承了 `File`的所有属性和方法,并且额外提供了以下的属性: 88 | 89 | `width` 90 | 91 | 图像的像素单位宽度。 92 | 93 | `height` 94 | 95 | 图像的像素单位高度。 96 | 97 | ## 附加到对象的文件的额外方法 ## 98 | 99 | 任何关联到一个对象(比如下面的`Car.photo`)的`File`都会有一些额外的方法: 100 | 101 | `File.save(name, content[, save=True])` 102 | 103 | 以提供的文件名和内容保存一个新文件。这样不会替换已存在的文件,但是会创建新的文件,并且更新对象来指向它。如果`save`为`True`,模型的`save()`方法会在文件保存之后调用。这就是说,下面两行: 104 | 105 | ``` 106 | >>> car.photo.save('myphoto.jpg', content, save=False) 107 | >>> car.save() 108 | ``` 109 | 110 | 等价于: 111 | 112 | ``` 113 | >>> car.photo.save('myphoto.jpg', content, save=True) 114 | ``` 115 | 116 | 要注意`content`参数必须是`File`或者 `File`的子类的实例,比如`ContentFile`。 117 | 118 | `File.delete([save=True])` 119 | 120 | 从模型实例中移除文件,并且删除内部的文件。如果`save`是`True`,模型的`save()` 方法会在文件删除之后调用。 121 | 122 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[File objects](https://docs.djangoproject.com/en/1.8/ref/files/file/)。 123 | > 124 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 125 | > 126 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 127 | -------------------------------------------------------------------------------- /3_3_3_Storage API.md: -------------------------------------------------------------------------------- 1 | # 文件储存API # 2 | 3 | ## 获取当前的储存类 ## 4 | 5 | Django提供了两个便捷的方法来获取当前的储存类: 6 | 7 | `class DefaultStorage[source]` 8 | 9 | `DefaultStorage` 提供对当前的默认储存系统的延迟访问,像`DEFAULT_FILE_STORAGE`中定义的那样。`DefaultStorage` 内部使用了`get_storage_class()`。 10 | 11 | `get_storage_class([import_path=None])[source]` 12 | 13 | 返回实现储存API的类或者模块。 14 | 15 | 当没有带着`import_path` 参数调用的时候, `get_storage_class` 会返回当前默认的储存系统,像`DEFAULT_FILE_STORAGE`中定义的那样。如果提供了`import_path`, `get_storage_class`会尝试从提供的路径导入类或者模块,并且如果成功的话返回它。如果导入不成功会抛出异常。 16 | 17 | ## FileSystemStorage类 ## 18 | 19 | `class FileSystemStorage([location=None, base_url=None, file_permissions_mode=None, directory_permissions_mode=None])[source]` 20 | 21 | `FileSystemStorage`类在本地文件系统上实现了基本的文件存储功能。它继承自`Storage` ,并且提供父类的所有公共方法的实现。 22 | 23 | `location` 24 | 25 | 储存文件的目录的绝对路径。默认为`MEDIA_ROOT`设置的值。 26 | 27 | `base_url` 28 | 29 | 在当前位置提供文件储存的URL。默认为`MEDIA_URL`设置的值。 30 | 31 | `file_permissions_mode` 32 | 33 | 文件系统的许可,当文件保存时会接收到它。默认为`FILE_UPLOAD_PERMISSIONS`。 34 | 35 | ``` 36 | New in Django 1.7: 37 | 38 | 新增了file_permissions_mode属性。之前,文件总是会接收到FILE_UPLOAD_PERMISSIONS许可。 39 | ``` 40 | 41 | `directory_permissions_mode` 42 | 43 | 文件系统的许可,当目录保存时会接收到它。默认为`FILE_UPLOAD_DIRECTORY_PERMISSIONS`。 44 | 45 | ``` 46 | New in Django 1.7: 47 | 48 | 新增了directory_permissions_mode属性。之前,目录总是会接收到FILE_UPLOAD_DIRECTORY_PERMISSIONS许可。 49 | ``` 50 | 51 | > 注意 52 | > 53 | > `FileSystemStorage.delete()`在提供的文件名称不存在的时候并不会抛出任何异常。 54 | 55 | ## Storage类 ## 56 | 57 | `class Storage[source]` 58 | 59 | `Storage`类为文件的存储提供了标准化的API,并带有一系列默认行为,所有其它的文件存储系统可以按需继承或者复写它们。 60 | 61 | > 注意 62 | > 63 | > 对于返回原生`datetime`对象的方法,所使用的有效时区为`os.environ['TZ']`的当前值。要注意它总是可以通过Django的`TIME_ZONE`来设置。 64 | 65 | `accessed_time(name)[source]` 66 | 67 | 返回包含文件的最后访问时间的原生`datetime`对象。对于不能够返回最后访问时间的储存系统,会抛出`NotImplementedError`异常。 68 | 69 | `created_time(name)[source]` 70 | 71 | 返回包含文件创建时间的原生`datetime`对象。对于不能够返回创建时间的储存系统,会抛出`NotImplementedError`异常。 72 | 73 | `delete(name)[source]` 74 | 75 | 删除`name`引用的文件。如果目标储存系统不支持删除操作,会抛出`NotImplementedError`异常。 76 | 77 | `exists(name)[source]` 78 | 79 | 如果提供的名称所引用的文件在文件系统中存在,则返回`True`,否则如果这个名称可用于新文件,返回`False`。 80 | 81 | `get_available_name(name, max_length=None)[source]` 82 | 83 | 返回基于`name`参数的文件名称,它在目标储存系统中可用于写入新的内容。 84 | 85 | 如果提供了`max_length`,文件名称长度不会超过它。如果不能找到可用的、唯一的文件名称,会抛出`SuspiciousFileOperation` 异常。 86 | 87 | 如果`name`命名的文件已存在,一个下划线加上随机7个数字或字母的字符串会添加到文件名称的末尾,扩展名之前。 88 | 89 | ``` 90 | Changed in Django 1.7: 91 | 92 | 之前,下划线和一位数字(比如"_1","_2",以及其他)会添加到文件名称的末尾,直到目标目录中发现了可用的名称。一些恶意的用户会利用这一确定性的算法来进行dos攻击。这一变化也在1.6.6, 1.5.9, 和 1.4.14中出现。 93 | ``` 94 | 95 | ``` 96 | Changed in Django 1.8: 97 | 98 | 新增了max_length参数。 99 | ``` 100 | 101 | `get_valid_name(name)[source]` 102 | 103 | 返回基于`name`参数的文件名称,它适用于目标储存系统。 104 | 105 | `listdir(path)[source]` 106 | 107 | 列出特定目录的所有内容,返回一个包含2元组的列表;第一个元素是目录,第二个是文件。对于不能够提供列表功能的储存系统,抛出`NotImplementedError`异常。 108 | 109 | `modified_time(name)[source]` 110 | 111 | 返回包含最后修改时间的原生`datetime`对象。对于不能够返回最后修改时间的储存系统,抛出`NotImplementedError`异常。 112 | 113 | `open(name, mode='rb')[source]` 114 | 115 | 通过提供的`name`打开文件。注意虽然返回的文件确保为`File`对象,但可能实际上是它的子类。在远程文件储存的情况下,这意味着读写操作会非常慢,所以警告一下。 116 | 117 | `path(name)[source]` 118 | 119 | 本地文件系统的路径,文件可以用Python标准的`open()`在里面打开。对于不能从本地文件系统访问的储存系统,抛出`NotImplementedError`异常。 120 | 121 | `save(name, content, max_length=None)[source]` 122 | 123 | 使用储存系统来保存一个新文件,最好带有特定的名称。如果名称为 `name`的文件已存在,储存系统会按需修改文件名称来获取一个唯一的名称。返回被储存文件的实际名称。 124 | 125 | max_length参数会传递给`get_available_name()`。 126 | 127 | `content`参数必须为`django.core.files.File`或者`File`子类的实例。 128 | 129 | ``` 130 | Changed in Django 1.8: 131 | 132 | 新增了max_length参数。 133 | ``` 134 | 135 | `size(name)[source]` 136 | 137 | 返回`name`所引用的文件的总大小,以字节为单位。对于不能够返回文件大小的储存系统,抛出`NotImplementedError`异常。 138 | 139 | `url(name)[source]` 140 | 141 | 返回URL,通过它可以访问到`name`所引用的文件。对于不支持通过URL访问的储存系统,抛出`NotImplementedError`异常。 142 | 143 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Storage API](https://docs.djangoproject.com/en/1.8/ref/files/storage/)。 144 | > 145 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 146 | > 147 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 148 | -------------------------------------------------------------------------------- /3_3_4_Managing files.md: -------------------------------------------------------------------------------- 1 | # 管理文件 # 2 | 3 | 这篇文档描述了Django为那些用户上传文件准备的文件访问API。底层的API足够通用,你可以使用为其它目的来使用它们。如果你想要处理静态文件(JS,CSS,以及其他),参见[管理静态文件(CSS和图像)](http://python.usyiyi.cn/django/howto/static-files/index.html)。 4 | 5 | 通常,Django使用`MEDIA_ROOT `和 `MEDIA_URL`设置在本地储存文件。下面的例子假设你使用这些默认值。 6 | 7 | 然而,Django提供了一些方法来编写自定义的 [文件储存系统](http://python.usyiyi.cn/django/topics/files.html#file-storage),允许你完全自定义Django在哪里以及如何储存文件。这篇文档的另一部分描述了这些储存系统如何工作。 8 | 9 | ## 在模型中使用文件 ## 10 | 11 | 当你使用`FileField` 或者 `ImageField`的时候,Django为你提供了一系列的API用来处理文件。 12 | 13 | 考虑下面的模型,它使用`ImageField`来储存一张照片: 14 | 15 | ``` 16 | from django.db import models 17 | 18 | class Car(models.Model): 19 | name = models.CharField(max_length=255) 20 | price = models.DecimalField(max_digits=5, decimal_places=2) 21 | photo = models.ImageField(upload_to='cars') 22 | ``` 23 | 24 | 任何`Car`的实例都有一个 `photo`字段,你可以通过它来获取附加图片的详细信息: 25 | 26 | ``` 27 | >>> car = Car.objects.get(name="57 Chevy") 28 | >>> car.photo 29 | 30 | >>> car.photo.name 31 | 'cars/chevy.jpg' 32 | >>> car.photo.path 33 | '/media/cars/chevy.jpg' 34 | >>> car.photo.url 35 | 'http://media.example.com/cars/chevy.jpg' 36 | ``` 37 | 38 | 例子中的`car.photo` 对象是一个`File` 对象,这意味着它拥有下面描述的所有方法和属性。 39 | 40 | > 注意 41 | > 42 | > 文件保存是数据库模型保存的一部分,所以磁盘上真实的文件名在模型保存之前并不可靠。 43 | 44 | 例如,你可以通过设置文件的 `name`属性为一个和文件储存位置 (`MEDIA_ROOT`,如果你使用默认的`FileSystemStorage`)相关的路径,来修改文件名称。 45 | 46 | ``` 47 | >>> import os 48 | >>> from django.conf import settings 49 | >>> initial_path = car.photo.path 50 | >>> car.photo.name = 'cars/chevy_ii.jpg' 51 | >>> new_path = settings.MEDIA_ROOT + car.photo.name 52 | >>> # Move the file on the filesystem 53 | >>> os.rename(initial_path, new_path) 54 | >>> car.save() 55 | >>> car.photo.path 56 | '/media/cars/chevy_ii.jpg' 57 | >>> car.photo.path == new_path 58 | True 59 | ``` 60 | 61 | ## File ## 62 | 63 | 当Django需要表示一个文件的时候,它在内部使用`django.core.files.File`实例。这个对象是 Python [内建文件对象](https://docs.python.org/library/stdtypes.html#bltin-file-objects)的一个简单封装,并带有一些Django特定的附加功能。 64 | 65 | 大多数情况你可以简单地使用Django提供给你的`File`对象(例如像上面那样把文件附加到模型,或者是上传的文件)。 66 | 67 | 如果你需要自行构造一个`File`对象,最简单的方法是使用Python内建的`file` 对象来创建一个: 68 | 69 | ``` 70 | >>> from django.core.files import File 71 | 72 | # Create a Python file object using open() 73 | >>> f = open('/tmp/hello.world', 'w') 74 | >>> myfile = File(f) 75 | ``` 76 | 77 | 现在你可以使用 `File`类的任何文档中记录的属性和方法了。 78 | 79 | 注意这种方法创建的文件并不会自动关闭。以下步骤可以用于自动关闭文件: 80 | 81 | ``` 82 | >>> from django.core.files import File 83 | 84 | # Create a Python file object using open() and the with statement 85 | >>> with open('/tmp/hello.world', 'w') as f: 86 | ... myfile = File(f) 87 | ... myfile.write('Hello World') 88 | ... 89 | >>> myfile.closed 90 | True 91 | >>> f.closed 92 | True 93 | ``` 94 | 95 | 在处理大量对象的循环中访问文件字段时,关闭文件极其重要。如果文件在访问之后没有手动关闭,会有消耗完文件描述符的风险。这可能导致如下错误: 96 | 97 | ``` 98 | IOError: [Errno 24] Too many open files 99 | ``` 100 | 101 | ## 文件储存 ## 102 | 103 | 在背后,Django需要决定在哪里以及如何将文件储存到文件系统。这是一个对象,它实际上理解一些东西,比如文件系统,打开和读取文件,以及其他。 104 | 105 | Django的默认文件储存由`DEFAULT_FILE_STORAGE`设置提供。如果你没有显式提供一个储存系统,就会使用它。 106 | 107 | 关于内建的默认文件储存系统的细节,请参见下面一节。另外,关于编写你自己的文件储存系统的一些信息,请见[编写自定义的文件系统](http://python.usyiyi.cn/django/howto/custom-file-storage.html)。 108 | 109 | ### 储存对象 ### 110 | 111 | 大多数情况你可能并不想使用`File`对象(它向文件提供适当的存储功能),你可以直接使用文件储存系统。你可以创建一些自定义文件储存类的实例,或者 – 大多数情况更加有用的 – 你可以使用全局的默认储存系统: 112 | 113 | ``` 114 | >>> from django.core.files.storage import default_storage 115 | >>> from django.core.files.base import ContentFile 116 | 117 | >>> path = default_storage.save('/path/to/file', ContentFile('new content')) 118 | >>> path 119 | '/path/to/file' 120 | 121 | >>> default_storage.size(path) 122 | 11 123 | >>> default_storage.open(path).read() 124 | 'new content' 125 | 126 | >>> default_storage.delete(path) 127 | >>> default_storage.exists(path) 128 | False 129 | ``` 130 | 131 | 关于文件储存API,参见 [文件储存API](http://python.usyiyi.cn/django/ref/files/storage.html)。 132 | 133 | ### 内建的文件系统储存类 ### 134 | 135 | Django自带了`django.core.files.storage.FileSystemStorage` 类,它实现了基本的本地文件系统中的文件储存。 136 | 137 | 例如,下面的代码会在 `/media/photos` 目录下储存上传的文件,无论`MEDIA_ROOT`设置是什么: 138 | 139 | ``` 140 | from django.db import models 141 | from django.core.files.storage import FileSystemStorage 142 | 143 | fs = FileSystemStorage(location='/media/photos') 144 | 145 | class Car(models.Model): 146 | ... 147 | photo = models.ImageField(storage=fs) 148 | ``` 149 | 150 | [自定义储存系统](http://python.usyiyi.cn/django/howto/custom-file-storage.html) 以相同方式工作:你可以把它们作为`storage`参数传递给`FileField`。 151 | 152 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Managing files](https://docs.djangoproject.com/en/1.8/topics/files/)。 153 | > 154 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 155 | > 156 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 157 | -------------------------------------------------------------------------------- /3_3_5_Custom storage.md: -------------------------------------------------------------------------------- 1 | # 编写自定义存储系统 # 2 | 3 | 如果你需要提供自定义文件存储 – 一个普遍的例子是在某个远程系统上储存文件 – 你可以通过定义一个自定义的储存类来实现。你需要遵循以下步骤: 4 | 5 | 1\. 你的自定义储存类必须是`django.core.files.storage.Storage`的子类: 6 | 7 | ``` 8 | from django.core.files.storage import Storage 9 | 10 | class MyStorage(Storage): 11 | ... 12 | ``` 13 | 14 | 2\. Django必须能够不带任何参数来实例化你的储存类。这意味着任何设置都应该从`django.conf.settings`中获取。 15 | 16 | ``` 17 | from django.conf import settings 18 | from django.core.files.storage import Storage 19 | 20 | class MyStorage(Storage): 21 | def __init__(self, option=None): 22 | if not option: 23 | option = settings.CUSTOM_STORAGE_OPTIONS 24 | ... 25 | ``` 26 | 27 | 3\. 你的储存类必须实现 `_open()` 和 `_save()`方法,以及任何适合于你的储存类的其它方法。更多这类方法请见下文。 28 | 29 | 另外,如果你的类提供本地文件存储,它必须覆写`path()`方法。 30 | 31 | 4\. 你的储存类必须是 可以析构的,所以它在迁移中的一个字段上使用的时候可以被序列化。只要你的字段拥有自己可以[序列化](http://python.usyiyi.cn/django/topics/migrations.html#migration-serializing)的参数,你就可以为它使用`django.utils.deconstruct.deconstructible`类装饰器(这也是Django用在`FileSystemStorage`上的东西)。 32 | 33 | 默认情况下,下面的方法会抛出`NotImplementedError`异常,并且必须覆写它们。 34 | 35 | + [Storage.delete()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.delete) 36 | + [Storage.exists()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.exists) 37 | + [Storage.listdir()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.listdir) 38 | + [Storage.size()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.size) 39 | + [Storage.url()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.url) 40 | 41 | 然而要注意,并不是这些方法全部都需要,可以故意省略一些。可以不必实现每个方法而仍然能拥有一个可以工作的储存类。 42 | 43 | 比如,如果在特定的储存后端中,列出内容的开销比较大,你可以决定不实现`Storage.listdir`。 44 | 45 | 另一个例子是只处理写入文件的后端。这种情况下,你不需要实现上面的任意一种方法。 46 | 47 | 根本上来说,需要实现哪种方法取决于你。如果不去实现一些方法,你会得到一个不完整(可能是不能用的)的接口。 48 | 49 | 你也会经常想要使用特意为自定义储存对象设计的钩子。它们是: 50 | 51 | `_open(name, mode='rb')` 52 | 53 | 必需的。 54 | 55 | 被`Storage.open()`调用,这是储存类用于打开文件的实际工具。它必须返回`File`对象,在大多数情况下,你会想要返回一些子类,它们实现了后端储存系统特定的逻辑。 56 | 57 | `_save(name, content)` 58 | 59 | 被`Storage.save()`调用。`name`必须事先通过`get_valid_name()` 和 `get_available_name()`过滤,并且`content`自己必须是一个`File`对象。 60 | 61 | 应该返回被保存文件的真实名称(通常是传进来的`name`,但是如果储存需要修改文件名称,则返回新的名称来代替)。 62 | 63 | `get_valid_name(name)` 64 | 65 | 返回适用于当前储存系统的文件名。传递给该方法的`name`参数是发送给服务器的原始文件名称,并移除了所有目录信息。你可以覆写这个方法,来自定义非标准的字符将会如何转换为安全的文件名称。 66 | 67 | `Storage`提供的代码只会保留原始文件名中的数字和字母字符、英文句号和下划线,并移除其它字符。 68 | 69 | `get_available_name(name, max_length=None)` 70 | 71 | 返回在储存系统中可用的文件名称,可能会顾及到提供的文件名称。传给这个方法的name参数需要事先过滤为储存系统有效的文件名称,根据上面描述的`get_valid_name()` 方法。 72 | 73 | 如果提供了`max_length`,文件名称长度不会超过它。如果不能找到可用的、唯一的文件名称,会抛出`SuspiciousFileOperation` 异常。 74 | 75 | 如果`name`命名的文件已存在,一个下划线加上随机7个数字或字母的字符串会添加到文件名称的末尾,扩展名之前。 76 | 77 | ``` 78 | Changed in Django 1.7: 79 | 80 | 之前,下划线和一位数字(比如"_1", "_2",以及其他)会添加到文件名称的末尾,直到目标目录中发现了可用的名称。一些恶意的用户会利用这一确定性的算法来进行dos攻击。 这一变化也在1.6.6, 1.5.9, 和 1.4.14中出现。 81 | ``` 82 | 83 | ``` 84 | Changed in Django 1.8: 85 | 86 | 新增了max_length参数。 87 | ``` 88 | 89 | [自定义储存系统](http://python.usyiyi.cn/django/howto/custom-file-storage.html) 以相同方式工作:你可以把它们作为`storage`参数传递给`FileField`。 90 | 91 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Custom storage](https://docs.djangoproject.com/en/1.8/howto/custom-file-storage/)。 92 | > 93 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 94 | > 95 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 96 | -------------------------------------------------------------------------------- /3_4.md: -------------------------------------------------------------------------------- 1 | # 基于类的视图 -------------------------------------------------------------------------------- /3_4_1_Overview.md: -------------------------------------------------------------------------------- 1 | # 基于类的视图 # 2 | 3 | 视图是一个可调用对象,它接收一个请求然后返回一个响应。这个可调用对象可以不只是函数,Django 提供一些可以用作视图的类。它们允许你结构化你的视图并且利用继承和混合重用代码。后面我们将介绍一些用于简单任务的通用视图,但你可能想要设计自己的可重用视图的结构以适合你的使用场景。完整的细节,请参见基于类的视图的参考文档。 4 | 5 | + [基于类的视图简介](http://python.usyiyi.cn/django/topics/class-based-views/intro.html) 6 | + [内建的基于类的通用视图](http://python.usyiyi.cn/django/topics/class-based-views/generic-display.html) 7 | + [使用基于类的视图处理表单](http://python.usyiyi.cn/django/topics/class-based-views/generic-editing.html) 8 | + [使用混合来扩展视图类](http://python.usyiyi.cn/django/topics/class-based-views/mixins.html) 9 | 10 | ## 基本的示例 ## 11 | 12 | Django 提供基本的视图类,它们适用于广泛的应用。所有的视图类继承自`View`类,它负责连接视图到URL、HTTP 方法调度和其它简单的功能。`RedirectView`用于简单的HTTP 重定向,`TemplateView`扩展基类来渲染模板。 13 | 14 | ## 在URLconf 中的简单用法 ## 15 | 16 | 使用通用视图最简单的方法是在URLconf 中创建它们。如果你只是修改基于类的视图的一些简单属性,你可以将它们直接传递给`as_view()`方法调用: 17 | 18 | ``` 19 | from django.conf.urls import url 20 | from django.views.generic import TemplateView 21 | 22 | urlpatterns = [ 23 | url(r'^about/', TemplateView.as_view(template_name="about.html")), 24 | ] 25 | ``` 26 | 27 | 传递给`as_view()`的参数将覆盖类中的属性。在这个例子中,我们设置`TemplateView`的`template_name`。可以使用类似的方法覆盖`RedirectView`的`url`属性。 28 | 29 | ## 子类化通用视图 ## 30 | 31 | 第二种,功能更强一点的使用通用视图的方式是继承一个已经存在的视图并在子类中覆盖其属性(例如`template_name`)或方法(例如`get_context_data`)以提供新的值或方法。例如,考虑只显示一个模板`about.html`的视图。Django 有一个通用视图`TemplateView`来做这件事,所以我们可以简单地子类化它,并覆盖模板的名称: 32 | 33 | ``` 34 | # some_app/views.py 35 | from django.views.generic import TemplateView 36 | 37 | class AboutView(TemplateView): 38 | template_name = "about.html" 39 | ``` 40 | 41 | 然后我们只需要添加这个新的视图到我们的URLconf 中。`TemplateView`是一个类不是一个函数,所以我们将URL 指向类的`as_view()`方法,它让基于类的视图提供一个类似函数的入口: 42 | 43 | ``` 44 | # urls.py 45 | from django.conf.urls import url 46 | from some_app.views import AboutView 47 | 48 | urlpatterns = [ 49 | url(r'^about/', AboutView.as_view()), 50 | ] 51 | ``` 52 | 53 | 关于如何使用内建的通用视图的更多信息,参考下一主题通用的基于类的视图。 54 | 55 | ## 支持其它HTTP 方法 ## 56 | 57 | 假设有人想通过HTTP 访问我们的书库,它使用视图作为API。这个API 客户端将随时连接并下载自上次访问以来新出版的书籍的数据。如果没有新的书籍,仍然从数据库中获取书籍、渲染一个完整的响应并发送给客户端将是对CPU 和带宽的浪费。如果有个API 用于查询书籍最新发布的时间将会更好。 58 | 59 | 我们在URLconf 中映射URL 到书籍列表视图: 60 | 61 | ``` 62 | from django.conf.urls import url 63 | from books.views import BookListView 64 | 65 | urlpatterns = [ 66 | url(r'^books/$', BookListView.as_view()), 67 | ] 68 | ``` 69 | 70 | 下面是这个视图: 71 | 72 | ``` 73 | from django.http import HttpResponse 74 | from django.views.generic import ListView 75 | from books.models import Book 76 | 77 | class BookListView(ListView): 78 | model = Book 79 | 80 | def head(self, *args, **kwargs): 81 | last_book = self.get_queryset().latest('publication_date') 82 | response = HttpResponse('') 83 | # RFC 1123 date format 84 | response['Last-Modified'] = last_book.publication_date.strftime('%a, %d %b %Y %H:%M:%S GMT') 85 | return response 86 | ``` 87 | 88 | 如果该视图从GET 请求访问,将在响应中返回一个普通而简单的对象列表(使用`book_list.html`模板)。但如果客户端发出一个`HEAD`请求,响应将具有一个空的响应体而`Last-Modified`头部会指示最新发布的书籍的时间。基于这个信息,客户端可以下载或不下载完整的对象列表。 89 | 90 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Overview](https://docs.djangoproject.com/en/1.8/topics/class-based-views/)。 91 | > 92 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 93 | > 94 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 95 | -------------------------------------------------------------------------------- /3_4_3_Built-in editing views.md: -------------------------------------------------------------------------------- 1 | # 使用基于类的视图处理表单 # 2 | 3 | 表单的处理通常有3 个步骤: 4 | 5 | + 初始的的GET (空白或预填充的表单) 6 | + 带有非法数据的POST(通常重新显示表单和错误信息) 7 | + 带有合法数据的POST(处理数据并重定向) 8 | 9 | 你自己实现这些功能经常导致许多重复的样本代码(参见在视图中使用表单)。为了避免这点,Django 提供一系列的通用的基于类的视图用于表单的处理。 10 | 11 | ## 基本的表单 ## 12 | 13 | 根据一个简单的联系人表单: 14 | 15 | ``` 16 | #forms.py 17 | 18 | from django import forms 19 | 20 | class ContactForm(forms.Form): 21 | name = forms.CharField() 22 | message = forms.CharField(widget=forms.Textarea) 23 | 24 | def send_email(self): 25 | # send email using the self.cleaned_data dictionary 26 | pass 27 | ``` 28 | 29 | 可以使用`FormView`来构造其视图: 30 | 31 | ``` 32 | #views.py 33 | 34 | from myapp.forms import ContactForm 35 | from django.views.generic.edit import FormView 36 | 37 | class ContactView(FormView): 38 | template_name = 'contact.html' 39 | form_class = ContactForm 40 | success_url = '/thanks/' 41 | 42 | def form_valid(self, form): 43 | # This method is called when valid form data has been POSTed. 44 | # It should return an HttpResponse. 45 | form.send_email() 46 | return super(ContactView, self).form_valid(form) 47 | ``` 48 | 49 | 注: 50 | 51 | + `FormView`继承`TemplateResponseMixin`所以这里可以使用`template_name`。 52 | + `form_valid()`的默认实现只是简单地重定向到`success_url`。 53 | 54 | ## 模型的表单 ## 55 | 56 | 通用视图在于模型一起工作时会真正光芒四射。这些通用的视图将自动创建一个ModelForm,只要它们能知道使用哪一个模型类: 57 | 58 | + 如果给出`model`属性,则使用该模型类。 59 | + 如果`get_object()` 返回一个对象,则使用该对象的类。 60 | + 如果给出`queryset`,则使用该查询集的模型。 61 | 62 | 模型表单提供一个`form_valid()` 的实现,它自动保存模型。如果你有特殊的需求,可以覆盖它;参见下面的例子。 63 | 64 | 你甚至不需要为`CreateView` 和`UpdateView`提供`success_url` —— 如果存在它们将使用模型对象的`get_absolute_url()`。 65 | 66 | 如果你想使用一个自定义的`ModelForm`(例如添加额外的验证),只需简单地在你的视图上设置`form_class`。 67 | 68 | > 注 69 | > 70 | > 71 | 当指定一个自定义的表单类时,你必须指定模型,即使`form_class` 可能是一个`ModelForm`。 72 | 73 | 首先我们需要添加`get_absolute_url()` 到我们的`Author` 类中: 74 | 75 | ``` 76 | #models.py 77 | 78 | from django.core.urlresolvers import reverse 79 | from django.db import models 80 | 81 | class Author(models.Model): 82 | name = models.CharField(max_length=200) 83 | 84 | def get_absolute_url(self): 85 | return reverse('author-detail', kwargs={'pk': self.pk}) 86 | ``` 87 | 88 | 然后我们可以使用`CreateView` 机器伙伴来做实际的工作。注意这里我们是如何配置通用的基于类的视图的;我们自己没有写任何逻辑: 89 | 90 | ``` 91 | #views.py 92 | 93 | from django.views.generic.edit import CreateView, UpdateView, DeleteView 94 | from django.core.urlresolvers import reverse_lazy 95 | from myapp.models import Author 96 | 97 | class AuthorCreate(CreateView): 98 | model = Author 99 | fields = ['name'] 100 | 101 | class AuthorUpdate(UpdateView): 102 | model = Author 103 | fields = ['name'] 104 | 105 | class AuthorDelete(DeleteView): 106 | model = Author 107 | success_url = reverse_lazy('author-list') 108 | ``` 109 | 110 | > 注 111 | > 112 | > 这里我们必须使用`reverse_lazy()` 而不是`reverse`,因为在该文件导入时URL 还没有加载。 113 | 114 | `fields` 属性的工作方式与`ModelForm` 的内部`Meta`类的`fields` 属性相同。除非你用另外一种方式定义表单类,该属性是必须的,如果没有将引发一个`ImproperlyConfigured` 异常。 115 | 116 | 如果你同时指定`fields` 和`form_class` 属性,将引发一个`ImproperlyConfigured` 异常。 117 | 118 | ``` 119 | Changed in Django 1.8: 120 | 121 | 省略fields 属性在以前是允许的,但是导致表单带有模型的所有字段。 122 | ``` 123 | 124 | ``` 125 | Changed in Django 1.8: 126 | 127 | 以前,如果fields 和form_class 两个都指定,会默默地忽略 fields。 128 | ``` 129 | 130 | 最后,我我们来将这些新的视图放到URLconf 中: 131 | 132 | ``` 133 | #urls.py 134 | 135 | from django.conf.urls import url 136 | from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete 137 | 138 | urlpatterns = [ 139 | # ... 140 | url(r'author/add/$', AuthorCreate.as_view(), name='author_add'), 141 | url(r'author/(?P[0-9]+)/$', AuthorUpdate.as_view(), name='author_update'), 142 | url(r'author/(?P[0-9]+)/delete/$', AuthorDelete.as_view(), name='author_delete'), 143 | ] 144 | ``` 145 | 146 | > 注 147 | > 148 | > 这些表单继承`SingleObjectTemplateResponseMixin`,它使用`template_name_suffix `并基于模型来构造`template_name`。 149 | > 150 | > 在这个例子中: 151 | > 152 | > + `CreateView` 和`UpdateView` 使用 `myapp/author_form.html` 153 | > + `DeleteView` 使用 `myapp/author_confirm_delete.html` 154 | > 155 | > 如果你希望分开`CreateView` 和`UpdateView` 的模板,你可以设置你的视图类的`template_name` 或`template_name_suffix`。 156 | 157 | ## 模型和request.user ## 158 | 159 | 为了跟踪使用CreateView 创建一个对象的用户,你可以使用一个自定义的ModelForm 来实现这点。首先,向模型添加外键关联: 160 | 161 | ``` 162 | #models.py 163 | 164 | from django.contrib.auth.models import User 165 | from django.db import models 166 | 167 | class Author(models.Model): 168 | name = models.CharField(max_length=200) 169 | created_by = models.ForeignKey(User) 170 | 171 | # ... 172 | ``` 173 | 174 | 在这个视图中,请确保你没有将`created_by` 包含进要编辑的字段列表,并覆盖`form_valid()` 来添加这个用户: 175 | 176 | ``` 177 | #views.py 178 | 179 | from django.views.generic.edit import CreateView 180 | from myapp.models import Author 181 | 182 | class AuthorCreate(CreateView): 183 | model = Author 184 | fields = ['name'] 185 | 186 | def form_valid(self, form): 187 | form.instance.created_by = self.request.user 188 | return super(AuthorCreate, self).form_valid(form) 189 | ``` 190 | 191 | 注意,你需要使用`login_required()` 来装饰这个视图,或者在`form_valid()` 中处理未认证的用户。 192 | 193 | ## AJAX 示例 ## 194 | 195 | 下面是一个简单的实例,展示你可以如何实现一个表单,使它可以同时为AJAX 请求和‘普通的’表单POST 工作: 196 | 197 | ``` 198 | from django.http import JsonResponse 199 | from django.views.generic.edit import CreateView 200 | from myapp.models import Author 201 | 202 | class AjaxableResponseMixin(object): 203 | """ 204 | Mixin to add AJAX support to a form. 205 | Must be used with an object-based FormView (e.g. CreateView) 206 | """ 207 | def form_invalid(self, form): 208 | response = super(AjaxableResponseMixin, self).form_invalid(form) 209 | if self.request.is_ajax(): 210 | return JsonResponse(form.errors, status=400) 211 | else: 212 | return response 213 | 214 | def form_valid(self, form): 215 | # We make sure to call the parent's form_valid() method because 216 | # it might do some processing (in the case of CreateView, it will 217 | # call form.save() for example). 218 | response = super(AjaxableResponseMixin, self).form_valid(form) 219 | if self.request.is_ajax(): 220 | data = { 221 | 'pk': self.object.pk, 222 | } 223 | return JsonResponse(data) 224 | else: 225 | return response 226 | 227 | class AuthorCreate(AjaxableResponseMixin, CreateView): 228 | model = Author 229 | fields = ['name'] 230 | ``` 231 | 232 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Built-in editing views](https://docs.djangoproject.com/en/1.8/topics/class-based-views/generic-editing/)。 233 | > 234 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 235 | > 236 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 237 | -------------------------------------------------------------------------------- /3_5.md: -------------------------------------------------------------------------------- 1 | # 高级 -------------------------------------------------------------------------------- /3_5_1_Generating CSV.md: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | 3 | # 使用Django输出CSV # 4 | 这篇文档阐述了如何通过使用Django视图动态输出CSV (Comma Separated Values)。 你可以使用Python CSV 库或者Django的模板系统来达到目的。 5 | 6 | ## 使用Python CSV库 ## 7 | 8 | Python自带了CSV库,`csv`。在Django中使用它的关键是,`csv`模块的CSV创建功能作用于类似于文件的对象,并且Django的`HttpResponse`对象就是类似于文件的对象。 9 | 10 | 这里是个例子: 11 | 12 | ``` 13 | import csv 14 | from django.http import HttpResponse 15 | 16 | def some_view(request): 17 | # Create the HttpResponse object with the appropriate CSV header. 18 | response = HttpResponse(content_type='text/csv') 19 | response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' 20 | 21 | writer = csv.writer(response) 22 | writer.writerow(['First row', 'Foo', 'Bar', 'Baz']) 23 | writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"]) 24 | 25 | return response 26 | ``` 27 | 28 | 代码和注释是不用多说的,但是一些事情需要提醒一下: 29 | 30 | + 响应对象获得了一个特殊的MIME类型,`text/csv`。这会告诉浏览器,文档是个CSV文件而不是HTML文件。如果你把它去掉,浏览器可能会把输出解释为HTML,会在浏览器窗口中显示一篇丑陋的、可怕的官样文章。 31 | + 响应对象获取了附加的`Content-Disposition`协议头,它含有CSV文件的名称。文件名可以是任意的;你想把它叫做什么都可以。浏览器会在”另存为“对话框中使用它,或者其它。 32 | + 钩住CSV生成API非常简单:只需要把`response`作为第一个参数传递给`csv.writer`。`csv.writer` 函数接受一个类似于文件的对象,而`HttpResponse` 对象正好合适。 33 | + 对于你CSV文件的每一行,调用`writer.writerow`,向它传递一个可迭代的对象比如列表或者元组。 34 | + CSV模板会为你处理引用,所以你不用担心没有转义字符串中的引号或者逗号。只需要向`writerow()`传递你的原始字符串,它就会执行正确的操作。 35 | 36 | > 在Python 2中处理Unicode 37 | > 38 | > Python2的`csv`模块不支持Unicode输入。由于Django在内部使用Unicode,这意味着从一些来源比如`HttpRequest`读出来的字符串可能导致潜在的问题。有一些选项用于处理它: 39 | > 40 | > + 手动将所有Unicode对象编码为兼容的编码。 41 | > + 使用[csv模块示例章节](https://docs.python.org/library/csv.html#examples)中提供的`UnicodeWriter`类。 42 | > + 使用[python-unicodecsv 模块](https://github.com/jdunck/python-unicodecsv),它作为`csv`模块随时可用的替代方案,能够优雅地处理Unicode。 43 | > 44 | > 更多信息请见`csv`模块的Python文档。 45 | 46 | ### 流式传输大尺寸CSV文件 ### 47 | 48 | 当处理生成大尺寸响应的视图时,你可能想要使用Django的`StreamingHttpResponse`类。例如,通过流式传输需要长时间来生成的文件,可以避免负载均衡器在服务器生成响应的时候断掉连接。 49 | 50 | 在这个例子中,我们利用Python的生成器来有效处理大尺寸CSV文件的拼接和传输: 51 | 52 | ``` 53 | import csv 54 | 55 | from django.utils.six.moves import range 56 | from django.http import StreamingHttpResponse 57 | 58 | class Echo(object): 59 | """An object that implements just the write method of the file-like 60 | interface. 61 | """ 62 | def write(self, value): 63 | """Write the value by returning it, instead of storing in a buffer.""" 64 | return value 65 | 66 | def some_streaming_csv_view(request): 67 | """A view that streams a large CSV file.""" 68 | # Generate a sequence of rows. The range is based on the maximum number of 69 | # rows that can be handled by a single sheet in most spreadsheet 70 | # applications. 71 | rows = (["Row {}".format(idx), str(idx)] for idx in range(65536)) 72 | pseudo_buffer = Echo() 73 | writer = csv.writer(pseudo_buffer) 74 | response = StreamingHttpResponse((writer.writerow(row) for row in rows), 75 | content_type="text/csv") 76 | response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' 77 | return response 78 | ``` 79 | 80 | ## 使用模板系统 ## 81 | 82 | 或者,你可以使用[Django模板系统](http://python.usyiyi.cn/django/topics/templates.html)来生成CSV。比起便捷的Python `csv`模板来说,这样比较低级,但是为了完整性,这个解决方案还是在这里展示一下。 83 | 84 | 它的想法是,传递一个项目的列表给你的模板,并且让模板在`for`循环中输出逗号。 85 | 86 | 这里是一个例子,它像上面一样生成相同的CSV文件: 87 | 88 | ``` 89 | from django.http import HttpResponse 90 | from django.template import loader, Context 91 | 92 | def some_view(request): 93 | # Create the HttpResponse object with the appropriate CSV header. 94 | response = HttpResponse(content_type='text/csv') 95 | response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' 96 | 97 | # The data is hard-coded here, but you could load it from a database or 98 | # some other source. 99 | csv_data = ( 100 | ('First row', 'Foo', 'Bar', 'Baz'), 101 | ('Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"), 102 | ) 103 | 104 | t = loader.get_template('my_template_name.txt') 105 | c = Context({ 106 | 'data': csv_data, 107 | }) 108 | response.write(t.render(c)) 109 | return response 110 | ``` 111 | 112 | 这个例子和上一个例子之间唯一的不同就是,这个例子使用模板来加载,而不是CSV模块。代码的结果 -- 比如`content_type='text/csv'` -- 都是相同的。 113 | 114 | 然后,创建模板`my_template_name.txt`,带有以下模板代码: 115 | 116 | ``` 117 | {% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}" 118 | {% endfor %} 119 | ``` 120 | 121 | 这个模板十分基础。它仅仅遍历了提供的数据,并且对于每一行都展示了一行CSV。它使用了`addslashes`模板过滤器来确保没有任何引用上的问题。 122 | 123 | ## 其它基于文本的格式 ## 124 | 125 | 要注意对于 CSV来说,这里并没有什么特别之处 -- 只是特定了输出格式。你可以使用这些技巧中的任何一个,来输出任何你想要的,基于文本的格式。你也可以使用相似的技巧来生成任意的二进制数据。例子请参见[在Django中输出PDF](http://python.usyiyi.cn/django/howto/outputting-pdf.html)。 126 | 127 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Generating CSV](https://docs.djangoproject.com/en/1.8/howto/outputting-csv/)。 128 | > 129 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 130 | > 131 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 132 | 133 | {% endraw %} 134 | -------------------------------------------------------------------------------- /3_5_2_Generating PDF.md: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | 3 | # 使用Django输出PDF # 4 | 5 | 这篇文档阐述了如何通过使用Django视图动态输出PDF。这可以通过一个出色的、开源的Python PDF库[ReportLab](http://www.reportlab.com/opensource/)来实现。 6 | 7 | 动态生成PDF文件的优点是,你可以为不同目的创建自定义的PDF -- 这就是说,为不同的用户或者不同的内容。 8 | 9 | 例如,Django在[kusports.com](http://www.kusports.com/)上用来为那些参加March Madness比赛的人,生成自定义的,便于打印的 NCAA 锦标赛晋级表作为PDF文件。 10 | 11 | ## 安装ReportLab ## 12 | 13 | ReportLab库在[PyPI](https://pypi.python.org/pypi/reportlab)上提供。也可以下载到[用户指南](http://www.reportlab.com/docs/reportlab-userguide.pdf) (PDF文件,不是巧合)。 你可以使用`pip`来安装ReportLab: 14 | 15 | ``` 16 | $ pip install reportlab 17 | ``` 18 | 19 | 通过在Python交互解释器中导入它来测试你的安装: 20 | 21 | ``` 22 | >>> import reportlab 23 | ``` 24 | 25 | 若没有抛出任何错误,则已安装成功。 26 | 27 | ## 编写你的视图 ## 28 | 29 | 使用Django动态生成PDF的关键是,ReportLab API作用于类似于文件的对象,并且Django的 `HttpResponse`对象就是类似于文件的对象。 30 | 31 | 这里是一个 “Hello World”的例子: 32 | 33 | ``` 34 | from reportlab.pdfgen import canvas 35 | from django.http import HttpResponse 36 | 37 | def some_view(request): 38 | # Create the HttpResponse object with the appropriate PDF headers. 39 | response = HttpResponse(content_type='application/pdf') 40 | response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"' 41 | 42 | # Create the PDF object, using the response object as its "file." 43 | p = canvas.Canvas(response) 44 | 45 | # Draw things on the PDF. Here's where the PDF generation happens. 46 | # See the ReportLab documentation for the full list of functionality. 47 | p.drawString(100, 100, "Hello world.") 48 | 49 | # Close the PDF object cleanly, and we're done. 50 | p.showPage() 51 | p.save() 52 | return response 53 | ``` 54 | 55 | 代码和注释是不用多说的,但是一些事情需要提醒一下: 56 | 57 | + 响应对象获得了一个特殊的MIME类型, `application/pdf`。这会告诉浏览器,文档是个PDF文件而不是HTML文件。 如果你把它去掉,浏览器可能会把输出解释为HTML,会在浏览器窗口中显示一篇丑陋的、可怕的官样文章。 58 | + 响应对象获取了附加的`Content-Disposition`协议头,它含有PDF文件的名称。 文件名可以是任意的;你想把它叫做什么都可以。浏览器会在”另存为“对话框中使用它,或者其它。 59 | + 在这个例子中,`Content-Disposition` 协议头以 `'attachment;'` 开头。 这样就强制让浏览器弹出对话框来提示或者确认,如果机器上设置了默认值要如何处理文档。如果你去掉了`'attachment;'`,无论什么程序或控件被设置为用于处理PDF,浏览器都会使用它。代码就像这样: 60 | 61 | ``` 62 | response['Content-Disposition'] = 'filename="somefilename.pdf"' 63 | ``` 64 | 65 | + 钩住ReportLab API 非常简单:只需要向`canvas.Canvas`传递`response`作为第一个参数。`Canvas`函数接受一个类似于文件的对象,而 `HttpResponse`对象正好合适。 66 | + 注意所有随后的PDF生成方法都在PDF对象(这个例子是p)上调用,而不是`response`对象上。 67 | + 最后,在PDF文件上调用`showPage()` 和 `save()`非常重要。 68 | 69 | > 注意 70 | > 71 | > ReportLab并不是线程安全的。一些用户报告了一些奇怪的问题,在构建生成PDF的Django视图时出现,这些视图在同一时间被很多人访问。 72 | 73 | ## 复杂的PDF ## 74 | 75 | 如果你使用ReportLab创建复杂的PDF文档,考虑使用`io`库作为你PDF文件的临时保存地点。这个库提供了一个类似于文件的对象接口,非常实用。这个是上面的“Hello World”示例采用 `io`重写后的样子: 76 | 77 | ``` 78 | from io import BytesIO 79 | from reportlab.pdfgen import canvas 80 | from django.http import HttpResponse 81 | 82 | def some_view(request): 83 | # Create the HttpResponse object with the appropriate PDF headers. 84 | response = HttpResponse(content_type='application/pdf') 85 | response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"' 86 | 87 | buffer = BytesIO() 88 | 89 | # Create the PDF object, using the BytesIO object as its "file." 90 | p = canvas.Canvas(buffer) 91 | 92 | # Draw things on the PDF. Here's where the PDF generation happens. 93 | # See the ReportLab documentation for the full list of functionality. 94 | p.drawString(100, 100, "Hello world.") 95 | 96 | # Close the PDF object cleanly. 97 | p.showPage() 98 | p.save() 99 | 100 | # Get the value of the BytesIO buffer and write it to the response. 101 | pdf = buffer.getvalue() 102 | buffer.close() 103 | response.write(pdf) 104 | return response 105 | ``` 106 | 107 | ## 更多资源 ## 108 | 109 | + [PDFlib](http://www.pdflib.org/)与Python捆绑的另一个PDF生成库。在Django中使用它的方法和这篇文章所阐述的相同。 110 | + [Pisa XHTML2PDF](http://www.xhtml2pdf.com/)是另一个PDF生成库。Pisa自带了如何将 Pisa 集成到 Django的例子。 111 | + [HTMLdoc](http://www.htmldoc.org/)是一个命令行脚本,它可以把HTML转换为PDF。它并没有Python接口,但是你可以使用`system` 或者 `popen`,在控制台中使用它,然后再Python中取回输出。 112 | 113 | ## 其它格式 ## 114 | 115 | 要注意在这些例子中并没有很多PDF特定的东西 -- 只是使用了`reportlab`。你可以使用相似的技巧来生成任何格式,只要你可以找到对应的Python库。关于用于生成基于文本的格式的其它例子和技巧,另见[使用Django输出CSV](http://python.usyiyi.cn/django/howto/outputting-csv.html)。 116 | 117 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Generating PDF](https://docs.djangoproject.com/en/1.8/howto/outputting-pdf/)。 118 | > 119 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 120 | > 121 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 122 | 123 | {% endraw %} 124 | -------------------------------------------------------------------------------- /3_6.md: -------------------------------------------------------------------------------- 1 | # 中间件 -------------------------------------------------------------------------------- /3_6_1_Overview.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 中间件 # 6 | 7 | 中间件是一个介入Django的请求和响应的处理过程中的钩子框架。它是一个轻量级,底层的“插件”系统,用于在全局修改Django的输入或输出。 8 | 9 | 中间件组件责任处理某些特殊的功能。例如,Django包含一个中间件组件,AuthenticationMiddleware ,使用会话将用户和请求关联。 10 | 11 | 这篇文档讲解了中间件如何工作,如何激活中间件,以及如何编写自己的中间件。Django集成了一些内置的中间件可以直接开箱即用。它们被归档在 内置中间件参考. 12 | 13 | ## 激活中间件 ## 14 | 15 | 要激活一个中间件组件,需要把它添加到你Django配置文件中的MIDDLEWARE_CLASSES 列表中。 16 | 17 | 在MIDDLEWARE_CLASSES中,每一个中间件组件用字符串的方式描述:一个完整的Python全路径加上中间件的类名称。例如,使用 django-admin startproject创建工程的时候生成的默认值: 18 | 19 | ``` 20 | MIDDLEWARE_CLASSES = ( 21 | 'django.contrib.sessions.middleware.SessionMiddleware', 22 | 'django.middleware.common.CommonMiddleware', 23 | 'django.middleware.csrf.CsrfViewMiddleware', 24 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 25 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 26 | 'django.contrib.messages.middleware.MessageMiddleware', 27 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 28 | 'django.middleware.security.SecurityMiddleware', 29 | ) 30 | ``` 31 | 32 | Django的程序中,中间件不是必需的 — 只要你喜欢,MIDDLEWARE_CLASSES可以为空 — 但是强烈推荐你至少使用CommonMiddleware。 33 | 34 | MIDDLEWARE_CLASSES中的顺序非常重要,因为一个中间件可能依赖于另外一个。例如,AuthenticationMiddleware在会话中储存已认证的用户。所以它必须在SessionMiddleware之后运行。一些关于Django中间件类的顺序的常见提示,请见Middleware ordering。 35 | 36 | ## 钩子和应用顺序 ## 37 | 38 | 在请求阶段中,调用视图之前,Django会按照MIDDLEWARE_CLASSES中定义的顺序自顶向下应用中间件。会用到两个钩子: 39 | 40 | + process_request() 41 | + process_view() 42 | 43 | 在响应阶段中,调用视图之后,中间件会按照相反的顺序应用,自底向上。会用到三个钩子: 44 | 45 | + process_exception() (仅当视图抛出异常的时候) 46 | + process_template_response() (仅用于模板响应) 47 | + process_response() 48 | 49 | ![](https://docs.djangoproject.com/en/1.8/_images/middleware.svg) 50 | 51 | 如果你愿意的话,你可以把它想象成一颗洋葱:每个中间件都是包裹视图的一层“皮”。 52 | 53 | 每个钩子的行为接下来会描述。 54 | 55 | ## 编写自己的中间件 ## 56 | 57 | 编写自己的中间件很容易的。每个中间件组件是一个单独的Python的class,你可以定一个或多个下面的这些方法: 58 | 59 | ### process_request ### 60 | 61 | **process_request(request)** 62 | 63 | request是一个HttpRequest 对象。 64 | 65 | 在Django决定执行哪个视图(view)之前,process_request()会被每次请求调用。 66 | 67 | 它应该返回一个None 或一个HttpResponse对象。如果返回 None, Django会继续处理这个请求,执行其他process_request()中间件,然后process_view()中间件显示对应的视图。如果它返回一个HttpResponse对象,Django便不再会去调用其他的请求(request), 视图(view)或其他中间件,或对应的视图;处理HttpResponse的中间件会处理任何返回的响应(response)。 68 | 69 | ### process_view ### 70 | 71 | **process_view(request, view_func, view_args, view_kwargs)** 72 | 73 | request是一个HttpRequest对象。view_func是 Django会调用的一个Python的函数。(它确实是一个函数对象,不是函数的字符名称。) view_args是一个会被传递到视图的位置参数列表,而view_kwargs 是一个会被传递到视图的关键字参数字典。 view_args和 view_kwargs 都不包括第一个视图参数(request)。 74 | 75 | process_view()会在Django调用视图(view)之前被调用。 76 | 77 | 它将返回None 或一个HttpResponse 对象。如果返回 None,将会继续处理这个请求,执行其他的process_view() 中间件,然后显示对应的视图。如果返回HttpResponse对象,Django就不再会去调用其他的视图(view),异常中间件(exception middleware)或对应的视图 ;它会把响应中间件应用到HttpResponse上,并返回结果。 78 | 79 | > 注意 80 | > 81 | > 在中间件内部,从process_request或者process_view方法中访问request.POST或者request.REQUEST将会阻碍该中间 件之后的所有视图无法修改request的上传处理程序, 一般情况要避免这样使用。 82 | 83 | > 类CsrfViewMiddleware可以被认为是个例外 ,因为它提供了csrf_exempt() 和 csrf_protect()两个允许视图来精确控制 在哪个点需要开启CSRF验证。 84 | 85 | ### process_template_response ### 86 | 87 | **process_template_response(request, response)** 88 | 89 | request是一个HttpRequest对象。response是一个TemplateResponse对象(或等价的对象),由Django视图或者中间件返回。 90 | 91 | 如果响应的实例有render()方法,process_template_response()在视图刚好执行完毕之后被调用,这表明了它是一个TemplateResponse对象(或等价的对象)。 92 | 93 | 这个方法必须返回一个实现了render方法的响应对象。它可以修改给定的response对象,通过修改 response.template_name和response.context_data或者它可以创建一个全新的 TemplateResponse或等价的对象。 94 | 95 | 你不需要显式渲染响应 —— 一旦所有的模板响应中间件被调用,响应会自动被渲染。 96 | 97 | 在一个响应的处理期间,中间件以相反的顺序运行,这包括process_template_response()。 98 | 99 | ### process_response ### 100 | 101 | **process_response(request, response)** 102 | 103 | request是一个HttpRequest对象。response是Django视图或者中间件返回的HttpResponse或者StreamingHttpResponse对象。 104 | 105 | process_response()在所有响应返回浏览器之前被调用。 106 | 107 | 这个方法必须返回HttpResponse或者StreamingHttpResponse对象。它可以改变已有的response,或者创建并返回新的HttpResponse或StreamingHttpResponse对象。 108 | 109 | 不像 process_request()和process_view()方法,即使同一个中间件类中的process_request()和process_view()方法会因为前面的一个中间件返回HttpResponse而被跳过,process_response()方法总是会被调用。特别是,这意味着你的process_response()方法不能依赖于process_request()方法中的设置。 110 | 111 | 最后,记住在响应阶段中,中间件以相反的顺序被应用,自底向上。意思是定义在MIDDLEWARE_CLASSES最底下的类会最先被运行。 112 | 113 | ### 处理流式响应 ### 114 | 115 | 不像HttpResponse,StreamingHttpResponse并没有content属性。所以,中间件再也不能假设所有响应都带有content属性。如果它们需要访问内容,他们必须测试是否为流式响应,并相应地调整自己的行为。 116 | 117 | ``` 118 | if response.streaming: 119 | response.streaming_content = wrap_streaming_content(response.streaming_content) 120 | else: 121 | response.content = alter_content(response.content) 122 | ``` 123 | 124 | > 注意 125 | > 126 | > 我们需要假设streaming_content可能会大到在内存中无法容纳。响应中间件可能会把它封装在新的生成器中,但是一定不要销毁它。封装一般会实现成这样: 127 | > 128 | ``` 129 | def wrap_streaming_content(content): 130 | for chunk in content: 131 | yield alter_content(chunk) 132 | ``` 133 | > 134 | 135 | ### process_exception ### 136 | 137 | **process_exception(request, exception)** 138 | 139 | request是一个HttpRequest对象。exception是一个被视图中的方法抛出来的 Exception对象。 140 | 141 | 当一个视图抛出异常时,Django会调用process_exception()来处理。process_exception()应该返回一个None 或者一个HttpResponse对象。如果它返回一个HttpResponse对象,模型响应和响应中间件会被应用,响应结果会返回给浏览器。Otherwise, default exception handling kicks in. 142 | 143 | 再次提醒,在处理响应期间,中间件的执行顺序是倒序执行的,这包括process_exception。如果一个异常处理的中间件返回了一个响应,那这个中间件上面的中间件都将不会被调用。 144 | 145 | ### \_\_init\_\_ ### 146 | 147 | 大多数的中间件类都不需要一个初始化方法,因为中间件的类定义仅仅是为process\_\*提供一个占位符。如果你确实需要一个全局的状态那就可以通过\_\_init\_\_来加载。然后要铭记如下两个警告: 148 | 149 | Django初始化你的中间件无需任何参数,因此不要定义一个有参数的\_\_init\_\_方法。 150 | 不像process\_\*每次请求到达都要调用\_\_init\_\_只会被调用一次,就是在Web服务启动的时候。 151 | 152 | 153 | ### 标记中间件不被使用 ### 154 | 155 | 有时在运行时决定是否一个中间件需要被加载是很有用的。 在这种情况下,你的中间件中的 \_\_init\_\_方法可以抛出一个django.core.exceptions.MiddlewareNotUsed异常。Django会从中间件处理过程中移除这部分中间件,并且当DEBUG为True的时候在django.request记录器中记录调试信息。 156 | 157 | ``` 158 | 1.8中的修改: 159 | 160 | 之前 MiddlewareNotUsed异常不会被记录。 161 | ``` 162 | 163 | ## 指导准则 ## 164 | 165 | + 中间件的类不能是任何类的子类。 166 | + 中间件可以存在与你Python路径中的任何位置。 Django所关心的只是被包含在MIDDLEWARE_CLASSES中的配置。 167 | + 将Django’s available middleware作为例子随便看看。 168 | + 如果你认为你写的中间件组建可能会对其他人有用,那就把它共享到社区! 让我们知道它,我们会考虑把它添加到Django中。 -------------------------------------------------------------------------------- /3_The view layer.md: -------------------------------------------------------------------------------- 1 | # 视图层 # 2 | 3 | Django 具有“视图”的概览,用于封装负责处理用户请求及返回响应的逻辑。通过下面的链接可以找到你需要知道的所有关于视图的内容: 4 | -------------------------------------------------------------------------------- /4_1.md: -------------------------------------------------------------------------------- 1 | # 基础 -------------------------------------------------------------------------------- /4_2.md: -------------------------------------------------------------------------------- 1 | # 面向设计师 -------------------------------------------------------------------------------- /4_2_3_Web design helpers.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # django.contrib.webdesign 4 | 5 | 自版本1.8后已弃用:程序包仅包含一个模板标记,并已移至内置标记([`lorem`](../templates/builtins.html#std:templatetag-lorem))。 6 | 7 | -------------------------------------------------------------------------------- /4_2_4_Humanization.md: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | 3 | 6 | 7 | # django.contrib.humanize # 8 | 9 | 一系列Django的模板过滤器,有助于向数据添加“人文关怀”。 10 | 11 | 把'django.contrib.humanize'添加到INSTALLED_APPS设置来激活这些过滤器。 执行以上步骤之后,在模板中使用{% load humanize %} ,你就可以访问到下面的过滤器了·。 12 | 13 | ## 基数词 ## 14 | 15 | 对于数字1~9,返回拼写出来的数字。否则返回数字本身。这样遵循了出版的格式。 16 | 17 | 例如: 18 | 19 | + 1 会变成one。 20 | + 2 会变成 two。 21 | + 10 会变成 10。 22 | 23 | 你可以传递整数,或者整数的字符串形式。 24 | 25 | ## 整数间的逗号 ## 26 | 27 | 将整数转化为字符串,每三位之间带一个逗号。 28 | 29 | 例如: 30 | 31 | + 4500 会变成 4,500。 32 | + 45000 会变成 45,000 33 | + 450000 会变成 450,000。 34 | + 4500000 会变成 4,500,000。 35 | 36 | 如果启动了格式本地化,将会被遵循。例如,在德语('de')中: 37 | 38 | + 45000 会变成 '45.000'。 39 | + 450000 会变成 '450.000'。 40 | 41 | 你可以传递整数,或者整数的字符串形式。 42 | 43 | ## 整数词组 ## 44 | 45 | 将一个大的整数转化为友好的文字表示形式。适用于超过一百万的数字。 46 | 47 | 例如: 48 | 49 | + 1000000 会变成 1.0 million。 50 | + 1200000 会变成 1.2 million。 51 | + 1200000000 会变成 1.2 billion。 52 | 53 | 支持高达10的100次方 (Googol) 的整数。 54 | 55 | 如果启动了格式本地化将会被遵循。例如,在德语('de')中: 56 | 57 | + 1000000 会变成 '1,0 Million'。 58 | + 1200000 会变成 '1,2 Million'。 59 | + 1200000000 会变成 '1,2 Milliarden'。 60 | 61 | 你可以传递整数,或者整数的字符串形式。 62 | 63 | ## 自然日期 ## 64 | 65 | 对于当天或者一天之内的日期, 返回“今天”,“明天”或者“昨天”,视情况而定。否则,使用传进来的格式字符串给日期格式化。 66 | 67 | 参数:日期的格式字符串在date标签中描述。 68 | 69 | 例如(其中“今天”是2007年2月17日): 70 | 71 | + 16 Feb 2007 会变成 yesterday。 72 | + 17 Feb 2007 会变成 today。 73 | + 18 Feb 2007 会变成 tomorrow。 74 | 75 | 其他日期按照提供的参数格式化,如果没提供参数的话,将会按照DATE_FORMAT 设置。 76 | 77 | ## 自然时间 ## 78 | 79 | 对于日期时间的值,返回一个字符串来表示多少秒、分钟或者小时之前 —— 如果超过一天之前,则回退为使用timesince格式。如果是未来的日期时间,返回值会自动使用合适的文字表述。 80 | 81 | 例如(其中“现在”是2007年2月17日16时30分0秒): 82 | 83 | + 17 Feb 2007 16:30:00 会变成 now。 84 | + 17 Feb 2007 16:29:31 会变成 29 seconds ago。 85 | + 17 Feb 2007 16:29:00 会变成 a minute ago。 86 | + 17 Feb 2007 16:25:35 会变成 4 minutes ago。 87 | + 17 Feb 2007 15:30:29 会变成 59 minutes ago。 88 | + 17 Feb 2007 15:30:01 会变成 59 minutes ago。 89 | + 17 Feb 2007 15:30:00 会变成 an hour ago。 90 | + 17 Feb 2007 13:31:29 会变成 2 hours ago。 91 | + 16 Feb 2007 13:31:29 会变成 1 day, 2 hours ago。 92 | + 16 Feb 2007 13:30:01 会变成 1 day, 2 hours ago。 93 | + 16 Feb 2007 13:30:00 会变成 1 day, 3 hours ago。 94 | + 17 Feb 2007 16:30:30 会变成 30 seconds from now。 95 | + 17 Feb 2007 16:30:29 会变成 29 seconds from now。 96 | + 17 Feb 2007 16:31:00 会变成 a minute from now。 97 | + 17 Feb 2007 16:34:35 会变成 4 minutes from now。 98 | + 17 Feb 2007 17:30:29 会变成 an hour from now。 99 | + 17 Feb 2007 18:31:29 会变成 2 hours from now。 100 | + 18 Feb 2007 16:31:29 会变成 1 day from now。 101 | + 26 Feb 2007 18:31:29 会变成 1 week, 2 days from now。 102 | 103 | ## 序数词 ## 104 | 105 | 将一个整数转化为它的序数词字符串。 106 | 107 | 例如: 108 | 109 | + 1 会变成 1st。 110 | + 2 会变成 2nd。 111 | + 3 会变成 3rd。 112 | 113 | 你可以传递整数,或者整数的字符串形式。 114 | 115 | {% endraw %} 116 | -------------------------------------------------------------------------------- /4_3.md: -------------------------------------------------------------------------------- 1 | # 面向程序员 -------------------------------------------------------------------------------- /4_The template layer.md: -------------------------------------------------------------------------------- 1 | #模板层 # 2 | 3 | 模板层提供了设计友好的语法来展示信息给用户。 了解其语法可以让设计师知道如何使用,让程序员知道如何扩展: 4 | -------------------------------------------------------------------------------- /5_1.md: -------------------------------------------------------------------------------- 1 | # 基础 -------------------------------------------------------------------------------- /5_2.md: -------------------------------------------------------------------------------- 1 | # 高级 -------------------------------------------------------------------------------- /5_Forms.md: -------------------------------------------------------------------------------- 1 | # 表单 # 2 | 3 | Django 提供了一个丰富的框架可便利地创建表单及操作表单数据。 4 | -------------------------------------------------------------------------------- /6_1.md: -------------------------------------------------------------------------------- 1 | # 设置 -------------------------------------------------------------------------------- /6_1_1_Overview.md: -------------------------------------------------------------------------------- 1 | # Django 的设置 # 2 | 3 | Django 的设置文件包含你安装的Django 的所有配置。这页文档解释设置是如何工作以及有哪些设置。 4 | 5 | ## 基础 ## 6 | 7 | 设置文件只是一个Python 模块,带有模块级别的变量。 8 | 9 | 下面是一些示例设置: 10 | 11 | ``` 12 | ALLOWED_HOSTS = ['www.example.com'] 13 | DEBUG = False 14 | DEFAULT_FROM_EMAIL = 'webmaster@example.com' 15 | ``` 16 | 17 | > 注 18 | > 19 | > 如果你设置`DEBUG` 为`False`,那么你应该正确设置`ALLOWED_HOSTS` 的值。 20 | 21 | 因为设置文件是一个Python 模块,所以适用以下情况: 22 | 23 | + 不允许出现Python 语法错误。 24 | + 它可以使用普通的Python 语法动态地设置。例如: 25 | 26 | ``` 27 | MY_SETTING = [str(i) for i in range(30)] 28 | ``` 29 | 30 | + 它可以从其它设置文件导入值。 31 | 32 | ## 指定设置文件 ## 33 | 34 | `DJANGO_SETTINGS_MODULE` 35 | 36 | 当你使用Django 时,你必须告诉它你正在使用哪个设置。这可以使用环境变量`DJANGO_SETTINGS_MODULE` 来实现。 37 | 38 | `DJANGO_SETTINGS_MODULE` 的值应该使用Python 路径的语法,例如`mysite.settings`。注意,设置模块应该在Python 的导入查找路径 中。 39 | 40 | ### django-admin 工具 ### 41 | 42 | 当使用`django-admin` 时, 你可以设置只设置环境变量一次,或者每次运行该工具时显式传递设置模块。 43 | 44 | 例如(Unix Bash shell): 45 | 46 | ``` 47 | export DJANGO_SETTINGS_MODULE=mysite.settings 48 | django-admin runserver 49 | ``` 50 | 51 | 例如(Windows shell): 52 | 53 | ``` 54 | set DJANGO_SETTINGS_MODULE=mysite.settings 55 | django-admin runserver 56 | ``` 57 | 58 | 使用--settings 命令行参数可以手工指定设置: 59 | 60 | ``` 61 | django-admin runserver --settings=mysite.settings 62 | ``` 63 | 64 | ### 在服务器上(mod_wsgi) ### 65 | 66 | 在线上服务器环境中,你需要告诉WSGI 的application 使用哪个设置文件。可以使用os.environ 实现: 67 | 68 | ``` 69 | import os 70 | 71 | os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' 72 | ``` 73 | 74 | 阅读[Django mod_wsgi 文档](http://python.usyiyi.cn/django/howto/deployment/wsgi/modwsgi.html) 以获得关于Django WSGI application 的更多和其它常见信息。 75 | 76 | ## 默认的设置 ## 77 | 78 | Django 的设置文件不需要定义所有的设置。每个设置都有一个合理的默认值。这些默认值位于`django/conf/global_settings.py` 模块中。 79 | 80 | 下面是Django 用来编译设置的算法: 81 | 82 | + 从`global_settings.py` 中加载设置。 83 | + 从指定的设置文件中加载设置,如有必要则覆盖全局的设置。 84 | 85 | 注意,设置文件不 应该从global_settings 中导入,因为这是多余的。 86 | 87 | ### 查看改变的设置 ### 88 | 89 | 有一个简单的方法可以查看哪些设置与默认的设置不一样了。python `manage.py` `diffsettings` 命令显示当前的设置文件和Django 默认设置之间的差异。 90 | 91 | 获取更多信息,查看`diffsettings` 的文档。 92 | 93 | ## 在Python 代码中使用设置 ## 94 | 95 | 在Django 应用中,可以通过导入`django.conf.settings` 对象来使用设置。例如: 96 | 97 | ``` 98 | from django.conf import settings 99 | 100 | if settings.DEBUG: 101 | # Do something 102 | ``` 103 | 104 | 注意,`django.conf.settings` 不是一个模块 —— 它是一个对象。所以不可以导入每个单独的设置: 105 | 106 | ``` 107 | from django.conf.settings import DEBUG # This won't work. 108 | ``` 109 | 110 | 还要注意,你的代码不应该 从`global_settings` 或你自己的设置文件中导入。`django.conf.settings` 抽象出默认设置和站点特定设置的概念;它表示一个单一的接口。它还可以将代码从你的设置所在的位置解耦出来。 111 | 112 | ## 运行时改变设置 ## 113 | 114 | 请不要在应用运行时改变设置。例如,不要在视图中这样做: 115 | 116 | ``` 117 | from django.conf import settings 118 | 119 | settings.DEBUG = True # Don't do this! 120 | ``` 121 | 122 | 给设置赋值的唯一地方是在设置文件中。 123 | 124 | ## 安全 ## 125 | 126 | 因为设置文件包含敏感的信息,例如数据库密码,你应该尽一切可能来限制对它的访问。例如,修改它的文件权限使得只有你和Web 服务器使用者可以读取它。这在共享主机的环境中特别重要。 127 | 128 | ## 可用的设置 ## 129 | 130 | 完整的可用设置清单,请参见[设置参考](http://python.usyiyi.cn/django/ref/settings.html)。 131 | 132 | ## 创建你自己的设置 ## 133 | 134 | 没有什么可以阻止你为自己的Django 应用创建自己的设置。只需要遵循下面的一些惯例: 135 | 136 | + 设置名称全部是大写 137 | + 不要使用一个已经存在的设置 138 | 139 | 对于序列类型的设置,Django 自己使用元组而不是列表,但这只是一个习惯。 140 | 141 | ## 不用DJANGO_SETTINGS_MODULE 设置 ## 142 | 143 | 有些情况下,你可能想绕开`DJANGO_SETTINGS_MODULE` 环境变量。例如,如果你正在使用自己的模板系统,而你不想建立指向设置模块的环境变量。 144 | 145 | 这些情况下,你可以手工配置Django 的设置。实现这点可以通过调用: 146 | 147 | `django.conf.settings.configure(default_settings, **settings)` 148 | 149 | 例如: 150 | 151 | ``` 152 | from django.conf import settings 153 | 154 | settings.configure(DEBUG=True) 155 | ``` 156 | 157 | 可以传递`configure()` 给任意多的关键字参数,每个关键字参数表示一个设置及其值。每个参数的名称应该都是大写,与上面讲到的设置名称相同。如果某个设置没有传递给`configure()` 而且在后面需要使用到它,Django 将使用其默认设置的值。 158 | 159 | 当你在一个更大的应用中使用到Django 框架的一部分,有必要以这种方式配置Django —— 而且实际上推荐这么做。 160 | 161 | 所以,当通过`settings.configure()` 配置时,Django 不会对进程的环境变量做任何修改(参见`TIME_ZONE` 文档以了解为什么会发生)。在这些情况下,它假设你已经完全控制你的环境变量。 162 | 163 | ## 自定义默认的设置 ## 164 | 165 | 如果你想让默认值来自其它地方而不是`django.conf.global_settings`,你可以传递一个提供默认设置的模块或类作为`default_settings` 参数(或第一个位置参数)给`configure()` 调用。 166 | 167 | 在下面的示例中,默认的设置来自`myapp_defaults`, 并且设置`DEBUG` 为`True`,而不论它在`myapp_defaults` 中的值是什么: 168 | 169 | ``` 170 | from django.conf import settings 171 | from myapp import myapp_defaults 172 | 173 | settings.configure(default_settings=myapp_defaults, DEBUG=True) 174 | ``` 175 | 176 | 下面的示例和上面一样,只是使用`myapp_defaults` 作为一个位置参数: 177 | 178 | ``` 179 | settings.configure(myapp_defaults, DEBUG=True) 180 | ``` 181 | 182 | 正常情况下,你不需要用这种方式覆盖默认值。Django 的默认值以及足够好使,你可以安全地使用它们。注意,如果你传递一个新的默认模块,你将完全取代 Django 的默认值,所以你必须指定每个可能用到的设置的值。完整的设置清单,参见`django.conf.settings.global_settings`。 183 | 184 | ### configure() 和DJANGO_SETTINGS_MODULE 两者必居其一 ### 185 | 186 | 如果你没有设置`DJANGO_SETTINGS_MODULE` 环境变量,你 必须 在使用到读取设置的任何代码之前调用`configure()` 。 187 | 188 | 如果你没有设置`DJANGO_SETTINGS_MODULE` 且没有调用 `configure()`,在首次访问设置时Django 将引发一个`ImportError` 异常。 189 | 190 | 如果你设置了`DJANGO_SETTINGS_MODULE`,并访问了一下设置,然后 调用`configure()`,Django 将引发一个`RuntimeError` 表示该设置已经有配置。有个属性正好可以用于这个情况: 191 | 192 | 例如: 193 | 194 | ``` 195 | from django.conf import settings 196 | if not settings.configured: 197 | settings.configure(myapp_defaults, DEBUG=True) 198 | ``` 199 | 200 | 另外,多次调用`configure()`或者在设置已经访问过之后调用 `configure()` 都是错误的。 201 | 202 | 归结为一点:只使用`configure()` 或 `DJANGO_SETTINGS_MODULE` 中的一个。不可以两个都用和都不用。 203 | 204 | > 另见 205 | > 206 | > [设置参考](http://python.usyiyi.cn/django/ref/settings.html) 207 | > 包含完整的核心设置和contrib 应用设置的列表。 208 | 209 | ‍ 210 | 211 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Overview](https://docs.djangoproject.com/en/1.8/topics/settings/)。 212 | > 213 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 214 | > 215 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 216 | -------------------------------------------------------------------------------- /6_2.md: -------------------------------------------------------------------------------- 1 | # 应用程序 -------------------------------------------------------------------------------- /6_3.md: -------------------------------------------------------------------------------- 1 | # 异常 -------------------------------------------------------------------------------- /6_4.md: -------------------------------------------------------------------------------- 1 | # django-admin 和 manage.py -------------------------------------------------------------------------------- /6_5.md: -------------------------------------------------------------------------------- 1 | # 测试 -------------------------------------------------------------------------------- /6_5_1_Introduction.md: -------------------------------------------------------------------------------- 1 | # Django中的测试 # 2 | 3 | 自动化测试对于现代web开发者来说,是非常实用的除错工具。你可以使用一系列测试-- 测试套件 -- 来解决或者避免大量问题: 4 | 5 | + 当你编写新代码的时候,你可以使用测试来验证你的代码是否像预期一样工作。 6 | + 当你重构或者修改旧代码的时候,你可以使用测试来确保你的修改不会在意料之外影响到你的应用的应为。 7 | 8 | 测试web应用是个复杂的任务,因为web应用由很多的逻辑层组成 -- 从HTTP层面的请求处理,到表单验证和处理,到模板渲染。使用Django的测试执行框架和各种各样的工具,你可以模拟请求,插入测试数据,检查你的应用的输出,以及大体上检查你的代码是否做了它应该做的事情。 9 | 10 | 最好的一点是,它非常简单。 11 | 12 | 在Django中编写测试的最佳方法是,使用构建于Python标准库的unittest模块。这在[编写和运行测试](http://python.usyiyi.cn/django/topics/testing/overview.html) 文档中会详细介绍。 13 | 14 | 你也可以使用任何其它 Python 的测试框架;Django为整合它们提供了API和工具。这在[高级测试话题](http://python.usyiyi.cn/django/topics/testing/advanced.html)的[使用不同的测试框架](http://python.usyiyi.cn/django/topics/testing/advanced.html#other-testing-frameworks) 一节中描述。 15 | 16 | + [编写和运行测试](http://python.usyiyi.cn/django/topics/testing/overview.html) 17 | + [测试工具](http://python.usyiyi.cn/django/topics/testing/tools.html) 18 | + [高级测试话题](http://python.usyiyi.cn/django/topics/testing/advanced.html) 19 | 20 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Introduction](https://docs.djangoproject.com/en/1.8/topics/testing/)。 21 | > 22 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 23 | > 24 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 25 | -------------------------------------------------------------------------------- /6_6.md: -------------------------------------------------------------------------------- 1 | # 部署 -------------------------------------------------------------------------------- /6_6_1_Overview.md: -------------------------------------------------------------------------------- 1 | # 部署 Django # 2 | 3 | 虽然Django 满满的便捷性让Web 开发人员活得轻松一些,但是如果不能轻松地部署你的网站,这些工具还是没有什么用处。Django 起初,易于部署就是一个主要的目标。有许多优秀的方法可以轻松地来部署Django: 4 | 5 | + [如何使用WSGI 部署](http://python.usyiyi.cn/django/howto/deployment/wsgi/index.html) 6 | + [部署的检查清单](http://python.usyiyi.cn/django/howto/deployment/checklist.html) 7 | 8 | FastCGI 的支持已经废弃并将在Django 1.9 中删除。 9 | 10 | + [如何使用FastCGI、SCGI 和AJP 部署Django](http://python.usyiyi.cn/django/howto/deployment/fastcgi.html) 11 | 12 | 如果你是部署Django 和/或 Python 的新手,我们建议你先试试 [mod_wsgi](http://python.usyiyi.cn/django/howto/deployment/wsgi/modwsgi.html)。 在大部分情况下,这将是最简单、最迅速和最稳当的部署选择。 13 | 14 | > 另见 15 | > 16 | > [Django Book(第二版)的第12 章](http://www.djangobook.com/en/2.0/chapter12.html) 更详细地讨论了部署,尤其是可扩展性。但是请注意,这个版本是基于Django 1.1 版本编写,而且在`mod_python` 废弃并于Django 1.5 中删除之后一直没有更新。 17 | 18 | ‍ 19 | 20 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Overview](https://docs.djangoproject.com/en/1.8/howto/deployment/)。 21 | > 22 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 23 | > 24 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 25 | -------------------------------------------------------------------------------- /6_6_2_WSGI servers.md: -------------------------------------------------------------------------------- 1 | # 如何使用WSGI 部署 # 2 | 3 | Django 首要的部署平台是[WSGI](http://www.wsgi.org/),它是Python Web 服务器和应用的标准。 4 | 5 | Django 的`startproject` 管理命名为你设置一个简单的默认WSGI 配置,你可以根据你项目的需要做调整并指定任何与WSGI 兼容的应用服务器使用。 6 | 7 | Django 包含以下WSGI 服务器的入门文档: 8 | 9 | + [如何使用Apache 和mod_wsgi 部署Django](http://python.usyiyi.cn/django/howto/deployment/wsgi/modwsgi.html) 10 | + [从Apache 中利用Django 的用户数据库进行认证](http://python.usyiyi.cn/django/howto/deployment/wsgi/apache-auth.html) 11 | + [如何使用Gunicorn 部署Django (100%)](http://python.usyiyi.cn/django/howto/deployment/wsgi/gunicorn.html) 12 | + [如何使用uWSGI 部署Django (100%)](http://python.usyiyi.cn/django/howto/deployment/wsgi/uwsgi.html) 13 | 14 | ## application 对象 ## 15 | 16 | 使用WSGI 部署的核心概览是`application` 可调用对象,应用服务器使用它来与你的代码进行交换。在Python 模块中,它通常一个名为`application` 的对象提供给服务器使用。 17 | 18 | `startproject` 命令创建一个`/wsgi.py` 文件,它就包含这样一个`application` 可调用对象。 19 | 20 | 它既可用于Django 的开发服务器,也可以用于线上WSGI 的部署。 21 | 22 | WSGI 服务器从它们的配置中获得`application` 可调用对象的路径。Django 内建的服务器,叫做`runserver` 和`runfcgi` 命令,是从`WSGI_APPLICATION` 设置中读取它。默认情况下,它设置为`.wsgi.application`,指向`/wsgi.py` 中的`application` 可调用对象。 23 | 24 | ## 配置settings 模块 ## 25 | 26 | 当WSGI 服务器加载你的应用时,Django 需要导入settings 模块 —— 这里是你的全部应用定义的地方。 27 | 28 | Django 使用`DJANGO_SETTINGS_MODULE` 环境变量来定位settings 模块。它包含settings 模块的路径,以点分法表示。对于开发环境和线上环境,你可以使用不同的值;这完全取决于你如何组织你的settings。 29 | 30 | 如果这个变量没有设置,默认的`wsgi.py` 设置为`mysite.settings`,其中`mysite` 为你的项目的名称。这是`runserver` 如何找到默认的`settings` 文件的机制。 31 | 32 | > 注 33 | > 34 | > 因为环境变量是进程范围的,当你在同一个进程中运行多个Django 站点时,它将不能工作。使用`mod_wsgi` 就是这个情况。 35 | > 36 | > 为了避免这个问题,可以使用mod_wsgi 的守护进程模式,让每个站点位于它自己的守护进程中,或者在`wsgi.py`中通过强制使用`os.environ["DJANGO_SETTINGS_MODULE"] = "mysite.settings"` 来覆盖这个值。 37 | 38 | ## 运用WSGI 中间件 ## 39 | 40 | 你可以简单地封装application 对象来运用 [WSGI 中间件](https://www.python.org/dev/peps/pep-3333/#middleware-components-that-play-both-sides)。 例如,你可以在`wsgi.py` 的底下添加以下这些行: 41 | 42 | ``` 43 | from helloworld.wsgi import HelloWorldApplication 44 | application = HelloWorldApplication(application) 45 | ``` 46 | 47 | 如果你结合使用 Django 的application 与另外一个WSGI application 框架,你还可以替换Django WSGI 的application 为一个自定义的WSGI application。 48 | 49 | > 注 50 | > 51 | > 某些第三方的WSGI 中间件在处理完一个请求后不调用响应对象上的`close` —— most notably Sentry’s error reporting middleware up to version 2.0.7。这些情况下,不会发送`request_finished` 信号。这可能导致数据库和memcache 服务的空闲连接。 52 | 53 | ‍ 54 | 55 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[WSGI servers](https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/)。 56 | > 57 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 58 | > 59 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 60 | -------------------------------------------------------------------------------- /6_6_4_Deploying static files.md: -------------------------------------------------------------------------------- 1 | # 部署静态文件 # 2 | 3 | > 另见 4 | > 5 | > `django.contrib.staticfiles` 的用法简介,请参见[管理静态文件(CSS、images)](http://python.usyiyi.cn/django/howto/static-files/index.html)。 6 | 7 | ## 在线上环境部署静态文件 ## 8 | 9 | 放置静态文件到线上环境的基本步骤很简单:当静态文件改变时,运行`collectstatic` 命令,然后安排将收集好的静态文件的目录(`STATIC_ROOT`) 搬到静态文件服务器上。取决于`STATICFILES_STORAGE`,这些文件可能需要手工移动到一个新的位置或者`Storage` 类的`post_process` 方法可以帮你。 10 | 11 | 当然,与所有的部署任务一样,魔鬼隐藏在细节中。每个线上环境的建立都会有所不同,所以你需要调整基本的纲要以适应你的需求。下面是一些常见的方法,可能有所帮助。 12 | 13 | ### 网站和静态文件位于同一台服务器上 ### 14 | 15 | 如果你的静态文件和网站位于同一台服务器,流程可能像是这样: 16 | 17 | + 将你的代码推送到部署的服务器上。 18 | + 在这台服务器上,运行`collectstatic` 来收集所有的静态文件到`STATIC_ROOT`。 19 | + 配置Web 服务器来托管URL` STATIC_URL`下的`STATIC_ROOT`。 例如,这是[如何使用Apache 和mod_wsgi 来完成它](http://python.usyiyi.cn/django/howto/deployment/wsgi/modwsgi.html#serving-files)。 20 | 21 | 你可能想自动化这个过程,特别是如果你有多台Web 服务器。有许多种方法来完成这个自动化,但是许多Django 开发人员喜欢 [Fabric](http://fabfile.org/)。 22 | 23 | 在一下的小节中,我们将演示一些示例的Fabric 脚本来自动化不同选择的文件部署。Fabric 脚本的语法相当简单,但这里不会讲述;参见[Fabric 的文档](http://docs.fabfile.org/) 以获得其语法的完整解释。 24 | 25 | 所以,一个部署静态文件来多台Web 服务器上的Fabric 脚本大概会是: 26 | 27 | ``` 28 | from fabric.api import * 29 | 30 | # Hosts to deploy onto 31 | env.hosts = ['www1.example.com', 'www2.example.com'] 32 | 33 | # Where your project code lives on the server 34 | env.project_root = '/home/www/myproject' 35 | 36 | def deploy_static(): 37 | with cd(env.project_root): 38 | run('./manage.py collectstatic -v0 --noinput') 39 | ``` 40 | 41 | ### 静态文件位于一台专门的服务器上 ## 42 | 43 | 大部分大型的Django 站点都使用一台单独的Web 服务器来存放静态文件 —— 例如一台不运行Django 的服务器。这种服务器通常运行一种不同类型的服务器 —— 更快但是功能很少。一些常见的选择有: 44 | 45 | + [Nginx](http://wiki.nginx.org/Main) 46 | + 裁剪版的[Apache](http://httpd.apache.org/) 47 | 48 | 配置这些服务器在这篇文档范围之外;查看每种服务器各自的文档以获得说明。 49 | 50 | 既然你的静态文件服务器不会允许Django,你将需要修改的部署策略,大概会是这样: 51 | 52 | + 当静态文件改变时,在本地运行`collectstatic`。 53 | + 将你本地的`STATIC_ROOT` 推送到静态文件服务器相应的目录中。在这一步,常见的选择[rsync](https://rsync.samba.org/) ,因为它只传输静态文件改变的部分。 54 | 55 | 下面是Fabric 脚本大概的样子: 56 | 57 | ``` 58 | from fabric.api import * 59 | from fabric.contrib import project 60 | 61 | # Where the static files get collected locally. Your STATIC_ROOT setting. 62 | env.local_static_root = '/tmp/static' 63 | 64 | # Where the static files should go remotely 65 | env.remote_static_root = '/home/www/static.example.com' 66 | 67 | @roles('static') 68 | def deploy_static(): 69 | local('./manage.py collectstatic') 70 | project.rsync_project( 71 | remote_dir = env.remote_static_root, 72 | local_dir = env.local_static_root, 73 | delete = True 74 | ) 75 | ``` 76 | 77 | ### 静态文件位于一个云服务或CDN 上 ### 78 | 79 | 两位一个常见的策略是放置静态文档到一个云存储提供商比如亚马逊的S3 和/或一个CDN(Content Delivery Network)上。这让你可以忽略保存静态文件的问题,并且通常可以加快网页的加载(特别是使用CDN 的时候)。 80 | 81 | 当使用这些服务时,除了不是使用rsync 传输你的静态文件到服务器上而是到存储提供商或CDN 上之外,基本的工作流程和上面的差不多。 82 | 83 | 有许多方式可以实现它,但是如果提供商具有API,那么自定义的文件存储后端 将使得这个过程相当简单。如果你已经写好或者正在使用第三方的自定义存储后端,你可以通过设置`STATICFILES_STORAGE` 来告诉`collectstatic` 来使用它。 84 | 85 | 例如,如果你已经在`myproject.storage.S3Storage` 中写好一个S3 存储的后端,你可以这样使用它: 86 | 87 | ``` 88 | STATICFILES_STORAGE = 'myproject.storage.S3Storage' 89 | ``` 90 | 91 | 一旦完成这个,你所要做的就是运行`collectstatic`,然后你的静态文件将被你的存储后端推送到S3 上。如果以后你需要切换到一个不同的存储提供商,你只需简单地修改你的`STATICFILES_STORAGE` 设置。 92 | 93 | 关于如何编写这些后端的细节,请参见[编写一个自定义的存储系统](http://python.usyiyi.cn/django/howto/custom-file-storage.html)。有第三方的应用提供存储后端,它们支持许多常见的文件存储API。一个不错的入口是[djangopackages.com 的概览](https://www.djangopackages.com/grids/g/storage-backends/)。 94 | 95 | ## 了解更多 ## 96 | 97 | 关于`django.contrib.staticfiles` 中包含的设置、命令、模板标签和其它细节,参见[staticfiles 参考](http://python.usyiyi.cn/django/ref/contrib/staticfiles.html)。 98 | 99 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Deploying static files](https://docs.djangoproject.com/en/1.8/howto/static-files/deployment/)。 100 | > 101 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 102 | > 103 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 104 | -------------------------------------------------------------------------------- /6_The development process.md: -------------------------------------------------------------------------------- 1 | # 开发过程 # 2 | 3 | 学习各种组件和工具帮助你对Django应用进行改进和测试: 4 | -------------------------------------------------------------------------------- /7_3_Admin documentation generator.md: -------------------------------------------------------------------------------- 1 | # Django管理文档生成器 # 2 | 3 | Django的`admindocs`应用从模型、视图、模板标签以及模板过滤器中,为任何`INSTALLED_APPS`中的应用获取文档。并且让文档可以在`Django admin`中使用。 4 | 5 | 在某种程度上,你可以使用`admindocs`来快为你自己的代码生成文档。这个应用的功能十分有限,然而它主要用于文档模板、模板标签和过滤器。例如,需要参数的模型方法在文档中会有意地忽略,因为它们不能从模板中调用。这个应用仍旧有用,因为它并不需要你编写任何额外的文档(除了`docstrings`),并且在 `Django admin`中使用很方便。 6 | 7 | ## 概览 ## 8 | 9 | 要启用`admindocs`,你需要执行以下步骤: 10 | 11 | + 向 `INSTALLED_APPS`添加`django.contrib.admindocs`。 12 | + 向你的`urlpatterns`添加(`r'^admin/doc/'`, `include('django.contrib.admindocs.urls')`)。 确保它在`r'^admin/'` 这一项 之前包含,以便`/admin/doc/ `的请求不会被后面的项目处理。 13 | + 安装`docutils` Python 模块 (http://docutils.sf.net/)。 14 | + 可选的: 使用`admindocs`的书签功能需要安装`django.contrib.admindocs.middleware.XViewMiddleware`。 15 | 16 | 一旦完成这些步骤,你可以开始通过你的`admin`接口和点击在页面右上方的“Documentation”链接来浏览文档。 17 | 18 | ## 文档助手 ## 19 | 20 | 下列特定的标记可以用于你的`docstrings`,来轻易创建到其他组件的超链接: 21 | 22 | Django Component | reStructuredText roles 23 | -|- 24 | Models | :model:\`app_label.ModelName\` 25 | Views | :view:\`app_label.view_name\` 26 | Template tags | :tag:\`tagname\` 27 | Template filters | :filter:\`filtername\` 28 | Templates | :template:\`path/to/template.html\` 29 | 30 | ## 模型参考 ## 31 | 32 | `admindocs`页面的`models`部分描述了系统中每个模型,以及所有可用的字段和方法(不带任何参数)。虽然模型的属性没有任何参数,但他们没有列出。和其它模型的关联以超链接形式出现。描述由字段上的`help_text`属性,或者从模型方法的`docstrings`导出。 33 | 34 | 带有有用文档的模型看起来像是这样: 35 | 36 | ``` 37 | class BlogEntry(models.Model): 38 | """ 39 | Stores a single blog entry, related to :model:`blog.Blog` and 40 | :model:`auth.User`. 41 | 42 | """ 43 | slug = models.SlugField(help_text="A short label, generally used in URLs.") 44 | author = models.ForeignKey(User) 45 | blog = models.ForeignKey(Blog) 46 | ... 47 | 48 | def publish(self): 49 | """Makes the blog entry live on the site.""" 50 | ... 51 | ``` 52 | 53 | ## 视图参考 ## 54 | 55 | 你站点中的每个URL都在·页面中有一个单独的记录,点击提供的URL会向你展示相应的视图。有一些有用的东西,你可以在你的视图函数的·中记录: 56 | 57 | + 视图所做工作的一个简短的描述。 58 | + 上下文,或者是视图的模板中可用变量的列表。 59 | + 用于当前视图的模板的名称。 60 | 61 | 例如: 62 | 63 | ``` 64 | from django.shortcuts import render 65 | 66 | from myapp.models import MyModel 67 | 68 | def my_view(request, slug): 69 | """ 70 | Display an individual :model:`myapp.MyModel`. 71 | 72 | **Context** 73 | 74 | ``mymodel`` 75 | An instance of :model:`myapp.MyModel`. 76 | 77 | **Template:** 78 | 79 | :template:`myapp/my_template.html` 80 | 81 | """ 82 | context = {'mymodel': MyModel.objects.get(slug=slug)} 83 | return render(request, 'myapp/my_template.html', context) 84 | ``` 85 | 86 | ## 模板标签和过滤器参考 ## 87 | 88 | `admindocs`的`tags` 和`filters`部分描述了Django自带的所有标签和过滤器(事实上,内建的标签参考 和 内建的过滤器参考文档直接来自于那些页面)。你创建的,或者由三方应用添加的任何标签或者过滤器,也会在这一部分中展示。 89 | 90 | ## 模板参考 ## 91 | 92 | 虽然`admindocs` 并不包含一个地方来保存模板,但如果你在结果页面中使用:template:\`path/to/template.html\`语法,会使用Django的模板加载器来验证该模板的路径。这是一个非常便捷的方法,来检查是否存在特定的模板,以及展示模板在文件系统的何处存放。 93 | 94 | ## 包含的书签 ## 95 | 96 | `admindocs`页面上有一些很有用的书签: 97 | 98 | Documentation for this page 99 | 100 | Jumps you from any page to the documentation for the view that generates that page. 101 | 102 | Show object ID 103 | 104 | Shows the content-type and unique ID for pages that represent a single object. 105 | 106 | Edit this object 107 | 108 | Jumps to the admin page for pages that represent a single object. 109 | 110 | 为使用这些书签,你需要用带有`is_staff` 设置为 `True`的`User`登录`Django admin`,或者安装了`XViewMiddleware`并且你通过 `INTERNAL_IPS`中的IP地址访问站点。 111 | 112 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Admin documentation generator](https://docs.djangoproject.com/en/1.8/ref/contrib/admin/admindocs/)。 113 | > 114 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 115 | > 116 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 117 | -------------------------------------------------------------------------------- /7_Admin.md: -------------------------------------------------------------------------------- 1 | # Admin # 2 | 3 | Django 最受欢迎的特性之一 —— 自动生成的Admin 界面的所有内容: 4 | -------------------------------------------------------------------------------- /8_1_Security overview.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Django安全 4 | 5 | 这份文档是 Django 的安全功能的概述。 它包括给 Django 驱动的网站一些加固建议。 6 | 7 | ## 跨站脚本 (XSS) 防护 8 | 9 | XSS攻击允许用户注入客户端脚本到其他用户的浏览器里。 这通常是通过存储在数据库中的恶意脚本,它将检索并显示给其他用户,或者通过让用户点击一个链接,这将导致攻击者的 JavaScript 被用户的浏览器执行。 然而,XSS 攻击可以来自任何不受信任的源数据,如 Cookie 或 Web 服务,任何没有经过充分处理就包含在网页中的数据。 10 | 11 | 使用 Django 模板保护你免受多数 XSS 攻击。 然而,重要的是要了解它提供了什么保护及其局限性。 12 | 13 | Django 模板会[ _编码特殊字符_ ](../ref/templates/language.html#automatic-html-escaping),这些字符在 HTML 中都是特别危险的。 虽然这可以防止大多数恶意输入的用户,但它不能完全保证万无一失。 例如,它不会防护以下内容: 14 | 15 | ``` 16 | 17 | 18 | ``` 19 | 20 | 如果 `var` 设置为 `'class1 onmouseover=javascript:func()'`, 这可能会导致在未经授权的 JavaScript 的执行,取决于浏览器如何呈现不完整的 HTML。 (对属性值使用引号可以修复这种情况。) 21 | 22 | 同样重要的是`is_safe`要特别小心的用在 自定义模板标签,[`safe`](../ref/templates/builtins.html#std:templatefilter-safe) 模板标签,[`mark_safe`](../ref/utils.html#module-django.utils.safestring "django.utils.safestring: Functions and classes for working with strings that can be displayed safely without further escaping in HTML.") ,还有 autoescape 被关闭的时候。 23 | 24 | 此外,如果您使用的是模板系统输出 HTML 以外的东西,可能会有完全不同的字符和单词需要编码。 25 | 26 | 你也应该在数据库中存储 HTML 的时候要非常小心,尤其是当 HTML 被检索然后展示出来。 27 | 28 | ## 跨站请求伪造 (CSRF) 防护 29 | 30 | CSRF 攻击允许恶意用户在另一个用户不知情或者未同意的情况下,以他的身份执行操作。 31 | 32 | Django 对大多数类型的 CSRF 攻击有内置的保护,在适当情况下你可以[_开启并使用它_](../ref/csrf.html#using-csrf) 。 然而,对于任何解决技术,都有它的局限性。 例如,CSRF 模块可以在全局范围内或为特定视图被禁用 。 您应该只在您知道在做什么的情况下操作。 还有其他 [_限制_](../ref/csrf.html#csrf-limitations) 如果你的网站有子域名并且在你的控制之外。 33 | 34 | [_CSRF 防护_](../ref/csrf.html#how-csrf-works) 是通过检查每个 POST 请求的一个随机数(nonce)来工作。 这确保了恶意用户不能简单“回放”你网站上面表单的POST,以及让另一个登录的用户无意中提交表单。恶意用户必须知道这个随机数,它是用户特定的(存在cookie里)。 35 | 36 | 使用 [_HTTPS_](#security-recommendation-ssl)来部署的时候,`CsrfViewMiddleware`会检查HTTP referer协议头是否设置为同源的URL(包括子域和端口)。因为HTTPS提供了附加的安全保护,转发不安全的连接请求时,必须确保链接使用 HTTPS,并使用HSTS支持的浏览器。 37 | 38 | 使用`csrf_exempt`装饰器来标记视图时,要非常小心,除非这是极其必要的。 39 | 40 | ## SQL 注入保护 41 | 42 | SQl注入是一种攻击类型,恶意用户可以在系统数据库中执行任意SQL代码。这可能会导致记录删除或者数据泄露。 43 | 44 | 通过使用Django的查询集,产生的SQL会由底层数据库驱动正确地转义。然而,Django也允许开发者编写[_原始查询_](db/sql.html#executing-raw-queries)或者执行[_自定义sql_](db/sql.html#executing-custom-sql)。这些功能应该谨慎使用,并且你应该时刻小心正确转义任何用户可以控制的参数。另外,你在使用[`extra()`](../ref/models/querysets.html#django.db.models.query.QuerySet.extra "django.db.models.query.QuerySet.extra")的时候应该谨慎行事。 45 | 46 | ## 点击劫持保护 47 | 48 | 点击劫持是一类攻击,恶意站点在一个frame中包裹了另一个站点。这类攻击可能导致用户被诱导在目标站点做出一些无意识的行为。 49 | 50 | Django在[`X-Frame-Options 中间件`](../ref/middleware.html#django.middleware.clickjacking.XFrameOptionsMiddleware "django.middleware.clickjacking.XFrameOptionsMiddleware")的表单中中含有 [_点击劫持保护_ ](../ref/clickjacking.html#clickjacking-prevention),它在支持的浏览器中可以保护站点免于在frame中渲染。也可以在每个视图中禁止这一保护,或者配置要发送的额外的协议头。 51 | 52 | 对于任何不需要将页面包装在三方站点的frame中,或者只需要包含它的一部分的站点,都强烈推荐启用这一中间件。 53 | 54 | ## SSL / HTTPS 55 | 56 | 把你的站点部署在HTTPS下总是更安全的,尽管不是在所有情况下都有效。如果不这样,恶意的网络用户可能会嗅探授权证书,或者其他在客户端和服务端之间传输的信息,或者一些情况下 -- **活跃的**网络攻击者 -- 会修改在两边传输的数据。 57 | 58 | 如果你想要HTTPS提供保护,那么需要在你的服务器上启用它,可能还需要去做一些额外的操作 59 | 60 | * 如果必要的话,设置 [`SECURE_PROXY_SSL_HEADER`](../ref/settings.html#std:setting-SECURE_PROXY_SSL_HEADER),确保你已经彻底了解警告。未能实现它会导致CSRF方面的缺陷,也是很危险的! 61 | 62 | * 设置重定向,那样通过HTTP的请求会重定向到HTTPS。 63 | 64 | 这可以通过自定义的中间件来实现。请注意[`SECURE_PROXY_SSL_HEADER`](../ref/settings.html#std:setting-SECURE_PROXY_SSL_HEADER)下的警告。对于反向代理的情况,配置web主服务器来重定向到HTTPS或许是最简单也许是最安全的做法。 65 | 66 | * 使用“安全的”cookie。 67 | 68 | 如果浏览器的连接一开始通过HTTP,这是大多数浏览器的通常情况,已存在的cookie可能会被泄露。因此,你应该将[`SESSION_COOKIE_SECURE`](../ref/settings.html#std:setting-SESSION_COOKIE_SECURE) 和[`CSRF_COOKIE_SECURE`](../ref/settings.html#std:setting-CSRF_COOKIE_SECURE)设置为`True`。这会使浏览器只在HTTPS连接中发送这些cookie。要注意这意味着会话在HTTP下不能工作,并且CSRF保护功能会在HTTP下阻止接受任何POST数据(如果你把所有HTTP请求都重定向到HTTPS之后就没问题了)。 69 | 70 | * 使用 HTTP 强制安全传输 (HSTS) 71 | 72 | HSTS 是一个HTTP协议头,它通知浏览器,到特定站点的所有链接都一直使用HTTPS。通过和重定向HTTP请求到HTTPS一起使用,确保连接总是享有附加的SSL安全保障,由一个已存在的成功的连接提供。HSTS通常在web服务器上面配置。 73 | 74 | ## Host 协议头验证 75 | 76 | 在某些情况下,Django使用客户端提供的`Host` 协议头来构造URL。虽然这些值可以被审查,来防止跨站脚本攻击(XSS),但是一个假的`Host`值可以用于跨站请求伪造(CSRF),有害的缓存攻击,以及email中的有害链接。 77 | 78 | 因为即使表面上看起来安全的web服务器也容易被篡改`主机头`,Django再次在django.http.HttpRequest.get_host()这个方法中验证主机头这个ALLOWED_HOSTS的设置 Django validates `Host` headers against the [`ALLOWED_HOSTS`](../ref/settings.html#std:setting-ALLOWED_HOSTS) setting in the [`django.http.HttpRequest.get_host()`](../ref/request-response.html#django.http.HttpRequest.get_host "django.http.HttpRequest.get_host") method. 79 | 80 | 验证只通过[`get_host()`](../ref/request-response.html#django.http.HttpRequest.get_host "django.http.HttpRequest.get_host")来应用;如果你的代码从`request.META`中直接访问`Host`协议头,就会绕开这一安全防护。 81 | 82 | 详见完整的[`ALLOWED_HOSTS`](../ref/settings.html#std:setting-ALLOWED_HOSTS)文档。 83 | 84 | 警告 85 | 86 | 本文档的先前版本建议配置Web服务器以确保其验证传入的HTTP `Host`头。虽然这仍然是建议,在许多常见的Web服务器,似乎验证`Host`头的配置可能实际上不这样做。例如,即使Apache配置为使您的Django站点从设置了`ServerName`的非默认虚拟主机提供,HTTP请求仍然可以匹配此虚拟主机并提供假`Host`标头。因此,Django现在要求您明确设置[`ALLOWED_HOSTS`](../ref/settings.html#std:setting-ALLOWED_HOSTS),而不是依赖于Web服务器配置。 87 | 88 | 另外,就像1.3.1,如果你的配置需要它的话,Django 需要你显式开启对`X-Forwarded-Host` 协议头的支持(通过 [`USE_X_FORWARDED_HOST`](../ref/settings.html#std:setting-USE_X_FORWARDED_HOST) 设置)。 89 | 90 | ## Session 会话安全 91 | 92 | 类似于部署在站点上的[_CSRF 限制_](../ref/csrf.html#csrf-limitations) 使不受信任的用户不能访问任何子域,[`django.contrib.sessions`](http/sessions.html#module-django.contrib.sessions "django.contrib.sessions: Provides session management for Django projects.")也有一些限制。详见[_安全中会话的话题指南_](http/sessions.html#topics-session-security)。 93 | 94 | ## 用户上传的内容 95 | 96 | 注意 97 | 98 | 考虑[_在云服务器或CDN上面部署静态文件_](../howto/static-files/deployment.html#staticfiles-from-cdn)来避免一些此类问题。 99 | 100 | * 如果你的站点接受上传文件,强烈推荐你在web服务器配置中,将这些上传限制为合理的大小,来避免拒绝服务(DOS)攻击。在Apache中,这可以简单地使用[LimitRequestBody](http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestbody)指令。 101 | 102 | * 如果你自己处理静态文件,确保像Apache的`mod_php`的处理器已关闭,它会将静态文件执行为代码。你并不希望用户能够通过上传和请求一个精心构造的文件来执行任意代码。 103 | 104 | * 当媒体以不遵循安全最佳做法的方式提供时,Django的媒体上传处理带来一些漏洞。具体来说,如果HTML文件包含有效的PNG标头,然后是恶意HTML,则可以将其上传为图片。此文件将通过对Django用于[`ImageField`](../ref/models/fields.html#django.db.models.ImageField "django.db.models.ImageField")图像处理(Pillow)的库的验证。当此文件随后显示给用户时,可能会显示为HTML,具体取决于Web服务器的类型和配置。 105 | 106 | 在框架级别上没有安全验证所有用户上传的文件内容的防弹技术解决方案,但是,还可以采取一些其他步骤来减轻这些攻击: 107 | 108 | 1. 通过始终提供来自不同顶级或二级域的用户上传的内容,可以防止一类攻击。这可以防止受到[同源策略](http://en.wikipedia.org/wiki/Same-origin_policy)保护(例如跨站点脚本)阻止的任何漏洞利用。For example, if your site runs on `example.com`, you would want to serve uploaded content (the [`MEDIA_URL`](../ref/settings.html#std:setting-MEDIA_URL) setting) from something like `usercontent-example.com`. _不_足以提供来自`usercontent.example.com`等子网域的内容。 109 | 2. 除此之外,应用可以选择为用户上传的文件定义一个允许的文件扩展名的白名单,并且配置web服务器直处理这些文件。 110 | 111 | ## 额外的安全话题 112 | 113 | 虽然Django提供了开箱即用的,良好的安全保护,但是合理地部署你的应用,以及利用web服务器、操作系统和其他组件的安全保护仍然很重要。 114 | 115 | * 确保你的Python代码在web服务器的根目录外。这会确保你的Python代码不会意外被解析为纯文本(或者意外被执行)。 116 | * 小心处理任何[_用户上传的文件_](../ref/models/fields.html#file-upload-security)。 117 | * Django并不限制验证用户的请求。要保护对验证系统的暴力破解攻击,你可以考虑部署一个DJango的插件或者web服务器模块来限制这些请求。 118 | * 秘密保存[`SECRET_KEY`](../ref/settings.html#std:setting-SECRET_KEY)。 119 | * 使用防火墙来限制缓存系统和数据库的访问是个好主意。 120 | 121 | -------------------------------------------------------------------------------- /8_3_Clickjacking protection.md: -------------------------------------------------------------------------------- 1 | # 点击劫持保护 # 2 | 3 | 点击劫持中间件和装饰器提供了简捷易用的,对[点击劫持](http://en.wikipedia.org/wiki/Clickjacking)的保护。这种攻击在恶意站点诱导用户点击另一个站点的被覆盖元素时出现,另一个站点已经加载到了隐藏的`frame`或`iframe`中。 4 | 5 | ## 点击劫持的示例 ## 6 | 7 | 假设一个在线商店拥有一个页面,已登录的用户可以点击“现在购买”来购买一个商品。用户为了方便,可以选择一直保持商店的登录状态。一个攻击者的站点可能在他们自己的页面上会创建一个“我喜欢Ponies”的按钮,并且在一个透明的`iframe`中加载商店的页面,把“现在购买”的按钮隐藏起来覆盖在“我喜欢Ponies”上。如果用户访问了攻击者的站点,点击“我喜欢Ponies”按钮会触发对“现在购买”按钮的无意识的点击,不知不觉中购买了商品。 8 | 9 | ## 点击劫持的防御 ## 10 | 11 | 现代浏览器遵循[X-Frame-Options](https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header)协议头,它表明一个资源是否允许加载到`frame`或者`iframe`中。如果响应包含值为`SAMEORIGIN`的协议头,浏览器会在`frame`中只加载同源请求的的资源。如果协议头设置为`DENY`,浏览器会在加载`frame`时屏蔽所有资源,无论请求来自于哪个站点。 12 | 13 | Django提供了一些简单的方法来在你站点的响应中包含这个协议头: 14 | 15 | + 一个简单的中间件,在所有响应中设置协议头。 16 | + 一系列的视图装饰器,可以用于覆盖中间件,或者只用于设置指定视图的协议头。 17 | 18 | ## 如何使用 ## 19 | 20 | ### 为所有响应设置X-Frame-Options ### 21 | 22 | 要为你站点中所有的响应设置相同的`X-Frame-Options`值,将`'django.middleware.clickjacking.XFrameOptionsMiddleware'`设置为 `MIDDLEWARE_CLASSES`: 23 | 24 | ``` 25 | MIDDLEWARE_CLASSES = ( 26 | ... 27 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 28 | ... 29 | ) 30 | ``` 31 | 32 | 这个中间件可以在startproject生成的设置文件中开启。 33 | 34 | 通常,这个中间件会为任何开放的`HttpResponse`设置`X-Frame-Options`协议头为`SAMEORIGIN`。如果你想用 `DENY`来替代它,要设置`X_FRAME_OPTIONS`: 35 | 36 | ``` 37 | X_FRAME_OPTIONS = 'DENY' 38 | ``` 39 | 40 | 使用这个中间件时可能会有一些视图,你并不想为它设置`X-Frame-Options`协议头。对于这些情况,你可以使用一个视图装饰器来告诉中间件不要设置协议头: 41 | 42 | ``` 43 | from django.http import HttpResponse 44 | from django.views.decorators.clickjacking import xframe_options_exempt 45 | 46 | @xframe_options_exempt 47 | def ok_to_load_in_a_frame(request): 48 | return HttpResponse("This page is safe to load in a frame on any site.") 49 | ``` 50 | 51 | ### 为每个视图设置 X-Frame-Options ### 52 | 53 | Django提供了以下装饰器来为每个基础视图设置`X-Frame-Options`协议头。 54 | 55 | ``` 56 | from django.http import HttpResponse 57 | from django.views.decorators.clickjacking import xframe_options_deny 58 | from django.views.decorators.clickjacking import xframe_options_sameorigin 59 | 60 | @xframe_options_deny 61 | def view_one(request): 62 | return HttpResponse("I won't display in any frame!") 63 | 64 | @xframe_options_sameorigin 65 | def view_two(request): 66 | return HttpResponse("Display in a frame if it's from the same origin as me.") 67 | ``` 68 | 69 | 注意你可以在中间件的连接中使用装饰器。使用装饰器来覆盖中间件。 70 | 71 | ## 限制 ## 72 | 73 | `X-Frame-Options`协议头只在现代浏览器中保护点击劫持。老式的浏览器会忽视这个协议头,并且需要 [其它点击劫持防范技巧](http://en.wikipedia.org/wiki/Clickjacking#Prevention)。 74 | 75 | ### 支持 X-Frame-Options 的浏览器 ### 76 | 77 | + Internet Explorer 8+ 78 | + Firefox 3.6.9+ 79 | + Opera 10.5+ 80 | + Safari 4+ 81 | + Chrome 4.1+ 82 | 83 | ### 另见 ### 84 | 85 | 浏览器对`X-Frame-Options`支持情况的[完整列表](https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header#Browser_compatibility)。 86 | 87 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Clickjacking protection](https://docs.djangoproject.com/en/1.8/ref/clickjacking/)。 88 | > 89 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 90 | > 91 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 92 | -------------------------------------------------------------------------------- /8_5_Cryptographic signing.md: -------------------------------------------------------------------------------- 1 | # 加密签名 # 2 | 3 | web应用安全的黄金法则是,永远不要相信来自不可信来源的数据。有时通过不可信的媒介来传递数据会非常方便。密码签名后的值可以通过不受信任的途径传递,这样是安全的,因为任何篡改都会检测的到。 4 | 5 | Django提供了用于签名的底层API,以及用于设置和读取被签名cookie的上层API,它们是web应用中最常使用的签名工具之一。 6 | 7 | 你可能会发现,签名对于以下事情非常有用: 8 | 9 | + 生成用于“重置我的账户”的URL,并发送给丢失密码的用户。 10 | + 确保储存在隐藏表单字段的数据不被篡改, 11 | + 生成一次性的秘密URL,用于暂时性允许访问受保护的资源,例如用户付费的下载文件。 12 | 13 | ## 保护 SECRET_KEY ## 14 | 15 | 当你使用 startproject创建新的Django项目时,自动生成的 settings.py文件会得到一个随机的SECRET_KEY值。这个值是保护签名数据的密钥 -- 它至关重要,你必须妥善保管,否则攻击者会使用它来生成自己的签名值。 16 | 17 | ## 使用底层 API ## 18 | 19 | Django的签名方法存放于`django.core.signing`模块。首先创建一个 `Signer` 的实例来对一个值签名: 20 | 21 | ``` 22 | >>> from django.core.signing import Signer 23 | >>> signer = Signer() 24 | >>> value = signer.sign('My string') 25 | >>> value 26 | 'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w' 27 | ``` 28 | 29 | 这个签名会附加到字符串末尾,跟在冒号后面。你可以使用`unsign`方法来获取原始的值: 30 | 31 | ``` 32 | >>> original = signer.unsign(value) 33 | >>> original 34 | 'My string' 35 | ``` 36 | 37 | 如果签名或者值以任何方式改变,会抛出`django.core.signing.BadSignature` 异常: 38 | 39 | ``` 40 | >>> from django.core import signing 41 | >>> value += 'm' 42 | >>> try: 43 | ... original = signer.unsign(value) 44 | ... except signing.BadSignature: 45 | ... print("Tampering detected!") 46 | ``` 47 | 48 | 通常,`Signer`类使用`SECRET_KEY`设置来生成签名。你可以通过向`Signer`构造器传递一个不同的密钥来使用它: 49 | 50 | ``` 51 | >>> signer = Signer('my-other-secret') 52 | >>> value = signer.sign('My string') 53 | >>> value 54 | 'My string:EkfQJafvGyiofrdGnuthdxImIJw' 55 | ``` 56 | 57 | `class Signer(key=None, sep=':', salt=None)[source]` 58 | 59 | 返回一个`signer`,它使用`key` 来生成签名,并且使用`sep`来分割值。`sep` 不能是 [URL安全的base64字母表(http://tools.ietf.org/html/rfc4648#section-5)]中的字符。字母表含有数字、字母、连字符和下划线。 60 | 61 | ### 使用salt参数 ### 62 | 63 | 如果你不希望对每个特定的字符串都生成一个相同的签名哈希值,你可以在`Signer`类中使用可选的`salt` 参数。使用`salt`参数会同时用它和`SECRET_KEY`初始化签名哈希函数: 64 | 65 | ``` 66 | >>> signer = Signer() 67 | >>> signer.sign('My string') 68 | 'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w' 69 | >>> signer = Signer(salt='extra') 70 | >>> signer.sign('My string') 71 | 'My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw' 72 | >>> signer.unsign('My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw') 73 | 'My string' 74 | ``` 75 | 76 | 以这种方法使用`salt`会把不同的签名放在不同的命名空间中。来自于单一命名空间(一个特定的`salt`值)的签名不能用于在不同的命名空间中验证相同的纯文本字符串。不同的命名空间使用不同的`salt`设置。这是为了防止攻击者使用在一个地方的代码中生成的签名后的字符串,作为使用不同`salt`来生成(和验证)签名的另一处代码的输入。 77 | 78 | 不像你的`SECRET_KEY`,你的`salt`参数可以不用保密。 79 | 80 | ### 验证带有时间戳的值 ### 81 | 82 | `TimestampSigner`是 `Signer`的子类,它向值附加一个签名后的时间戳。这可以让你确认一个签名后的值是否在特定时间段之内被创建: 83 | 84 | ``` 85 | >>> from datetime import timedelta 86 | >>> from django.core.signing import TimestampSigner 87 | >>> signer = TimestampSigner() 88 | >>> value = signer.sign('hello') 89 | >>> value 90 | 'hello:1NMg5H:oPVuCqlJWmChm1rA2lyTUtelC-c' 91 | >>> signer.unsign(value) 92 | 'hello' 93 | >>> signer.unsign(value, max_age=10) 94 | ... 95 | SignatureExpired: Signature age 15.5289158821 > 10 seconds 96 | >>> signer.unsign(value, max_age=20) 97 | 'hello' 98 | >>> signer.unsign(value, max_age=timedelta(seconds=20)) 99 | 'hello' 100 | ``` 101 | 102 | `class TimestampSigner(key=None, sep=':', salt=None)[source]` 103 | 104 | `sign(value)[source]` 105 | 106 | 签名`value`,并且附加当前的时间戳。 107 | 108 | `unsign(value, max_age=None)[source]` 109 | 110 | 检查`value`是否在少于`max_age` 秒之前被签名,如果不是则抛出`SignatureExpired`异常。`max_age` 参数接受一个整数或者`datetime.timedelta`对象。 111 | 112 | ``` 113 | Changed in Django 1.8: 114 | 115 | 在此之前, max_age参数只接受整数。 116 | ``` 117 | 118 | ### 保护复杂的数据结构 ### 119 | 120 | 如果你希望保护一个列表、元组或字典,你可以使用签名模块的`dumps` 和 `loads` 函数来实现。它们模仿了Python的`pickle`模块,但是在背后使用了`JSON`序列化。`JSON`可以确保即使你的`SECRET_KEY`被盗取,攻击者并不能利用`pickle`的格式来执行任意的命令: 121 | 122 | ``` 123 | >>> from django.core import signing 124 | >>> value = signing.dumps({"foo": "bar"}) 125 | >>> value 126 | 'eyJmb28iOiJiYXIifQ:1NMg1b:zGcDE4-TCkaeGzLeW9UQwZesciI' 127 | >>> signing.loads(value) 128 | {'foo': 'bar'} 129 | ``` 130 | 131 | 由于`JSON`的本质(列表和元组之间没有原生的区别),如果你传进来一个元组,你会从`signing.loads(object)`得到一个列表: 132 | 133 | ``` 134 | >>> from django.core import signing 135 | >>> value = signing.dumps(('a','b','c')) 136 | >>> signing.loads(value) 137 | ['a', 'b', 'c'] 138 | ``` 139 | 140 | `dumps(obj, key=None, salt='django.core.signing', compress=False)[source]` 141 | 142 | 返回URL安全,sha1签名的base64压缩的JSON字符串。序列化的对象使用`TimestampSigner`来签名。 143 | 144 | `loads(string, key=None, salt='django.core.signing', max_age=None)[source]` 145 | 146 | `dumps()`的反转,如果签名失败则抛出`BadSignature`异常。如果提供了`max_age`则会检查它(以秒为单位)。 147 | 148 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Cryptographic signing](https://docs.djangoproject.com/en/1.8/topics/signing/)。 149 | > 150 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 151 | > 152 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 153 | -------------------------------------------------------------------------------- /8_Security.md: -------------------------------------------------------------------------------- 1 | # 安全 # 2 | 3 | 安全在Web应用开发中是一项至关重要的话题,Django提供了多种保护手段和机制: 4 | -------------------------------------------------------------------------------- /9_1_1_Overview.md: -------------------------------------------------------------------------------- 1 | # 国际化和本地化 # 2 | 3 | ## 概述 ## 4 | 5 | 国际化和本地化的目的就是让一个网站应用能做到根据用户语种和指定格式的不同而提供不同的内容。 6 | 7 | Django 对文本翻译, 日期、时间和数字的格式化,以及时区提供了完善的支持。 8 | 9 | 实际上,Django做了两件事: 10 | 11 | + 由开发者和模板作者指定应用的哪些部分应该翻译,或是根据本地语种和文化进行相应的格式化。 12 | + 根据用户的偏好设置,使用钩子将web应用本地化。 13 | 14 | 很显然,翻译取决于用户所选语言,而格式化通常取决于用户所在国家。 这些信息由浏览器通过`Accept-Language` 协议头提供。不过确定时区就不是这么简单了。 15 | 16 | ## 定义 ## 17 | 18 | 国际化和本地化通常会被混淆,这里我们对其进行简单的定义和区分: 19 | 20 | **国际化** 21 | 22 | 让软件支持本地化的准备工作,通常由开发者完成。 23 | 24 | **本地化** 25 | 26 | 编写翻译和本地格式,通常由翻译者完成。 27 | 28 | 更多细节详见[W3C Web Internationalization FAQ](http://www.w3.org/International/questions/qa-i18n)、[Wikipedia article](http://en.wikipedia.org/wiki/Internationalization_and_localization)和[GNU gettext documentation](http://www.gnu.org/software/gettext/manual/gettext.html#Concepts)。 29 | 30 | > 警告 31 | > 32 | > 是否启用翻译和格式化分别由配置项`USE_I18N`和 `USE_L10N` 决定。 但是,这两个配置项都同时影响国际化和本地化。 这种情况是Django的历史因素所致。 33 | 34 | 下面几项可帮助我们更好地处理某种语言: 35 | 36 | **本地化名称** 37 | 38 | 表示地域文化的名称,可以是 `ll` 格式的语种代码,也可以是 `ll_CC` 格式的语种和国家组合代码。例如:`it`, `de_AT`, `es`, `pt_BR` 。语种部分总是小写而国家部分则应是大写,中间以下划线(\_)连接。 39 | 40 | **语言代码** 41 | 42 | 表示语言的名称。浏览器会发送带有语言代码的 ``Accept-Language`` HTTP报头给服务器。例如:`it`, `de-at`, `es`, `pt-br`. 语种和国家部分都是小写,中间以破折线(-)连接。 43 | 44 | **消息文件** 45 | 46 | 消息文件是纯文本文件,包含某种语言下所有可用的[翻译字符串](http://python.usyiyi.cn/django/topics/i18n/index.html#term-translation-string)及其对应的翻译结果。消息文件以 `.po` 做为文件扩展名。 47 | 48 | **翻译字符串** 49 | 50 | 可以被翻译的文字。 51 | 52 | **格式文件** 53 | 54 | 针对某个地域定义数据格式的Python模块。 55 | 56 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Overview](https://docs.djangoproject.com/en/1.8/topics/i18n/)。 57 | > 58 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 59 | > 60 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 61 | -------------------------------------------------------------------------------- /9_1_4_Localized Web UI formatting and form input.md: -------------------------------------------------------------------------------- 1 | # 格式本地化 2 | 3 | ## 概览 4 | 5 | Django的格式化系统可以在模板中使用当前[_地区_](index.html#term-locale-name)特定的格式,来展示日期、时间和数字。也可以处理表单中输入的本地化。 6 | 7 | 当它被开启时,访问相同内容的两个用户可能会看到以不同方式格式化的日期、时间和数字,这取决于它们的当前地区的格式。 8 | 9 | 格式化系统默认是禁用的。需要在你的设置文件中设置[`USE_L10N = True`](../../ref/settings.html#std:setting-USE_L10N)来启用它。 10 | 11 | 注意 12 | 13 | 为了方便起见,[`django-admin startproject创建的默认的`settings.py`文件包含了 `](../../ref/django-admin.html#django-admin-startproject)`[`USE_L10N = True`](../../ref/settings.html#std:setting-USE_L10N) 的设置。`但是要注意,要开启千位分隔符的数字格式化,你需要在你的设置文件中设置[`USE_THOUSAND_SEPARATOR = True`](../../ref/settings.html#std:setting-USE_THOUSAND_SEPARATOR)。或者,你也可以在你的模板中使用[`intcomma`](../../ref/contrib/humanize.html#std:templatefilter-intcomma)来格式化数字。 14 | 15 | 注意 16 | 17 | [`USE_I18N`](../../ref/settings.html#std:setting-USE_I18N) 是另一个独立的并且相关的设置,它控制着Django是否应该开启翻译。详见[_翻译_](translation.html)。 18 | 19 | ## 表单中的本地化识别输入 20 | 21 | 格式化开启之后,Django可以在表单中使用本地化格式来解析日期、时间和数字。也就是说,在表单上输入时,它会尝试不同的格式和地区来猜测用户使用的格式。 22 | 23 | 注意 24 | 25 | Django对于展示数据,使用和解析数据不同的格式。尤其是,解析日期的格式不能使用`%a`(星期名称的缩写),`%A` (星期名称的全称),`%b` (月份名称的缩写), `%B`(月份名称的全称),或者`%p`(上午/下午)。 26 | 27 | 只是使用`localize`参数,就能开启表单字段的本地化输入和输出: 28 | 29 | ``` 30 | class CashRegisterForm(forms.Form): 31 | product = forms.CharField() 32 | revenue = forms.DecimalField(max_digits=4, decimal_places=2, localize=True) 33 | 34 | ``` 35 | 36 | ## 在模板中控制本地化 37 | 38 | 当你使用[`USE_L10N`](../../ref/settings.html#std:setting-USE_L10N)来开启格式化的时候,Django会尝试使用地区特定的格式,无论值在模板的什么位置输出。 39 | 40 | 然而,这对于本地化的值不可能总是十分合适,如果你在输出JavaScript或者机器阅读的XML,你会想要使用去本地化的值。你也可能想只在特定的模板中使用本地化,而不是任何位置都使用。 41 | 42 | DJango提供了`l10n`模板库,包含以下标签和过滤器,来实现对本地化的精细控制。 43 | 44 | ### 模板标签 45 | 46 | #### localize 47 | 48 | 在包含的代码块内开启或关闭模板变量的本地化。 49 | 50 | 这个标签可以对本地化进行比[`USE_L10N`](../../ref/settings.html#std:setting-USE_L10N)更加精细的操作。 51 | 52 | 这样做来为一个模板激活或禁用本地化: 53 | 54 | ``` 55 | {% load l10n %} 56 | 57 | {% localize on %} 58 | {{ value }} 59 | {% endlocalize %} 60 | 61 | {% localize off %} 62 | {{ value }} 63 | {% endlocalize %} 64 | 65 | ``` 66 | 67 | 注意 68 | 69 | 在 `{% localize %}`代码块内并不遵循f [`USE_L10N`](../../ref/settings.html#std:setting-USE_L10N)的值。 70 | 71 | 对于在每个变量基础上执行相同工作的模板过滤器,参见[`localize`](#std:templatefilter-localize) 和 [`unlocalize`](#std:templatefilter-unlocalize)。 72 | 73 | ### 模板过滤器 74 | 75 | #### localize 76 | 77 | 强制单一值的本地化。 78 | 79 | 例如: 80 | 81 | ``` 82 | {% load l10n %} 83 | 84 | {{ value|localize }} 85 | 86 | ``` 87 | 88 | 使用[`unlocalize`](#std:templatefilter-unlocalize)来在单一值上禁用本地化。使用[`localize`](#std:templatetag-localize) 模板标签来在大块的模板区域内控制本地化。 89 | 90 | #### unlocalize 91 | 92 | 强制单一值不带本地化输出。 93 | 94 | 例如: 95 | 96 | ``` 97 | {% load l10n %} 98 | 99 | {{ value|unlocalize }} 100 | 101 | ``` 102 | 103 | 使用[`localize`](#std:templatefilter-localize)来强制单一值的本地化。使用[`localize`](#std:templatetag-localize)模板标签来在大块的模板区域内控制本地化。 104 | 105 | ## 创建自定义的格式文件 106 | 107 | Django为许多地区提供了格式定义,但是有时你可能想要创建你自己的格式,因为你的的确并没有现成的格式文件,或者你想要覆写其中的一些值。 108 | 109 | Changed in Django 1.8: 110 | 111 | 添加了指定[`FORMAT_MODULE_PATH`](../../ref/settings.html#std:setting-FORMAT_MODULE_PATH)为列表的功能。之前只支持单一的字符串值。 112 | 113 | 指定你首先放置格式文件的位置来使用自定义格式。把你的[`FORMAT_MODULE_PATH`](../../ref/settings.html#std:setting-FORMAT_MODULE_PATH)设置设置为格式文件存在的包名来使用它,例如: 114 | 115 | ``` 116 | FORMAT_MODULE_PATH = [ 117 | 'mysite.formats', 118 | 'some_app.formats', 119 | ] 120 | 121 | ``` 122 | 123 | 文件并不直接放在这个目录中,而是放在和地区名称相同的目录中,文件也必须名为`formats.py`。 124 | 125 | 需要这样一个结构来自定义英文格式: 126 | 127 | ``` 128 | mysite/ 129 | formats/ 130 | __init__.py 131 | en/ 132 | __init__.py 133 | formats.py 134 | 135 | ``` 136 | 137 | 其中`formats.py`包含自定义的格式定义。例如: 138 | 139 | ``` 140 | from __future__ import unicode_literals 141 | 142 | THOUSAND_SEPARATOR = '\xa0' 143 | 144 | ``` 145 | 146 | 使用非间断空格(Unicode `00A0`)作为千位分隔符,来代替英语中默认的逗号。 147 | 148 | ## 提供本地化格式的限制 149 | 150 | 一些地区对数字使用上下文敏感的格式,Django的本地化系统不能自动处理它。 151 | 152 | ### 瑞士(德语) 153 | 154 | 瑞士的数字格式化取决于被格式化的数字类型。对于货币值,使用逗号作为千位分隔符,以及使用小数点作为十进制分隔符。对于其它数字,逗号用于十进制分隔符,空格用于千位分隔符。Django提供的本地格式使用通用的分隔符,即逗号用于十进制分隔符,空格用于千位分隔符。 155 | 156 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[ocalized Web UI formatting and form input](https://docs.djangoproject.com/en/1.8/topics/i18n/formatting/)。 157 | > 158 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 159 | > 160 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 161 | -------------------------------------------------------------------------------- /9_2_“Local flavor”.md: -------------------------------------------------------------------------------- 1 | # "本地特色"附加功能 2 | 3 | 由于历史因素,Django自带了`django.contrib.localflavor` -- 各种各样的代码片段,有助于在特定的国家地区或文化中使用。为了便于维护以及减少Django代码库的体积,这些代码现在在Django之外单独发布。 4 | 5 | 详见官方文档: 6 | 7 | > [https://django-localflavor.readthedocs.org/](https://django-localflavor.readthedocs.org/) 8 | 9 | 这些代码托管在GIthub上面,[https://github.com/django/django-localflavor](https://github.com/django/django-localflavor)。 10 | 11 | ## 如何迁移 12 | 13 | 如果你使用了老版本的`django.contrib.localflavor`包,或者 `django-localflavor-*` 的模板之一,执行这两个简单的步骤就可以更新你的代码: 14 | 15 | * 在PyPI中安装第三方的`django-localflavor` 包。 16 | 17 | * 修改你应用的导入语句来引用新的包。 18 | 19 | 例如,将: 20 | 21 | ``` 22 | from django.contrib.localflavor.fr.forms import FRPhoneNumberField 23 | 24 | ``` 25 | 26 | ...改为: 27 | 28 | ``` 29 | from localflavor.fr.forms import FRPhoneNumberField 30 | 31 | ``` 32 | 33 | 新的包中的代码和以前一样(它是直接从Django中复制出来的),所以你并不用担心功能上的向后兼容问题。只需要修改导入语句。 34 | 35 | ## 弃用政策 36 | 37 | 在 Django 1.5中,导入`django.contrib.localflavor`会产生 `DeprecationWarning`异常。也就是说你的代码还可以继续工作,但是你应该尽快修改它。 38 | 39 | 在Django 1.6中,导入 `django.contrib.localflavor`将不会继续工作。 40 | 41 | > 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[“Local flavor”](https://docs.djangoproject.com/en/1.8/topics/localflavor/)。 42 | > 43 | > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 44 | > 45 | > [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 46 | -------------------------------------------------------------------------------- /9_Internationalization and localization.md: -------------------------------------------------------------------------------- 1 | # 国际化和本地化 # 2 | 3 | Django 提供了一种健壮的国际化和本地化框架来帮助你实现多种语言和世界区域范围的开发。 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django 中文文档 1.8 # 2 | 3 | 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),来源:[Django documentation](https://docs.djangoproject.com/en/1.8/),最后更新:2017.2.15。 4 | 5 | 本文档以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。 6 | 7 | [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。 8 | 9 | ## 下载 ## 10 | 11 | + [在线阅读](https://www.gitbook.com/book/wizardforcel/django-chinese-docs-18/details) 12 | + [PDF下载](https://www.gitbook.com/download/pdf/book/wizardforcel/django-chinese-docs-18) 13 | + [EPUB下载](https://www.gitbook.com/download/epub/book/wizardforcel/django-chinese-docs-18) 14 | + [MOBI下载](https://www.gitbook.com/download/mobi/book/wizardforcel/django-chinese-docs-18) 15 | + [代码仓库](https://github.com/wizardforcel/django-chinese-docs-18) 16 | 17 | ## 赞助我们 18 | 19 | ![](img/usyiyi_qr_alipay.png) 20 | 21 | ![](img/usyiyi_qr_wechat.png) -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | + [新手入门](1_First steps.md) 2 | + [从零开始](1_1.md) 3 | + [概览](1_1_1_Django at a glance.md) 4 | + [安装](1_1_2_Quick install guide.md) 5 | + [教程](1_2.md) 6 | + [第1部分:模型](1_2_1_Part 1 Models.md) 7 | + [第2部分:管理站点](1_2_2_Part 2 The admin site.md) 8 | + [第3部分:视图和模板](1_2_3_Part 3 Views and templates.md) 9 | + [第4部分:表单和通用视图](1_2_4_Part 4 Forms and generic views.md) 10 | + [第5部分:测试](1_2_5_Part 5 Testing.md) 11 | + [第6部分:静态文件](1_2_6_Part 6 Static files.md) 12 | + [高级教程](1_3.md) 13 | + [如何编写可重用的应用 ](1_3_1_How to write reusable apps.md) 14 | + [为Django编写首个补丁](1_3_2_Writing your first patch for Django.md) 15 | + [模型层](2_The model layer.md) 16 | + [模型](2_1.md) 17 | + [模型语法](2_1_1_Model syntax.md) 18 | + [字段类型](2_1_2_Field types.md) 19 | + [元选项](2_1_3_Meta options.md) 20 | + [模型类](2_1_4_Model class.md) 21 | + [查询集](2_2.md) 22 | + [执行查询](2_2_1_Making queries.md) 23 | + [查询集方法参考](2_2_2_QuerySet method reference.md) 24 | + [查找表达式](2_2_3_Lookup expressions.md) 25 | + [模型的实例](2_3.md) 26 | + [实例方法](2_3_1_Instance methods.md) 27 | + [访问关联对象](2_3_2_Accessing related objects.md) 28 | + [迁移](2_4.md) 29 | + [迁移简介](2_4_1_Introduction to Migrations.md) 30 | + [操作参考](2_4_2_Operations reference.md) 31 | + [模式编辑器](2_4_3_SchemaEditor.md) 32 | + [编写迁移](2_4_4_Writing migrations.md) 33 | + [高级](2_5.md) 34 | + [管理器](2_5_1_Manager.md) 35 | + [原始的SQL查询](2_5_2_Performing raw SQL queries.md) 36 | + [事务](2_5_3_Transactions.md) 37 | + [聚合](2_5_4_Aggregation.md) 38 | + [自定义字段](2_5_5_Custom fields.md) 39 | + [多数据库](2_5_6_Multiple databases.md) 40 | + [自定义查找](2_5_7_Custom lookups.md) 41 | + [查询表达式](2_5_8_Query Expressions.md) 42 | + [条件表达式](2_5_9_Conditional Expressions.md) 43 | + [数据库函数](2_5_10_Database Functions.md) 44 | + [其它](2_6.md) 45 | + [支持的数据库](2_6_1_Supported databases.md) 46 | + [遗留的数据库](2_6_2_Legacy databases.md) 47 | + [提供初始数据](2_6_3_Providing initial data.md) 48 | + [优化数据库访问](2_6_4_Optimize database access.md) 49 | + [PostgreSQL特色功能]() 50 | + [视图层](3_The view layer.md) 51 | + [基础](3_1.md) 52 | + [URL配置](3_1_1_URLconfs.md) 53 | + [视图函数](3_1_2_View functions.md) 54 | + [快捷函数](3_1_3_Shortcuts.md) 55 | + [装饰器](3_1_4_Decorators.md) 56 | + [参考](3_2.md) 57 | + [内建的视图](3_2_1_Built-in Views.md) 58 | + [请求/响应 对象](3_2_2_Request response objects.md) 59 | + [TemplateResponse 对象](3_2_3_TemplateResponse objects.md) 60 | + [TemplateResponse 和SimpleTemplateResponse](3_2_4_TemplateResponse objects.md) 61 | + [文件上传](3_3.md) 62 | + [概览](3_3_1_Overview.md) 63 | + [File 对象](3_3_2_File objects.md) 64 | + [储存API](3_3_3_Storage API.md) 65 | + [管理文件](3_3_4_Managing files.md) 66 | + [自定义存储](3_3_5_Custom storage.md) 67 | + [基于类的视图](3_4.md) 68 | + [概览](3_4_1_Overview.md) 69 | + [内建显示视图](3_4_2_Built-in display views.md) 70 | + [内建编辑视图](3_4_3_Built-in editing views.md) 71 | + [使用Mixin](3_4_4_Using mixins.md) 72 | + [API参考](3_4_5_API reference.md) 73 | + [分类索引](3_4_6_Flattened index.md) 74 | + [高级](3_5.md) 75 | + [生成 CSV](3_5_1_Generating CSV.md) 76 | + [生成 PDF](3_5_2_Generating PDF.md) 77 | + [中间件](3_6.md) 78 | + [概览](3_6_1_Overview.md) 79 | + [内建的中间件类](3_6_2_Built-in middleware classes.md) 80 | + [模板层](4_The template layer.md) 81 | + [基础](4_1.md) 82 | + [概览](4_1_1_Overview.md) 83 | + [面向设计师](4_2.md) 84 | + [语言概览](4_2_1_Language overview.md) 85 | + [内建标签和过滤器](4_2_2_Built-in tags and filters.md) 86 | + [网页设计助手(已废弃)](4_2_3_Web design helpers.md) 87 | + [人性化](4_2_4_Humanization.md) 88 | + [面向程序员](4_3.md) 89 | + [模板API](4_3_1_Template API.md) 90 | + [自定义标签和过滤器](4_3_2_Custom tags and filters.md) 91 | + [表单](5_Forms.md) 92 | + [基础](5_1.md) 93 | + [概览](5_1_1_Overview.md) 94 | + [表单API](5_1_2_Form API.md) 95 | + [内建的字段](5_1_3_Built-in widgets.md) 96 | + [内建的Widget](5_1_4_Built-in widgets.md) 97 | + [高级](5_2.md) 98 | + [模型表单](5_2_1_Forms for models.md) 99 | + [整合媒体](5_2_2_Integrating media.md) 100 | + [表单集](5_2_3_Formsets.md) 101 | + [自定义验证](5_2_4_Customizing validation.md) 102 | + [开发过程](6_The development process.md) 103 | + [设置](6_1.md) 104 | + [概览](6_1_1_Overview.md) 105 | + [完整设置列表](6_1_2_Full list of settings.md) 106 | + [应用程序](6_2.md) 107 | + [概览](6_2_Overview.md) 108 | + [异常](6_3.md) 109 | + [概览](6_3_Overview.md) 110 | + [django-admin 和 manage.py](6_4.md) 111 | + [概览](6_4_1_Overview.md) 112 | + [添加自定义的命令](6_4_2_Adding custom commands.md) 113 | + [测试](6_5.md) 114 | + [介绍](6_5_1_Introduction.md) 115 | + [编写并运行测试](6_5_2_Writing and running tests.md) 116 | + [包含的测试工具](6_5_3_Included testing tools.md) 117 | + [高级主题](6_5_4_Advanced topics.md) 118 | + [部署](6_6.md) 119 | + [概述](6_6_1_Overview.md) 120 | + [WSGI服务器](6_6_2_WSGI servers.md) 121 | + [FastCGI/SCGI/AJP (已废弃)](6_6_3_FastCGI SCGI AJP.md) 122 | + [部署静态文件](6_6_4_Deploying static files.md) 123 | + [通过email追踪代码错误](6_6_5_Tracking code errors by email.md) 124 | + [Admin](7_Admin.md) 125 | + [管理站点](7_1_Admin site.md) 126 | + [管理操作](7_2_Admin actions.md) 127 | + [管理文档生成器](7_3_Admin documentation generator.md) 128 | + [安全](8_Security.md) 129 | + [安全概述](8_1_Security overview.md) 130 | + [说明Django中的安全问题](8_2_Disclosed security issues in Django.md) 131 | + [点击劫持保护](8_3_Clickjacking protection.md) 132 | + [伪造跨站请求保护](8_4_Cross Site Request Forgery protection.md) 133 | + [加密签名](8_5_Cryptographic signing.md) 134 | + [安全中间件](8_6_Security Middleware.md) 135 | + [国际化和本地化](9_Internationalization and localization.md) 136 | + [概述](9_1_1_Overview.md) 137 | + [国际化](9_1_2_Internationalization.md) 138 | + [本地化WEB UI格式化输入](9_1_4_Localized Web UI formatting and form input.md) 139 | + [“本地特色”](9_2_“Local flavor”.md) 140 | + [时区](9_3_Time zones.md) 141 | + [性能和优化](10_Performance and optimization overview.md) 142 | + [Python 的兼容性](11_Python compatibility.md) 143 | + [Jython 支持](11_1_Jython support.md) 144 | + [Python 3 兼容性](11_2_Python 3 compatibility.md) 145 | + [常见的网站应用工具](13_Common Web application tools.md) 146 | + [认证](13_1.md) 147 | + [概览](13_1_1_Overview.md) 148 | + [使用认证系统](13_1_2_Using the authentication system.md) 149 | + [密码管理](13_1_3_Password management.md) 150 | + [自定义认证](13_1_4_Customizing authentication.md) 151 | + [API参考](13_1_5_API Reference.md) 152 | + [缓存](13_2_Caching.md) 153 | + [日志](13_3_Logging.md) 154 | + [发送邮件](13_4_Sending emails.md) 155 | + [组织 feeds (RSS/Atom)](13_5_Syndication feeds.md) 156 | + [分页](13_6_Pagination.md) 157 | + [消息框架](13_7_Messages framework.md) 158 | + [序列化](13_8_Serialization.md) 159 | + [会话](13_9_1_Sessions.md) 160 | + [网站地图](13_10_Sitemaps.md) 161 | + [静态文件处理](13_11_Static files management.md) 162 | + [数据验证](13_12_Data validation.md) 163 | + [其它核心功能](14_Other core functionalities.md) 164 | + [按需内容处理](14_1_Conditional content processing.md) 165 | + [内容类型和泛型关系](14_2_Content types and generic relations.md) 166 | + [Flatpage](14_3_Flatpages.md) 167 | + [重定向](14_4_1_Redirects.md) 168 | + [信号](14_5_Signals.md) 169 | + [系统检查框架](14_6_System check framework.md) 170 | + [网站框架](14_7_The sites framework.md) 171 | + [Django中的Unicode编码](14_8_Unicode in Django.md) 172 | -------------------------------------------------------------------------------- /img/usyiyi_qr_alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usyiyi/django-chinese-docs-18/44acbcf7bfd8cb8d1941c891992aeed5f3103c7c/img/usyiyi_qr_alipay.png -------------------------------------------------------------------------------- /img/usyiyi_qr_wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usyiyi/django-chinese-docs-18/44acbcf7bfd8cb8d1941c891992aeed5f3103c7c/img/usyiyi_qr_wechat.png -------------------------------------------------------------------------------- /styles/ebook.css: -------------------------------------------------------------------------------- 1 | /* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */ 2 | /* Author: Nicolas Hery - http://nicolashery.com */ 3 | /* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */ 4 | /* Source: https://github.com/nicolahery/markdownpad-github */ 5 | 6 | /* RESET 7 | =============================================================================*/ 8 | 9 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 10 | margin: 0; 11 | padding: 0; 12 | border: 0; 13 | } 14 | 15 | /* BODY 16 | =============================================================================*/ 17 | 18 | body { 19 | font-family: Helvetica, arial, freesans, clean, sans-serif; 20 | font-size: 14px; 21 | line-height: 1.6; 22 | color: #333; 23 | background-color: #fff; 24 | padding: 20px; 25 | max-width: 960px; 26 | margin: 0 auto; 27 | } 28 | 29 | body>*:first-child { 30 | margin-top: 0 !important; 31 | } 32 | 33 | body>*:last-child { 34 | margin-bottom: 0 !important; 35 | } 36 | 37 | /* BLOCKS 38 | =============================================================================*/ 39 | 40 | p, blockquote, ul, ol, dl, table, pre { 41 | margin: 15px 0; 42 | } 43 | 44 | /* HEADERS 45 | =============================================================================*/ 46 | 47 | h1, h2, h3, h4, h5, h6 { 48 | margin: 20px 0 10px; 49 | padding: 0; 50 | font-weight: bold; 51 | -webkit-font-smoothing: antialiased; 52 | } 53 | 54 | h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code { 55 | font-size: inherit; 56 | } 57 | 58 | h1 { 59 | font-size: 24px; 60 | border-bottom: 1px solid #ccc; 61 | color: #000; 62 | } 63 | 64 | h2 { 65 | font-size: 18px; 66 | color: #000; 67 | } 68 | 69 | h3 { 70 | font-size: 14px; 71 | } 72 | 73 | h4 { 74 | font-size: 14px; 75 | } 76 | 77 | h5 { 78 | font-size: 14px; 79 | } 80 | 81 | h6 { 82 | color: #777; 83 | font-size: 14px; 84 | } 85 | 86 | body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child { 87 | margin-top: 0; 88 | padding-top: 0; 89 | } 90 | 91 | a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 { 92 | margin-top: 0; 93 | padding-top: 0; 94 | } 95 | 96 | h1+p, h2+p, h3+p, h4+p, h5+p, h6+p { 97 | margin-top: 10px; 98 | } 99 | 100 | /* LINKS 101 | =============================================================================*/ 102 | 103 | a { 104 | color: #4183C4; 105 | text-decoration: none; 106 | } 107 | 108 | a:hover { 109 | text-decoration: underline; 110 | } 111 | 112 | /* LISTS 113 | =============================================================================*/ 114 | 115 | ul, ol { 116 | padding-left: 30px; 117 | } 118 | 119 | ul li > :first-child, 120 | ol li > :first-child, 121 | ul li ul:first-of-type, 122 | ol li ol:first-of-type, 123 | ul li ol:first-of-type, 124 | ol li ul:first-of-type { 125 | margin-top: 0px; 126 | } 127 | 128 | ul ul, ul ol, ol ol, ol ul { 129 | margin-bottom: 0; 130 | } 131 | 132 | dl { 133 | padding: 0; 134 | } 135 | 136 | dl dt { 137 | font-size: 14px; 138 | font-weight: bold; 139 | font-style: italic; 140 | padding: 0; 141 | margin: 15px 0 5px; 142 | } 143 | 144 | dl dt:first-child { 145 | padding: 0; 146 | } 147 | 148 | dl dt>:first-child { 149 | margin-top: 0px; 150 | } 151 | 152 | dl dt>:last-child { 153 | margin-bottom: 0px; 154 | } 155 | 156 | dl dd { 157 | margin: 0 0 15px; 158 | padding: 0 15px; 159 | } 160 | 161 | dl dd>:first-child { 162 | margin-top: 0px; 163 | } 164 | 165 | dl dd>:last-child { 166 | margin-bottom: 0px; 167 | } 168 | 169 | /* CODE 170 | =============================================================================*/ 171 | 172 | pre, code, tt { 173 | font-size: 12px; 174 | font-family: Consolas, "Liberation Mono", Courier, monospace; 175 | } 176 | 177 | code, tt { 178 | margin: 0 0px; 179 | padding: 0px 0px; 180 | white-space: nowrap; 181 | border: 1px solid #eaeaea; 182 | background-color: #f8f8f8; 183 | border-radius: 3px; 184 | } 185 | 186 | pre>code { 187 | margin: 0; 188 | padding: 0; 189 | white-space: pre; 190 | border: none; 191 | background: transparent; 192 | } 193 | 194 | pre { 195 | background-color: #f8f8f8; 196 | border: 1px solid #ccc; 197 | font-size: 13px; 198 | line-height: 19px; 199 | overflow: auto; 200 | padding: 6px 10px; 201 | border-radius: 3px; 202 | } 203 | 204 | pre code, pre tt { 205 | background-color: transparent; 206 | border: none; 207 | } 208 | 209 | kbd { 210 | -moz-border-bottom-colors: none; 211 | -moz-border-left-colors: none; 212 | -moz-border-right-colors: none; 213 | -moz-border-top-colors: none; 214 | background-color: #DDDDDD; 215 | background-image: linear-gradient(#F1F1F1, #DDDDDD); 216 | background-repeat: repeat-x; 217 | border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD; 218 | border-image: none; 219 | border-radius: 2px 2px 2px 2px; 220 | border-style: solid; 221 | border-width: 1px; 222 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; 223 | line-height: 10px; 224 | padding: 1px 4px; 225 | } 226 | 227 | /* QUOTES 228 | =============================================================================*/ 229 | 230 | blockquote { 231 | border-left: 4px solid #DDD; 232 | padding: 0 15px; 233 | color: #777; 234 | } 235 | 236 | blockquote>:first-child { 237 | margin-top: 0px; 238 | } 239 | 240 | blockquote>:last-child { 241 | margin-bottom: 0px; 242 | } 243 | 244 | /* HORIZONTAL RULES 245 | =============================================================================*/ 246 | 247 | hr { 248 | clear: both; 249 | margin: 15px 0; 250 | height: 0px; 251 | overflow: hidden; 252 | border: none; 253 | background: transparent; 254 | border-bottom: 4px solid #ddd; 255 | padding: 0; 256 | } 257 | 258 | /* TABLES 259 | =============================================================================*/ 260 | 261 | table th { 262 | font-weight: bold; 263 | } 264 | 265 | table th, table td { 266 | border: 1px solid #ccc; 267 | padding: 6px 13px; 268 | } 269 | 270 | table tr { 271 | border-top: 1px solid #ccc; 272 | background-color: #fff; 273 | } 274 | 275 | table tr:nth-child(2n) { 276 | background-color: #f8f8f8; 277 | } 278 | 279 | /* IMAGES 280 | =============================================================================*/ 281 | 282 | img { 283 | max-width: 100% 284 | } --------------------------------------------------------------------------------