Unicode数据

Django在任何地方都支持Unicode数据。

此文档告诉您,如果编写的应用程序使用的数据或模板不是用ASCII编码的,您需要知道什么。

正在创建数据库

确保数据库配置为能够存储任意字符串数据。通常,这意味着给它一个utf-8或utf-16编码。如果使用更严格的编码(例如,Latin1(ISO8859-1)),将无法在数据库中存储某些字符,信息将丢失。

  • mysql用户,参考 MySQL manual 有关如何设置或更改数据库字符集编码的详细信息。

  • PostgreSQL用户,请参阅 PostgreSQL manual 有关使用正确编码创建数据库的详细信息,请参阅。

  • Oracle用户,请参阅 Oracle manual 有关如何设置的详细信息 (section 2 或更改 (section 11 )数据库字符集编码。

  • sqlite用户,无需执行任何操作。sqlite总是使用utf-8进行内部编码。

Django的所有数据库后端都会自动将字符串转换为适当的编码,以便与数据库通信。它们还自动将从数据库中检索到的字符串转换为字符串。您甚至不需要告诉Django您的数据库使用什么编码:这是透明处理的。

有关更多信息,请参阅下面的“数据库API”部分。

一般字符串处理

每当您在django中使用字符串时(例如,在数据库查找、模板呈现或其他任何地方),您有两个选项来编码这些字符串。您可以使用普通字符串或字节字符串(以“b”开头)。

警告

字节串不携带任何有关其编码的信息。出于这个原因,我们必须做一个假设,Django假设所有字节都是UTF-8格式的。

如果您向Django传递一个以其他格式编码的字符串,事情将以有趣的方式出错。通常,Django会 UnicodeDecodeError 在某个时刻。

如果代码只使用ASCII数据,那么可以安全地使用普通字符串,随意传递它们,因为ASCII是UTF-8的一个子集。

不要被愚弄以为 DEFAULT_CHARSET 设置被设置为除 'utf-8' 您可以在字节串中使用其他编码! DEFAULT_CHARSET 仅适用于模板呈现(和电子邮件)结果生成的字符串。Django将始终为内部字节串采用UTF-8编码。原因是 DEFAULT_CHARSET 设置实际上不在您的控制之下(如果您是应用程序开发人员)。它由安装和使用应用程序的人员控制——如果该人员选择了不同的设置,您的代码仍然必须继续工作。因此,它不能依赖于这种设置。

在大多数情况下,当Django处理字符串时,它会先将它们转换为字符串,然后再执行其他操作。因此,作为一般规则,如果传入一个字节串,那么就准备在结果中接收一个字符串。

翻译后的字符串

除了字符串和字节字符串之外,还有第三种类型的字符串类对象,您在使用Django时可能会遇到。框架的国际化特性引入了“懒惰翻译”的概念——一个被标记为已翻译的字符串,但其实际翻译结果在对象被用于字符串之前无法确定。在使用字符串之前转换区域设置未知的情况下,此功能非常有用,即使字符串最初可能是在首次导入代码时创建的。

通常情况下,您不必担心懒惰的翻译。请注意,如果您检查一个对象,它声称是 django.utils.functional.__proxy__ 对象,这是一个懒惰的翻译。调用 str() 使用惰性转换作为参数将在当前区域设置中生成字符串。

有关惰性翻译对象的详细信息,请参阅 internationalization 文档。

实用功能

因为一些字符串操作会一次又一次地出现,所以Django提供了一些有用的函数,这些函数可以使处理字符串和字节字符串对象变得更加容易。

转换函数

这个 django.utils.encoding 模块包含一些函数,可以方便地在字符串和字节串之间来回转换。

  • smart_str(s, encoding='utf-8', strings_only=False, errors='strict') 将其输入转换为字符串。这个 encoding 参数指定输入编码。(例如,Django在处理表单输入数据时在内部使用它,而表单输入数据可能不是UTF-8编码的。) strings_only 参数如果设置为true,将导致python数、布尔值和 None 未转换为字符串(保留其原始类型)。这个 errors 参数接受python接受的任何值 str() 用于错误处理的函数。

  • force_str(s, encoding='utf-8', strings_only=False, errors='strict') 相同 smart_str() 几乎在所有情况下。区别在于第一个参数是 lazy translation 实例。同时 smart_str() 保留懒惰的翻译, force_str() 将这些对象强制为字符串(导致发生转换)。通常,你会想用 smart_str() . 然而, force_str() 在模板标记和过滤器中非常有用 must 有一个字符串可以使用,而不仅仅是可以转换为字符串的东西。

  • smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict') 本质上与 smart_str() . 它将第一个参数强制为bytestring。这个 strings_only 参数的行为与for相同 smart_str()force_str() . 这与Python的内置语义稍有不同 str() 功能,但在Django内部的一些地方需要差异。

通常,您只需要使用 force_str() . 在任何可能是字符串或字节串的输入数据上尽早调用它,从那时起,您可以将结果视为始终是字符串。

URI和IRI处理

Web框架必须处理URL(这是IRI的一种类型)。URL的一个要求是仅使用ASCII字符进行编码。但是,在国际环境中,您可能需要从 IRI --非常宽泛地说,一个 URI 可以包含Unicode字符的。使用以下函数引用IRI并将其转换为URI:

这两组函数的用途略有不同,保持它们的直线性很重要。通常,你会使用 quote() 在IRI或URI路径的各个部分上,以便正确编码任何保留字符,如“&”或“%”。然后,你申请 iri_to_uri() 到完整的IRI,它将任何非ASCII字符转换为正确的编码值。

备注

从技术上讲,这样说是不对的 iri_to_uri() 在IRI规范中实现完整的算法。它还没有执行算法的国际域名编码部分。

这个 iri_to_uri() 函数不会更改URL中允许的ASCII字符。因此,例如,当传递给 iri_to_uri() . 这意味着您可以向这个函数传递一个完整的URL,它不会弄乱查询字符串或类似的任何东西。

举个例子可能会说明以下问题:

>>> from urllib.parse import quote
>>> from django.utils.encoding import iri_to_uri
>>> quote("Paris & Orléans")
'Paris%20%26%20Orl%C3%A9ans'
>>> iri_to_uri("/favorites/François/%s" % quote("Paris & Orléans"))
'/favorites/Fran%C3%A7ois/Paris%20%26%20Orl%C3%A9ans'

如果仔细观察,可以看到 quote() 在第二个示例中,传递给 iri_to_uri() . 这是一个非常重要和有用的特性。这意味着您可以构建IRI,而不必担心它是否包含非ASCII字符,然后在末尾调用 iri_to_uri() 关于结果。

同样,Django提供 django.utils.encoding.uri_to_iri() 它根据 RFC 3987#section-3.2 .

以下是演示的一个示例:

>>> from django.utils.encoding import uri_to_iri
>>> uri_to_iri("/%E2%99%A5%E2%99%A5/?utf8=%E2%9C%93")
'/♥♥/?utf8=✓'
>>> uri_to_iri("%A9hello%3Fworld")
'%A9hello%3Fworld'

在第一个例子中,UTF-8字符是不加引号的。在第二种情况下,百分比编码保持不变,因为它们不在有效的UTF-8范围内或表示保留字符。

两个 iri_to_uri()uri_to_iri() 函数是等幂的,这意味着以下总是正确的:

iri_to_uri(iri_to_uri(some_string)) == iri_to_uri(some_string)
uri_to_iri(uri_to_iri(some_string)) == uri_to_iri(some_string)

因此,您可以在同一个URI/IRI上安全地多次调用它,而不必冒重复引用问题的风险。

模型

因为从数据库返回的所有字符串都是 str 当Django从数据库中检索数据时,基于字符的对象、模型字段(charfield、textfield、urlfield等)将包含Unicode值。这是 总是 这种情况下,即使数据可以放入一个ASCII字节串。

您可以在创建模型或填充字段时传入字节串,Django将在需要时将其转换为字符串。

照顾 get_absolute_url()

URL只能包含ASCII字符。如果您是从可能不是ASCII的数据块构造一个URL,请注意以适合于URL的方式对结果进行编码。这个 reverse() 函数自动为您处理此问题。

如果您正在手动构造一个URL(即, not 使用 reverse() 函数),您需要自己处理编码。在这种情况下,使用 iri_to_uri()quote() 记录的功能 above. 例如::

from urllib.parse import quote
from django.utils.encoding import iri_to_uri


def get_absolute_url(self):
    url = "/person/%s/?x=0&y=0" % quote(self.location)
    return iri_to_uri(url)

此函数返回正确编码的URL,即使 self.location 有点像“杰克去过巴黎和奥兰”。(事实上, iri_to_uri() 在上面的示例中,调用不是严格必需的,因为在第一行引用时,所有非ASCII字符都将被删除。)

模板

手动创建模板时使用字符串::

from django.template import Template

t2 = Template("This is a string template.")

但常见的情况是从文件系统中读取模板。如果模板文件未使用UTF-8编码存储,请调整 TEMPLATES 设置。内置的 django 后端系统提供 'file_charset' 选项更改用于从磁盘读取文件的编码。

这个 DEFAULT_CHARSET 设置控制渲染模板的编码。默认设置为UTF-8。

模板标记和筛选器

编写自己的模板标记和过滤器时需要记住的几个提示:

  • 始终从模板标记返回字符串 render() 方法和来自模板筛选器。

  • 使用 force_str() 优先于 smart_str() 在这些地方。标记呈现和过滤器调用在呈现模板时发生,因此延迟将延迟转换对象转换为字符串没有好处。在那一点上,单独使用字符串更容易。

文件夹

如果您打算允许用户上传文件,则必须确保用于运行Django的环境配置为使用非ASCII文件名。如果您的环境配置不正确,您将遇到 UnicodeEncodeError 使用包含非ASCII字符的文件名或内容保存文件时例外。

文件系统对UTF-8文件名的支持各不相同,可能取决于环境。通过运行以下命令,检查交互式python shell中的当前配置:

import sys

sys.getfilesystemencoding()

这将输出“utf-8”。

这个 LANG 环境变量负责设置Unix平台上的预期编码。有关设置此变量的适当语法和位置,请参阅您的操作系统和应用程序服务器的文档。请参阅 如何将Django与Apache和 mod_wsgi 举个例子。

在开发环境中,可能需要将设置添加到 ~.bashrc 类似于:

export LANG="en_US.UTF-8"

表单提交

HTML表单提交是一个棘手的领域。无法保证提交内容中包含编码信息,这意味着框架可能必须猜测提交数据的编码。

Django采用“懒惰”的方式对表单数据进行解码。AN中的数据 HttpRequest 对象只有在您访问时才会被解码。事实上,大多数数据根本没有被解码。只有 HttpRequest.GETHttpRequest.POST 数据结构有任何解码应用于它们。这两个字段将以Unicode数据的形式返回其成员。所有其他属性和方法 HttpRequest 返回客户提交的数据。

默认情况下, DEFAULT_CHARSET 设置用作表单数据的假定编码。如果需要为特定表单更改此项,可以设置 encoding 属性上的 HttpRequest 实例。例如::

def some_view(request):
    # We know that the data must be encoded as KOI8-R (for some reason).
    request.encoding = "koi8-r"
    ...

您甚至可以在访问后更改编码 request.GETrequest.POST ,所有后续访问都将使用新的编码。

大多数开发人员不需要担心更改表单编码,但对于与无法控制编码的遗留系统进行通信的应用程序来说,这是一个有用的功能。

Django不解码文件上载的数据,因为该数据通常被视为字节的集合,而不是字符串。任何自动解码都会改变字节流的含义。