提交 a6a2826f 编写于 作者: W wizardforcel

update 2016.11.27

上级 84c6063b
# 消息框架
在网页应用中,你经常需要在处理完表单或其它类型的用户输入后,显示一个通知消息(也叫做“flash message”)给用户。
对于这个功能,Django 提供基于Cookie 和会话的消息,无论是匿名用户还是认证的用户。其消息框架允许你临时将消息存储在请求中,并在接下来的请求(通常就是下一个请求)中提取它们并显示。每个消息都带有一个特定`level` 标签,表示其优先级(例如`info``warning``error`)。
## 启用消息框架
消息框架的实现通过一个[_中间件_](../middleware.html) 类和对应的[_context processor_](../templates/api.html)
`django-admin startproject` 创建的默认`settings.py` ?已经包含启用消息框架功能需要的所有的设置:
* [`INSTALLED_APPS`](../settings.html#std:setting-INSTALLED_APPS) 中的`'django.contrib.messages'`
* [`MIDDLEWARE_CLASSES`](../settings.html#std:setting-MIDDLEWARE_CLASSES) 中的`'django.contrib.sessions.middleware.SessionMiddleware'``'django.contrib.messages.middleware.MessageMiddleware'`
默认的[_后端存储_](#message-storage-backends) 依赖[_sessions_](../../topics/http/sessions.html)。所以[`MIDDLEWARE_CLASSES`](../settings.html#std:setting-MIDDLEWARE_CLASSES) 中必须启用`SessionMiddleware` 并出现在`MessageMiddleware` 之前。
* [`TEMPLATES`](../settings.html#std:setting-TEMPLATES) 设置中定义的`DjangoTemplates``'context_processors'` 选项包含 `'django.contrib.messages.context_processors.messages'`
如果你不想使用消息框架,你可以删除[`INSTALLED_APPS`](../settings.html#std:setting-INSTALLED_APPS) 中的 `'django.contrib.messages'`[`MIDDLEWARE_CLASSES`](../settings.html#std:setting-MIDDLEWARE_CLASSES) 中的`MessageMiddleware`[`TEMPLATES`](../settings.html#std:setting-TEMPLATES) 中的`messages` context processor。
## 配置消息框架引擎
### 后台存储
消息框架可以使用不同的后台存储临时消息。
Django 在[`django.contrib.messages`](#module-django.contrib.messages "django.contrib.messages: Provides cookie- and session-based temporary message storage.") 中提供三个内建的存储类:
_class_ `storage.session.SessionStorage`
这个类存储所有的消息于请求的会话中。因此,它要求启用Django 的`contrib.sessions` 应用。
_class_ `storage.cookie.CookieStorage`
这个类存储消息数据于与Cookie 中(已经用一个安全的哈希进行签名以防止篡改)以在请求之间传递消息。如果Cookie 数据的大小将超过2048 字节,将丢弃旧的消息。
_class_ `storage.fallback.FallbackStorage`
这个类首先使用`CookieStorage`,如果消息塞不进一个Cookie 中则使用`SessionStorage`。 它同样要求启用Django 的`contrib.sessions` 应用。
这个行为避免每次都写会话。在通常情况下,它提供的性能应该是最好的。
[`FallbackStorage`](#django.contrib.messages.storage.fallback.FallbackStorage "django.contrib.messages.storage.fallback.FallbackStorage") 是默认的存储类。如果它不适合你的需要,你可以通过设置 [`MESSAGE_STORAGE`](../settings.html#std:setting-MESSAGE_STORAGE) 为它的完整导入路径选择另外一个存储类,例如:
```
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
```
_class_ `storage.base.BaseStorage`
如果想编写你自己的存储类,子类化`django.contrib.messages.storage.base` 中的`BaseStorage` 类并实现`_get``_store` 方法。
### 消息级别
消息框架的级别是可配置的,与Python logging 模块类似。消息的级别可以让你根据类型进行分组,这样它们能够在不同的视图和模板中过滤或显示出来。
可以直接从`django.contrib.messages` 导入的内建级别有:
| Constant | Purpose |
| --- | --- |
| `DEBUG` | Development-related messages that will be ignored (or removed) in a production deployment |
| `INFO` | Informational messages for the user |
| `SUCCESS` | An action was successful, e.g. “Your profile was updated successfully” |
| `WARNING` | A failure did not occur but may be imminent |
| `ERROR` | An action was **not** successful or some other failure occurred |
[`MESSAGE_LEVEL`](../settings.html#std:setting-MESSAGE_LEVEL) 设置可以用来改变记录的最小级别(它还可以[在每个请求中修改](#changing-the-minimum-recorded-level-per-request))。小于这个级别的消息将被忽略。
### 消息的标签
消息的标签是一个字符串,表示消息的级别以及在视图中添加的其它标签(参见下文[添加额外的消息标签](#adding-extra-message-tags))。标签存储在字符串中并通过空格分隔。通常情况下,消息的标签用于作为CSS 类来根据消息的类型定制消息的风格。默认情况下,每个级别具有一个标签,为其级别的字符串常量的小写:
| Level Constant | Tag |
| --- | --- |
| `DEBUG` | `debug` |
| `INFO` | `info` |
| `SUCCESS` | `success` |
| `WARNING` | `warning` |
| `ERROR` | `error` |
若要修改消息级别的默认标签,设置[`MESSAGE_TAGS`](../settings.html#std:setting-MESSAGE_TAGS)为包含你想要修改的级别的字典。As this extends the default tags, you only need to provide tags for the levels you wish to override:
```
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.INFO: '',
50: 'critical',
}
```
## 在视图和模板中使用消息
`add_message`(_request_, _level_, _message_, _extra_tags=''_, _fail_silently=False_)
### 新增一条消息
新增一条消息,调用:
```
from django.contrib import messages
messages.add_message(request, messages.INFO, 'Hello world.')
```
有几个快捷方法提供标准的方式来新增消息并带有常见的标签(这些标签通常表示消息的HTML 类型):
```
messages.debug(request, '%s SQL statements were executed.' % count)
messages.info(request, 'Three credits remain in your account.')
messages.success(request, 'Profile details updated.')
messages.warning(request, 'Your account expires in three days.')
messages.error(request, 'Document deleted.')
```
### 显示消息
`get_messages`(_request_)
**在你的模板中**,像下面这样使用:
```
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
```
如果你正在使用context processor,你的模板应该通过 `RequestContext` 渲染。否则,需要确保`messages` 在模板的Context 中可以访问。
即使你知道只有一条消息,你也应该仍然迭代`messages` 序列,否则下个请求中的消息不会被清除。
New in Django 1.7.
Context processor 还提供一个`DEFAULT_MESSAGE_LEVELS` 变量,它映射消息级别的名称到它们的数值:
```
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
{% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %}
{{ message }}
</li>
{% endfor %}
</ul>
{% endif %}
```
**在模板的外面**,你可以使用[`get_messages()`](#django.contrib.messages.get_messages "django.contrib.messages.get_messages")
```
from django.contrib.messages import get_messages
storage = get_messages(request)
for message in storage:
do_something_with_the_message(message)
```
例如,你可以获取所有的消息并在[_JSONResponseMixin_](../../topics/class-based-views/mixins.html#jsonresponsemixin-example) 而不是[`TemplateResponseMixin`](../class-based-views/mixins-simple.html#django.views.generic.base.TemplateResponseMixin "django.views.generic.base.TemplateResponseMixin") 中返回它们。
[`get_messages()`](#django.contrib.messages.get_messages "django.contrib.messages.get_messages") 将返回配置的存储后台的一个实例。
### The `Message`
_class_ `storage.base.Message`
当浏览某个模板的消息列表时,你得到的其实是`Message` 类的实例。它只是一个非常简单、只带很少属性的对象:
* `message`: 消息的实际内容文本。
* `level`: 一个整数,它描述了消息的类型 (请参阅上面 [message levels](#message-levels)一节).
* `tags`: 一个字符串,它由该消息的所有标签 (`extra_tags``level_tag`)组合而成,组合时用空格分割开这些标签。
* `extra_tags`: 一个字符串,它由该消息的定制标签组合而成,并用空格分割。缺省为空。
New in Django 1.7.
* `level_tag`: 代表该消息级别的字符串。该属性缺省由小写的关联常数名组成, 但当设置[`MESSAGE_TAGS`](../settings.html#std:setting-MESSAGE_TAGS)参数时,可改变该规则。
### Creating custom message levels
Messages levels are nothing more than integers, so you can define your own level constants and use them to create more customized user feedback, e.g.:
```
CRITICAL = 50
def my_view(request):
messages.add_message(request, CRITICAL, 'A serious error occurred.')
```
When creating custom message levels you should be careful to avoid overloading existing levels. The values for the built-in levels are:
| Level Constant | Value |
| --- | --- |
| `DEBUG` | 10 |
| `INFO` | 20 |
| `SUCCESS` | 25 |
| `WARNING` | 30 |
| `ERROR` | 40 |
If you need to identify the custom levels in your HTML or CSS, you need to provide a mapping via the [`MESSAGE_TAGS`](../settings.html#std:setting-MESSAGE_TAGS) setting.
Note
If you are creating a reusable application, it is recommended to use only the built-in [message levels](#message-levels) and not rely on any custom levels.
### 在每个请求中修改最小的记录级别
每个请求都可以通过 `set_level`方法设置最小记录级别:
```
from django.contrib import messages
# Change the messages level to ensure the debug message is added.
messages.set_level(request, messages.DEBUG)
messages.debug(request, 'Test message...')
# In another request, record only messages with a level of WARNING and higher
messages.set_level(request, messages.WARNING)
messages.success(request, 'Your profile was updated.') # ignored
messages.warning(request, 'Your account is about to expire.') # recorded
# Set the messages level back to default.
messages.set_level(request, None)
```
与此相似,当前有效的记录级别可以用`get_level`方法获取:
```
from django.contrib import messages
current_level = messages.get_level(request)
```
有关最小记录级别相关的函数信息,请参阅上面 [Message levels](#message-levels) 一节.
### Adding extra message tags
For more direct control over message tags, you can optionally provide a string containing extra tags to any of the add methods:
```
messages.add_message(request, messages.INFO, 'Over 9000!',
extra_tags='dragonball')
messages.error(request, 'Email box full', extra_tags='email')
```
Extra tags are added before the default tag for that level and are space separated.
### 当消息框架被禁止时,失败静悄悄
If you’re writing a reusable app (or other piece of code) and want to include messaging functionality, but don’t want to require your users to enable it if they don’t want to, you may pass an additional keyword argument `fail_silently=True` to any of the `add_message` family of methods. 举个例子
```
messages.add_message(request, messages.SUCCESS, 'Profile details updated.',
fail_silently=True)
messages.info(request, 'Hello world.', fail_silently=True)
```
Note
Setting `fail_silently=True` only hides the `MessageFailure` that would otherwise occur when the messages framework disabled and one attempts to use one of the `add_message` family of methods. It does not hide failures that may occur for other reasons.
### 在基于类的视图中添加消息
_class_ `views.SuccessMessageMixin`
向基于[`FormView`](../class-based-views/generic-editing.html#django.views.generic.edit.FormView "django.views.generic.edit.FormView") 的类添加一条成功的消息
`get_success_message`(_cleaned_data_)
`cleaned_data` 是表单中的清洁数据,用于字符串格式化
**示例 views.py**
```
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(SuccessMessageMixin, CreateView):
model = Author
success_url = '/success/'
success_message = "%(name)s was created successfully"
```
字符串插值可以使用`%(field_name)s` 语法访问`form` 中的清洁数据。对于ModelForms,如果你需要访问保存的`object` 中的字段,可以覆盖[`get_success_message()`](#django.contrib.messages.views.SuccessMessageMixin.get_success_message "django.contrib.messages.views.SuccessMessageMixin.get_success_message") 方法。
**ModelForms 的示例views.py**
```
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import ComplicatedModel
class ComplicatedCreate(SuccessMessageMixin, CreateView):
model = ComplicatedModel
success_url = '/success/'
success_message = "%(calculated_field)s was created successfully"
def get_success_message(self, cleaned_data):
return self.success_message % dict(cleaned_data,
calculated_field=self.object.calculated_field)
```
## Expiration of messages
The messages are marked to be cleared when the storage instance is iterated (and cleared when the response is processed).
To avoid the messages being cleared, you can set the messages storage to `False` after iterating:
```
storage = messages.get_messages(request)
for message in storage:
do_something_with(message)
storage.used = False
```
## Behavior of parallel requests
Due to the way cookies (and hence sessions) work, **the behavior of any backends that make use of cookies or sessions is undefined when the same client makes multiple requests that set or get messages in parallel**. For example, if a client initiates a request that creates a message in one window (or tab) and then another that fetches any uniterated messages in another window, before the first window redirects, the message may appear in the second window instead of the first window where it may be expected.
In short, when multiple simultaneous requests from the same client are involved, messages are not guaranteed to be delivered to the same window that created them nor, in some cases, at all. Note that this is typically not a problem in most applications and will become a non-issue in HTML5, where each window/tab will have its own browsing context.
## 设置
A few [_settings_](../settings.html#settings-messages) give you control over message behavior:
* [MESSAGE_LEVEL](../settings.html#std:setting-MESSAGE_LEVEL)
* [MESSAGE_STORAGE](../settings.html#std:setting-MESSAGE_STORAGE)
* [MESSAGE_TAGS](../settings.html#std:setting-MESSAGE_TAGS)
New in Django 1.7.
For backends that use cookies, the settings for the cookie are taken from the session cookie settings:
* [SESSION_COOKIE_DOMAIN](../settings.html#std:setting-SESSION_COOKIE_DOMAIN)
* [SESSION_COOKIE_SECURE](../settings.html#std:setting-SESSION_COOKIE_SECURE)
* [SESSION_COOKIE_HTTPONLY](../settings.html#std:setting-SESSION_COOKIE_HTTPONLY)
此差异已折叠。
此差异已折叠。
# 为Django编写首个补丁 #
## 介绍 ##
有兴趣为社区做出点贡献吗?也许你会在Django中发现你想要修复的漏洞,或者你希望为它添加一个小特征。
为Django作贡献这件事本身就是使你的顾虑得到解决的最好方式。一开始这可能会使你怯步,但事实上是很简单的。整个过程中我们会一步一步为你解说,所以你可以通过例子学习。
## Who’s this tutorial for? ##
使用教程前,我们希望你至少对于Django的运行方式有基础的了解。这意味着你可以自如地在写你自己的Django app时使用教程。 除此之外,你应该对于Python本身有很好的了解。如果您并不太了解, 我们为您推荐Dive Into Python,对于初次使用Python的程序员来说这是一本很棒(而且免费)的在线电子书。
对于版本控制系统及Trac不熟悉的人来说,这份教程及其中的链接所包含的信息足以满足你们开始学习的需求。然而,如果你希望定期为Django贡献,你可能会希望阅读更多关于这些不同工具的信息。
当然对于其中的大部分内容,Django会尽可能做出解释以帮助广大的读者。
> 何处获得帮助:
>
> 如果你在使用本教程时遇到困难,你可以发送信息给django开发者 或者登陆 #django-dev on irc.freenode.net 向其他Django使用者需求帮助。
## 教程包含的内容 ##
一开始我们会帮助你为Django编写补丁,在教程结束时,你将具备对于工具和所包含过程的基本了解。准确来说,我们的教程将包含以下几点:
+ 安装Git。
+ 如何下载Django的开发复本
+ 运行Django的测试组件
+ 为你的补丁编写一个测试
+ 为你的补丁编码。
+ 测试你的补丁。
+ 为你所做的改变写一个补丁文件。
+ 去哪里寻找更多的信息。
一旦你完成了这份教程,你可以浏览剩下的Django’s documentation on contributing. 它包含了大量信息。任何想成为Django的正式贡献者必须去阅读它。如果你有问题,它也许会给你答案
## 安装Git ##
使用教程前,你需要安装好Git,下载Django的最新开发版本并且为你作出的改变生成补丁文件
为了确认你是否已经安装了Git, 输入 git 进入命令行。如果信息提示命令无法找到, 你就需要下载并安装Git, 详情阅读 Git’s download page.
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# TemplateResponse 和SimpleTemplateResponse
标准的[`HttpResponse`](request-response.html#django.http.HttpResponse "django.http.HttpResponse") 对象是静态的结构。在构造的时候提供给它们一个渲染好的内容,但是当内容改变时它们却不能很容易地完成相应的改变。
然而,有时候允许装饰器或者中间件在响应构造_之后_修改它是很有用的。例如,你可能想改变使用的模板,或者添加额外的数据到上下文中。
TemplateResponse 提供了实现这一点的方法。与基本的[`HttpResponse`](request-response.html#django.http.HttpResponse "django.http.HttpResponse") 对象不同,TemplateResponse 对象会记住视图提供的模板和上下文的详细信息来计算响应。响应的最终结果在后来的响应处理过程中直到需要时才计算。
## SimpleTemplateResponse 对象
_class_ `SimpleTemplateResponse`[[source]](../_modules/django/template/response.html#SimpleTemplateResponse)
### 属性
`SimpleTemplateResponse.template_name`
渲染的模板的名称。接收一个与后端有关的模板对象(例如[`get_template()`](../topics/templates.html#django.template.loader.get_template "django.template.loader.get_template") 返回的对象)、模板的名称或者一个模板名称列表。
例如:`['foo.html', 'path/to/bar.html']`
Deprecated since version 1.8: `template_name` 以前只接受一个 [`Template`](templates/api.html#django.template.Template "django.template.Template")
`SimpleTemplateResponse.context_data`
渲染模板时用到的上下文数据。它必须是一个[`dict`](https://docs.python.org/3/library/stdtypes.html#dict "(in Python v3.4)")
例如:`{'foo': 123}`
Deprecated since version 1.8: `context_data` 以前只接受一个 [`Context`](templates/api.html#django.template.Context "django.template.Context")
`SimpleTemplateResponse.rendered_content`[[source]](../_modules/django/template/response.html#SimpleTemplateResponse.rendered_content)
使用当前的模板和上下文数据渲染出来的响应内容。
`SimpleTemplateResponse.is_rendered`[[source]](../_modules/django/template/response.html#SimpleTemplateResponse.is_rendered)
一个布尔值,表示响应内容是否已经渲染。
### 方法
`SimpleTemplateResponse.__init__`(_template_, _context=None_, _content_type=None_, _status=None_, _charset=None_, _using=None_)[[source]](../_modules/django/template/response.html#SimpleTemplateResponse.__init__)
使用给定的模板、上下文、Content-Type、HTTP 状态和字符集初始化一个[`SimpleTemplateResponse`](#django.template.response.SimpleTemplateResponse "django.template.response.SimpleTemplateResponse") 对象。
`template`
一个与后端有关的模板对象(例如[`get_template()`](../topics/templates.html#django.template.loader.get_template "django.template.loader.get_template") 返回的对象)、模板的名称或者一个模板名称列表。
Deprecated since version 1.8: `template` 以前只接受一个[`Template`](templates/api.html#django.template.Template "django.template.Template")
`context`
一个[`dict`](https://docs.python.org/3/library/stdtypes.html#dict "(in Python v3.4)"),包含要添加到模板上下文中的值。它默认是一个空的字典。
Deprecated since version 1.8: `context` 以前只接受一个[`Context`](templates/api.html#django.template.Context "django.template.Context")
`content_type`
HTTP `Content-Type` 头部包含的值,包含MIME 类型和字符集的编码。 如果指定`content_type`,则使用它的值。 否则,使用[`DEFAULT_CONTENT_TYPE`](settings.html#std:setting-DEFAULT_CONTENT_TYPE)
`status`
响应的HTTP 状态码。
`charset`
响应编码使用的字符集。 如果没有给出则从`content_type`中提取,如果提取不成功则使用 [`DEFAULT_CHARSET`](settings.html#std:setting-DEFAULT_CHARSET) 设置。
`using`
加载模板使用的模板引擎的[`名称`](settings.html#std:setting-TEMPLATES-NAME)
Changed in Django 1.8:
添加`charset``using` 参数。
`SimpleTemplateResponse.resolve_context`(_context_)[[source]](../_modules/django/template/response.html#SimpleTemplateResponse.resolve_context)
预处理上下文数据(context data),这个上下文数据将会被用来渲染的模版。接受包含上下文数据的一个[`dict`](https://docs.python.org/3/library/stdtypes.html#dict "(in Python v3.4)")。默认返回同一个[`dict`](https://docs.python.org/3/library/stdtypes.html#dict "(in Python v3.4)")
若要自定义上下文,请覆盖这个方法。
Changed in Django 1.8:
`resolve_context` 返回一个[`dict`](https://docs.python.org/3/library/stdtypes.html#dict "(in Python v3.4)")。它以前返回一个[`Context`](templates/api.html#django.template.Context "django.template.Context")
Deprecated since version 1.8: `resolve_context` 不再接受[`Context`](templates/api.html#django.template.Context "django.template.Context")
`SimpleTemplateResponse.resolve_template`(_template_)[[source]](../_modules/django/template/response.html#SimpleTemplateResponse.resolve_template)
解析渲染用到的模板实例。接收一个与后端有关的模板对象(例如[`get_template()`](../topics/templates.html#django.template.loader.get_template "django.template.loader.get_template") 返回的对象)、模板的名称或者一个模板名称列表。
返回将被渲染的模板对象。
若要自定义模板的加载,请覆盖这个方法。
Changed in Django 1.8:
`resolve_template` 返回一个与后端有关的模板对象。它以前返回一个[`Template`](templates/api.html#django.template.Template "django.template.Template")
Deprecated since version 1.8: `resolve_template` 不再接受一个 [`Template`](templates/api.html#django.template.Template "django.template.Template")
`SimpleTemplateResponse.add_post_render_callback`()[[source]](../_modules/django/template/response.html#SimpleTemplateResponse.add_post_render_callback)
添加一个渲染之后调用的回调函数。这个钩子可以用来延迟某些特定的处理操作(例如缓存)到渲染之后。
如果[`SimpleTemplateResponse`](#django.template.response.SimpleTemplateResponse "django.template.response.SimpleTemplateResponse") 已经渲染,那么回调函数将立即执行。
调用时,将只传递给回调函数一个参数 —— 渲染后的 [`SimpleTemplateResponse`](#django.template.response.SimpleTemplateResponse "django.template.response.SimpleTemplateResponse") 实例。
如果回调函数返回非`None` 值,它将用作响应并替换原始的响应对象(以及传递给下一个渲染之后的回调函数,以此类推)。
`SimpleTemplateResponse.render`()[[source]](../_modules/django/template/response.html#SimpleTemplateResponse.render)
设置`response.content`[`SimpleTemplateResponse.rendered_content`](#django.template.response.SimpleTemplateResponse.rendered_content "django.template.response.SimpleTemplateResponse.rendered_content") 的结果,执行所有渲染之后的回调函数,最后返回生成的响应对象。
`render()` 只在第一次调用它时其作用。以后的调用将返回第一次调用的结果。
## TemplateResponse 对象
_class_ `TemplateResponse`[[source]](../_modules/django/template/response.html#TemplateResponse)
`TemplateResponse`[`SimpleTemplateResponse`](#django.template.response.SimpleTemplateResponse "django.template.response.SimpleTemplateResponse") 的子类,而且能知道当前的[`HttpRequest`](request-response.html#django.http.HttpRequest "django.http.HttpRequest")
### 方法
`TemplateResponse.__init__`(_request_, _template_, _context=None_, _content_type=None_, _status=None_, _current_app=None_, _charset=None_, _using=None_)[[source]](../_modules/django/template/response.html#TemplateResponse.__init__)
使用给定的模板、上下文、Content-Type、HTTP 状态和字符集实例化一个[`TemplateResponse`](#django.template.response.TemplateResponse "django.template.response.TemplateResponse") 对象。
`request`
An [`HttpRequest`](request-response.html#django.http.HttpRequest "django.http.HttpRequest") instance.
`template`
一个与后端有关的模板对象(例如[`get_template()`](../topics/templates.html#django.template.loader.get_template "django.template.loader.get_template") 返回的对象)、模板的名称或者一个模板名称列表。
Deprecated since version 1.8: `template` 以前只接受一个[`Template`](templates/api.html#django.template.Template "django.template.Template")
`context`
一个[`dict`](https://docs.python.org/3/library/stdtypes.html#dict "(in Python v3.4)"),包含要添加到模板上下文中的值。 它默认是一个空的字典。
Deprecated since version 1.8: `context` 以前只接受一个[`Context`](templates/api.html#django.template.Context "django.template.Context")
`content_type`
HTTP `Content-Type` 头部包含的值,包含MIME 类型和字符集的编码。如果指定`content_type`,则使用它的值。否则,使用[`DEFAULT_CONTENT_TYPE`](settings.html#std:setting-DEFAULT_CONTENT_TYPE)
`status`
响应的HTTP 状态码。
`current_app`
包含当前视图的应用。更多信息,参见[_带命名空间的URL 解析策略_](../topics/http/urls.html#topics-http-reversing-url-namespaces)
Deprecated since version 1.8: 废弃`current_app` 参数。你应该去设置`request.current_app`
`charset`
响应编码使用的字符集。如果没有给出则从`content_type`中提取,如果提取不成功则使用 [`DEFAULT_CHARSET`](settings.html#std:setting-DEFAULT_CHARSET) 设置。
`using`
加载模板使用的模板引擎的[`名称`](settings.html#std:setting-TEMPLATES-NAME)
Changed in Django 1.8:
添加`charset``using` 参数。
## 渲染的过程
[`TemplateResponse`](#django.template.response.TemplateResponse "django.template.response.TemplateResponse") 实例返回给客户端之前,它必须被渲染。渲染的过程采用模板和上下文变量的中间表示形式,并最终将它转换为可以发送给客户端的字节流。
有三种情况会渲染`TemplateResponse`
* 使用[`SimpleTemplateResponse.render()`](#django.template.response.SimpleTemplateResponse.render "django.template.response.SimpleTemplateResponse.render") 方法显式渲染`TemplateResponse` 实例的时候。
* 通过给`response.content` 赋值显式设置响应内容的时候。
* 传递给模板响应中间件之后,响应中间件之前。
`TemplateResponse` 只能渲染一次。[`SimpleTemplateResponse.render()`](#django.template.response.SimpleTemplateResponse.render "django.template.response.SimpleTemplateResponse.render") 的第一次调用设置响应的内容;以后的响应不会改变响应的内容。
然而,当显式给`response.content` 赋值时,修改会始终生效。如果你想强制重新渲染内容,你可以重新计算渲染的内容并手工赋值给响应的内容:
```
# Set up a rendered TemplateResponse
>>> from django.template.response import TemplateResponse
>>> t = TemplateResponse(request, 'original.html', {})
>>> t.render()
>>> print(t.content)
Original content
# Re-rendering doesn't change content
>>> t.template_name = 'new.html'
>>> t.render()
>>> print(t.content)
Original content
# Assigning content does change, no render() call required
>>> t.content = t.rendered_content
>>> print(t.content)
New content
```
### 渲染后的回调函数
某些操作 —— 例如缓存 —— 不可以在没有渲染的模板上执行。它们必须在完整的渲染后的模板上执行。
如果你正在使用中间件,解决办法很容易。中间件提供多种在从视图退出时处理响应的机会。如果你向响应中间件添加一些行为,它们将保证在模板渲染之后执行。
然而,如果正在使用装饰器,就不会有这样的机会。装饰器中定义的行为会立即执行。
为了补偿这一点(以及其它类似的使用情形)[`TemplateResponse`](#django.template.response.TemplateResponse "django.template.response.TemplateResponse") 允许你注册在渲染完成时调用的回调函数。使用这个回调函数,你可以延迟某些关键的处理直到你可以保证渲染后的内容是可以访问的。
要定义渲染后的回调函数,只需定义一个接收一个响应作为参数的函数并将这个函数注册到模板响应中:
```
from django.template.response import TemplateResponse
def my_render_callback(response):
# Do content-sensitive processing
do_post_processing()
def my_view(request):
# Create a response
response = TemplateResponse(request, 'mytemplate.html', {})
# Register the callback
response.add_post_render_callback(my_render_callback)
# Return the response
return response
```
`my_render_callback()` 将在`mytemplate.html` 渲染之后调用,并将被传递一个[`TemplateResponse`](#django.template.response.TemplateResponse "django.template.response.TemplateResponse") 实例作为参数。
如果模板已经渲染,回调函数将立即执行。
## 使用TemplateResponse 和SimpleTemplateResponse
[`TemplateResponse`](#django.template.response.TemplateResponse "django.template.response.TemplateResponse") 对象和普通的[`django.http.HttpResponse`](request-response.html#django.http.HttpResponse "django.http.HttpResponse") 一样可以用于任何地方。它可以用来作为[`render()`](../topics/http/shortcuts.html#django.shortcuts.render "django.shortcuts.render")[`render_to_response()`](../topics/http/shortcuts.html#django.shortcuts.render_to_response "django.shortcuts.render_to_response") 的另外一种选择。
例如,下面这个简单的视图使用一个简单模板和包含查询集的上下文返回一个[`TemplateResponse`](#django.template.response.TemplateResponse "django.template.response.TemplateResponse")
```
from django.template.response import TemplateResponse
def blog_index(request):
return TemplateResponse(request, 'entry_list.html', {'entries': Entry.objects.all()})
```
此差异已折叠。
此差异已折叠。
此差异已折叠。
# 表单验证和字段验证
表单验证发生在数据验证之后。如果你需要定制化这个过程,有几个不同的地方可以修改,每个地方的目的不一样。表单处理过程中要运行三种类别的验证方法。它们通常在你调用表单的`is_valid()` 方法时执行。还有其它方法可以触发验证过程(访问`errors` 属性或直接调用`full_clean()` ),但是通用情况下不需要。
一般情况下,如果处理的数据有问题,每个类别的验证方法都会引发`ValidationError`,并将相关信息传递给`ValidationError`[_参见下文_](#raising-validation-error)中引发`ValidationError` 的最佳实践。如果没有引发`ValidationError`,这些方法应该返回验证后的(规整化的)数据的Python 对象。
大部分应该可以使用[validators](#validators) 完成,它们可以很容易地重用。Validators 是简单的函数(或可调用对象),它们接收一个参数并对非法的输入抛出`ValidationError`。 Validators 在字段的`to_python``validate` 方法调用之后运行。
表单的验证划分成几个步骤,它们可以定制或覆盖:
* 字段的`to_python()` 方法是验证的第一步。它将值强制转换为正确的数据类型,如果不能转换则引发`ValidationError`。 这个方法从Widget 接收原始的值并返回转换后的值。例如,FloatField 将数据转换为Python 的`float` 或引发`ValidationError`
* 字段的`validate()` 方法处理字段特异性的验证,这种验证不适合位于validator 中。它接收一个已经转换成正确数据类型的值, 并在发现错误时引发`ValidationError`。这个方法不返回任何东西且不应该改变任何值。当你遇到不可以或不想放在validator 中的验证逻辑时,应该覆盖它来处理验证。
* 字段的`run_validators()` 方法运行字段的所有Validator,并将所有的错误信息聚合成一个单一的`ValidationError`。你应该不需要覆盖这个方法。
* Field子类的`clean()` 方法。它负责以正确的顺序运行`to_python``validate``run_validators` 并传播它们的错误。如果任何时刻、任何方法引发`ValidationError`,验证将停止并引发这个错误。这个方法返回验证后的数据,这个数据在后面将插入到表单的 `cleaned_data` 字典中。
* 表单子类中的`clean_<fieldname>()` 方法 —— `<fieldname>` 通过表单中的字段名称替换。这个方法完成于特定属性相关的验证,这个验证与字段的类型无关。这个方法没有任何传入的参数。你需要查找`self.cleaned_data` 中该字段的值,记住此时它已经是一个Python 对象而不是表单中提交的原始字符串(它位于`cleaned_data` 中是因为字段的`clean()` 方法已经验证过一次数据)。
例如,如果你想验证名为`serialnumber` 的`CharField` 的内容是否唯一, `clean_serialnumber()` 将是实现这个功能的理想之处。你需要的不是一个特别的字段(它只是一个`CharField`),而是一个特定于表单字段特定验证,并规整化数据。
这个方法返回从cleaned_data 中获取的值,无论它是否修改过。
* 表单子类的`clean()` 方法。这个方法可以实现需要同时访问表单多个字段的验证。这里你可以验证如果提供字段`A`,那么字段`B` 必须包含一个合法的邮件地址以及类似的功能。 这个方法可以返回一个完全不同的字典,该字典将用作`cleaned_data`
因为字段的验证方法在调用`clean()` 时会运行,你还可以访问表单的`errors` 属性,它包含验证每个字段时的所有错误。
注意,你覆盖的[`Form.clean()`](api.html#django.forms.Form.clean "django.forms.Form.clean") 引发的任何错误将不会与任何特定的字段关联。它们位于一个特定的“字段”(叫做`__all__`)中,如果需要可以通过 [`non_field_errors()`](api.html#django.forms.Form.non_field_errors "django.forms.Form.non_field_errors") 方法访问。如果你想添加一个特定字段的错误到表单中,需要调用 [`add_error()`](api.html#django.forms.Form.add_error "django.forms.Form.add_error")。
还要注意,覆盖`ModelForm` 子类的`clean()` 方法需要特殊的考虑。(更多信息参见[_ModelForm 文档_](../../topics/forms/modelforms.html#overriding-modelform-clean-method))。
这些方法按以上给出的顺序执行,一次验证一个字段。也就是说,对于表单中的每个字段(按它们在表单定义中出现的顺序),先运行`Field.clean()` ,然后运行`clean_<fieldname>()`。每个字段的这两个方法都执行完之后,最后运行[`Form.clean()`](api.html#django.forms.Form.clean "django.forms.Form.clean") 方法,无论前面的方法是否抛出过异常。
下面有上面每个方法的示例。
我们已经提到过,所有这些方法都可以抛出`ValidationError`。对于每个字段,如果`Field.clean()` 方法抛出 `ValidationError`,那么将不会调用该字段对应的clean_<fieldname>()方法。 但是,剩余的字段的验证方法仍然会执行。
## 抛出`ValidationError`
为了让错误信息更加灵活或容易重写,请考虑下面的准则:
* 给构造函数提供一个富有描述性的错误码`code`
```
# Good
ValidationError(_('Invalid value'), code='invalid')
# Bad
ValidationError(_('Invalid value'))
```
* 不要预先将变量转换成消息字符串;使用占位符和构造函数的`params` 参数:
```
# Good
ValidationError(
_('Invalid value: %(value)s'),
params={'value': '42'},
)
# Bad
ValidationError(_('Invalid value: %s') % value)
```
* 使用字典参数而不要用位置参数。这使得重写错误信息时不用考虑变量的顺序或者完全省略它们:
```
# Good
ValidationError(
_('Invalid value: %(value)s'),
params={'value': '42'},
)
# Bad
ValidationError(
_('Invalid value: %s'),
params=('42',),
)
```
*`gettext` 封装错误消息使得它可以翻译:
```
# Good
ValidationError(_('Invalid value'))
# Bad
ValidationError('Invalid value')
```
所有的准则放在一起就是:
```
raise ValidationError(
_('Invalid value: %(value)s'),
code='invalid',
params={'value': '42'},
)
```
如果你想编写可重用的表单、表单字段和模型字段,遵守这些准则是非常必要的。
如果你在验证的最后(例如,表单的`clean()` 方法)且知道_永远_ 不需要重新错误信息,虽然不提倡但你仍然可以选择重写不详细的信息:
```
ValidationError(_('Invalid value: %s') % value)
```
New in Django 1.7.
[`Form.errors.as_data()`](api.html#django.forms.Form.errors.as_data "django.forms.Form.errors.as_data")[`Form.errors.as_json()`](api.html#django.forms.Form.errors.as_json "django.forms.Form.errors.as_json") 方法很大程度上受益于`ValidationError`(利用`code` 名和`params` 字典)。
### 抛出多个错误
如果在一个验证方法中检查到多个错误并且希望将它们都反馈给表单的提交者,可以传递一个错误的列表给`ValidationError` 构造函数。
和上面一样,建议传递的列表中的`ValidationError` 实例都带有 `code``params`,但是传递一个字符串列表也可以工作:
```
# Good
raise ValidationError([
ValidationError(_('Error 1'), code='error1'),
ValidationError(_('Error 2'), code='error2'),
])
# Bad
raise ValidationError([
_('Error 1'),
_('Error 2'),
])
```
## 实践验证
前面几节解释在一般情况下表单的验证是如何工作的。因为有时直接看功能在实际中的应用会更容易掌握,下面是一些列小例子,它们用到前面的每个功能。
### 使用Validator
Django 的表单(以及模型)字段支持使用简单的函数和类用于验证,它们叫做Validator。Validator 是可调用对象或函数,它接收一个值,如果该值合法则什么也不返回,否则抛出[`ValidationError`](../exceptions.html#django.core.exceptions.ValidationError "django.core.exceptions.ValidationError")。它们可以通过字段的`validators` 参数传递给字段的构造函数,或者定义在[`Field`](fields.html#django.forms.Field "django.forms.Field") 类的`default_validators` 属性中。
简单的Validator 可以用于在字段内部验证值,让我们看下Django 的`SlugField`
```
from django.forms import CharField
from django.core import validators
class SlugField(CharField):
default_validators = [validators.validate_slug]
```
正如你所看到的,`SlugField` 只是一个带有自定义Validator 的`CharField`,它们验证提交的文本符合某些字符规则。这也可以在字段定义时实现,所以:
```
slug = forms.SlugField()
```
等同于:
```
slug = forms.CharField(validators=[validators.validate_slug])
```
常见的情形,例如验证邮件地址和正则表达式,可以使用Django 中已经存在的Validator 类处理。例如,`validators.validate_slug`[`RegexValidator`](../validators.html#django.core.validators.RegexValidator "django.core.validators.RegexValidator") 的一个实例,它构造时的第一个参数为:`^[-a-zA-Z0-9_]+$`[_编写Validator_](../validators.html) 一节可以查到已经存在的Validator 以及如何编写Validator 的一个示例。
### 表单字段的默认验证
让我们首先创建一个自定义的表单字段,它验证其输入是一个由逗号分隔的邮件地址组成的字符串。完整的类像这样:
```
from django import forms
from django.core.validators import validate_email
class MultiEmailField(forms.Field):
def to_python(self, value):
"Normalize data to a list of strings."
# Return an empty list if no input was given.
if not value:
return []
return value.split(',')
def validate(self, value):
"Check if value consists only of valid emails."
# Use the parent's handling of required fields, etc.
super(MultiEmailField, self).validate(value)
for email in value:
validate_email(email)
```
使用这个字段的每个表单都将在处理该字段数据之前运行这些方法。这个验证特定于该类型的字段,与后面如何使用它无关。
让我们来创建一个简单的`ContactForm` 来向你演示如何使用这个字段:
```
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
recipients = MultiEmailField()
cc_myself = forms.BooleanField(required=False)
```
只需要简单地使用`MultiEmailField`,就和其它表单字段一样。当调用表单的`is_valid()` 方法时,`MultiEmailField.clean()` 方法将作为验证过程的一部分运行,它将调用自定义的`to_python()``validate()` 方法。
### 验证特定字段属性
继续上面的例子,假设在我们的`ContactForm` 中,我们想确保`recipients` 字段始终包含`"fred@example.com"`。这是特定于我们这个表单的验证,所以我们不打算将它放在通用的`MultiEmailField` 类中。我们将编写一个运行在`recipients` 字段上的验证方法,像这样:
```
from django import forms
class ContactForm(forms.Form):
# Everything as before.
...
def clean_recipients(self):
data = self.cleaned_data['recipients']
if "fred@example.com" not in data:
raise forms.ValidationError("You have forgotten about Fred!")
# Always return the cleaned data, whether you have changed it or
# not.
return data
```
### 验证相互依赖的字段
假设我们添加另外一个需求到我们的联系人表单中:如果`cc_myself` 字段为`True`,那么`subject` 必须包含单词`"help"`。我们的这个验证包含多个字段,所以表单的[`clean()`](api.html#django.forms.Form.clean "django.forms.Form.clean") 方法是个不错的地方。注意,我们这里讨论的是表单的`clean()` 方法,之前我们编写的字段的`clean()` 方法。区别字段和表单之间的差别非常重要。字段是单个数据,表单是字段的集合。
在调用表单`clean()` 方法的时候,所有字段的验证方法已经执行完(前两节),所以`self.cleaned_data` 填充的是目前为止已经合法的数据。所以你需要记住这个事实,你需要验证的字段可能没有通过初试的字段检查。
在这一步,有两种方法报告错误。最简单的方法是在表单的顶端显示错误。你可以在`clean()` 方法中抛出`ValidationError` 来创建错误。例如:
```
from django import forms
class ContactForm(forms.Form):
# Everything as before.
...
def clean(self):
cleaned_data = super(ContactForm, self).clean()
cc_myself = cleaned_data.get("cc_myself")
subject = cleaned_data.get("subject")
if cc_myself and subject:
# Only do something if both fields are valid so far.
if "help" not in subject:
raise forms.ValidationError("Did not send for 'help' in "
"the subject despite CC'ing yourself.")
```
Changed in Django 1.7:
在以前版本的Django 中,要求`form.clean()` 返回`cleaned_data` 的一个字典。现在,这个方法仍然可以返回将要用到的数据的字典,但是不再是强制的。
在这段代码中,如果抛出验证错误,表单将在表单的顶部显示(通常是)描述该问题的一个错误信息。
注意,示例代码中`super(ContactForm, self).clean()` 的调用时为了保证维持父类中的验证逻辑。
第二种方法涉及将错误消息关联到某个字段。在这种情况下,让我们在表单的显示中分别关联一个错误信息到“subject” 和“cc_myself” 行。在实际应用中要小心,因为它可能导致表单的输出变得令人困惑。我们只是向你展示这里可以怎么做,在特定的情况下,需要你和你的设计人员确定什么是好的方法。我们的新代码(代替前面的示例)像这样:
```
from django import forms
class ContactForm(forms.Form):
# Everything as before.
...
def clean(self):
cleaned_data = super(ContactForm, self).clean()
cc_myself = cleaned_data.get("cc_myself")
subject = cleaned_data.get("subject")
if cc_myself and subject and "help" not in subject:
msg = "Must put 'help' in subject when cc'ing yourself."
self.add_error('cc_myself', msg)
self.add_error('subject', msg)
```
`add_error()` 的第二个参数可以是一个简单的字符串,但更倾向是`ValidationError` 的一个实例。更多细节参见[_抛出ValidationError_](#raising-validation-error)。注意,`add_error()` 将从`cleaned_data` 中删除相应的字段。
此差异已折叠。
此差异已折叠。
......@@ -11,11 +11,11 @@
+ [第6部分:静态文件](1_2_6_Part 6 Static files.md)
+ [高级教程](1_3.md)
+ [如何编写可重用的应用 ](1_3_1_How to write reusable apps.md)
+ [为Django编写首个补丁](1_3_2_Writing your first patch for Django_.md)
+ [为Django编写首个补丁](1_3_2_Writing your first patch for Django.md)
+ [模型层](2_The model layer.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)
......@@ -33,7 +33,7 @@
+ [高级](2_5.md)
+ [管理器](2_5_1_Manager.md)
+ [原始的SQL查询](2_5_2_Performing raw SQL queries.md)
+ [事务]()
+ [事务](2_5_3_Transactions.md)
+ [聚合](2_5_4_Aggregation.md)
+ [自定义字段]()
+ [多数据库](2_5_6_Multiple databases.md)
......@@ -42,7 +42,7 @@
+ [条件表达式](2_5_9_Conditional Expressions.md)
+ [数据库函数](2_5_10_Database Functions.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)
......@@ -55,8 +55,9 @@
+ [装饰器](3_1_4_Decorators.md)
+ [参考](3_2.md)
+ [内建的视图](3_2_1_Built-in Views.md)
+ [请求/响应 对象]()
+ [请求/响应 对象](3_2_2_Request response objects.md)
+ [TemplateResponse 对象](3_2_3_TemplateResponse objects.md)
+ [TemplateResponse 和SimpleTemplateResponse](3_2_4_TemplateResponse objects.md)
+ [文件上传](3_3.md)
+ [概览](3_3_1_Overview.md)
+ [File 对象](3_3_2_File objects.md)
......@@ -67,7 +68,7 @@
+ [概览](3_4_1_Overview.md)
+ [内建显示视图](3_4_2_Built-in display views.md)
+ [内建编辑视图](3_4_3_Built-in editing views.md)
+ [使用Mixin]()
+ [使用Mixin](3_4_4_Using mixins.md)
+ [API参考](3_4_5_API reference.md)
+ [分类索引](3_4_6_Flattened index.md)
+ [高级](3_5.md)
......@@ -85,7 +86,7 @@
+ [网页设计助手(已废弃)]()
+ [人性化](4_2_4_Humanization.md)
+ [面向程序员](4_3.md)
+ [模板API]()
+ [模板API](4_3_1_Template API.md)
+ [自定义标签和过滤器]()
+ [表单](5_Forms.md)
+ [基础](5_1.md)
......@@ -96,14 +97,14 @@
+ [高级](5_2.md)
+ [模型表单]()
+ [整合媒体](5_2_2_Integrating media.md)
+ [表单集]()
+ [自定义验证]()
+ [表单集](5_2_3_Formsets_.md)
+ [自定义验证](5_2_4_Customizing validation.md)
+ [开发过程](6_The development process.md)
+ [设置](6_1.md)
+ [概览](6_1_1_Overview.md)
+ [完整列表设置]()
+ [应用程序](6_2.md)
+ [概览]()
+ [概览](6_2_Overview.md)
+ [异常](6_3.md)
+ [概览](6_3_Overview.md)
+ [django-admin 和 manage.py](6_4.md)
......@@ -130,6 +131,7 @@
+ [点击劫持保护](8_3_Clickjacking protection.md)
+ [伪造跨站请求保护]()
+ [加密签名](8_5_Cryptographic signing.md)
+ [安全中间件](8_6_Security Middleware.md)
+ [国际化和本地化](9_Internationalization and localization.md)
+ [概述](9_1_1_Overview.md)
+ [国际化]()
......@@ -149,7 +151,7 @@
+ [发送邮件]()
+ [组织 feeds (RSS/Atom)]()
+ [分页](13_6_Pagination.md)
+ [消息框架]()
+ [消息框架](13_7_Messages framework_.md)
+ [序列化]()
+ [会话](13_9_1_Sessions.md)
+ [网站地图]()
......@@ -162,5 +164,5 @@
+ [重定向](14_4_1_Redirects.md)
+ [信号](14_5_Signals.md)
+ [系统检查框架](14_6_System check framework.md)
+ [网站框架]()
+ [网站框架](14_7_The sites framework_.md)
+ [Django中的Unicode编码]()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册