# Django 6.0.7 documentation

# 将你的应用从 Django 0.96 移植到 1.0

Django 1.0 在某些地方与 0.96 版本是不兼容的。

这个指南会帮助你把 0.96 的项目和应用移植到 1.0 版本。本文档的第一部分包含了需要运行在 1.0上的常见变更。如果通过了第一部分的变更，你的代码依然无法运行，那么请检查 Less-common Changes 不常见的变更部分中列出的兼容问题。

See also

文档 1.0 版本注意事项。这份文档更深入地解释了 1.0 中新的特性；移植指南更多考虑了帮助你快速更新你的代码。

## 常见变更

本部分描述了 0.96 与 1.0 之间的变更，这些变更都是大部分用户会做的事情。

### 使用 Unicode 字符集

逐字地把字符串 (`'foo'`) 变成 Unicode 字符串 (`u'foo'`)。Django 目前从始至终贯穿地使用 Unicode 字符集字符串。在绝大多数地方，生食字符串会继续有效，但更新成 Unicode 字符串会防止某些含糊的问题出现。

查阅 Unicode 数据 文档了解细节。

### 模型

对于你的模型文件中常见的变更：

#### `maxlength` 重命名为 `max_length`

把你的 `maxlength` 参数名变成 `max_length` (这次参数名的变更是为了与表单区域保持一致)：

#### 用 `__unicode__` 代替 `__str__`

使用 `__unicode__` 方法替换你的模型中的 `__str__` 函数，然后才能确保你使用 使用 Unicode 字符集 (`u'foo'`) 中所介绍的方法。

#### 移除 `prepopulated_from`

删除模型区域中 `prepopulated_from` 参数。这个参数不再合法，而且已经转移到管理员 `admin.py` 模块里的 `ModelAdmin` 类中去了。查阅 管理员 内容了解更多对管理员变更的细节。

#### 移除 `core`

删除你的模型区域里的 `core` 参数。这个参数不再需要了，因为相同功能 (参考 行内编辑) 目前已经由后台接口做出不同地处理。你不用再担心行内编辑，除非你在 管理员 部分才考虑行内编辑。目前，删除所有对 `core` 参数的指向。

#### 用 `admin.py` 替换 `class Admin:`

从你的模型里移除所有嵌入 `class Admin` 管理员类声明。如果你保留这种类的话，不会对任何有影响，但也不会起任何作用。要注册应用程序管理员，你要把这种类的声明移到 `admin.py` 管理员模块文件里；查看 管理员 内容了解更多细节。

See also

对 djangosnippets 做出的贡献者已经写了一个脚本会 扫描你的 models.py 模型模块后生成一个相应的 admin.py 管理员模块。

#### 例如

下面的一个例子 `models.py` 模型文件含有所有你需要做出的变更：

老版本 (0.96) `models.py`:

    class Author(models.Model):
        first_name = models.CharField(maxlength=30)
        last_name = models.CharField(maxlength=30)
        slug = models.CharField(maxlength=60, prepopulate_from=("first_name", "last_name"))

        class Admin:
            list_display = ["first_name", "last_name"]

        def __str__(self):
            return "%s %s" % (self.first_name, self.last_name)

新版本 (1.0) `models.py`:

    class Author(models.Model):
        first_name = models.CharField(max_length=30)
        last_name = models.CharField(max_length=30)
        slug = models.CharField(max_length=60)

        def __unicode__(self):
            return "%s %s" % (self.first_name, self.last_name)

新版本 (1.0) `admin.py` 管理员模块：

    from django.contrib import admin
    from models import Author

    class AuthorAdmin(admin.ModelAdmin):
        list_display = ["first_name", "last_name"]
        prepopulated_fields = {"slug": ("first_name", "last_name")}

    admin.site.register(Author, AuthorAdmin)

### 管理员

在 1.0 版本中最具挑战性的新管理员功能问题之一。Django 管理接口 (`django.contrib.admin`) 管理员功能已经完全重构了；许多管理员定义目前都完全从许多模型定义中解构出来，许多框架都重写后使用 Django 的新表单处理库，而且重新设计成具有扩展和自定义能力。

实际中，意味着你要重写所有你的 `class Admin` 管理员类声明。你已经在上面的 models 中见过了如何替换你的 `class Admin` 管理员类，就是在 `admin.py` 管理员模块文件中用一个 `admin.site.register()` 管理员网页注册调用来替代。下面这些都是一些更详细的如何重写 `Admin` 管理员声明类的新句法。

#### 使用新的行内句法

新的 `edit_inline` 行内编辑选项都已经移到 `admin.py` 管理员模块中了。下面有一个例子：

老版本 (0.96):

    class Parent(models.Model): ...

    class Child(models.Model):
        parent = models.ForeignKey(Parent, edit_inline=models.STACKED, num_in_admin=3)

新版本 (1.0):

    class ChildInline(admin.StackedInline):
        model = Child
        extra = 3

    class ParentAdmin(admin.ModelAdmin):
        model = Parent
        inlines = [ChildInline]

    admin.site.register(Parent, ParentAdmin)

查阅 InlineModelAdmin 对象 管理员行内文档了解更多细节。

#### 简化 `fields`，或使用 `fieldsets`

老的 `fields` 句法是非常迷糊的，而且已经被简化过。老的句法依然有效，但你要使用 `fieldsets` 来代替老旧句法。

老版本 (0.96):

    class ModelOne(models.Model):
        ...

        class Admin:
            fields = ((None, {"fields": ("foo", "bar")}),)

    class ModelTwo(models.Model):
        ...

        class Admin:
            fields = (
                ("group1", {"fields": ("foo", "bar"), "classes": "collapse"}),
                ("group2", {"fields": ("spam", "eggs"), "classes": "collapse wide"}),
            )

新版本 (1.0):

    class ModelOneAdmin(admin.ModelAdmin):
        fields = ("foo", "bar")

    class ModelTwoAdmin(admin.ModelAdmin):
        fieldsets = (
            ("group1", {"fields": ("foo", "bar"), "classes": "collapse"}),
            ("group2", {"fields": ("spam", "eggs"), "classes": "collapse wide"}),
        )

See also

- 更多关于变更的详细信息以及背后的原因都可以在 NewformsAdminBranch wiki page 新表单管理分支维基百科页面找到

- 新的管理员功能伴随着许多新特性；你可以在 管理员文档 中来阅读有关内容。

### URLs

#### 更新你的根路径 `urls.py` 模块

如果你正在使用后台界面，你需要更新你的根路径``urls.py`` 模块。

老版本 (0.96) `urls.py`:

    from django.conf.urls.defaults import *

    urlpatterns = patterns(
        "",
        (r"^admin/", include("django.contrib.admin.urls")),
        # ... the rest of your URLs here ...
    )

新版本 (1.0) `urls.py`:

    from django.conf.urls.defaults import *

    # The next two lines enable the admin and load each admin.py file:
    from django.contrib import admin

    admin.autodiscover()

    urlpatterns = patterns(
        "",
        (r"^admin/(.*)", admin.site.root),
        # ... the rest of your URLs here ...
    )

### 视图

#### 使用 `django.forms` 来代替 `newforms` 用法

用 `django.forms` 来替换 `django.newforms` 用法 -- Django 1.0 版本重命名了 `newforms` 模块名 (曾用在 0.96 版本的名字) 采用以前用过的 `forms` 名字。而 `oldforms` 模块也会被移除。

如果你已经使用 `newforms` 库的话，而且你采用了我们所建议的 `import` 导入语句句法，那么所有你要做的就是改变一下你的导入依据即可。

老版本:

    from django import newforms as forms

新版本:

    from django import forms

如果你正在使用老的表单系统 (正规的名字是 `django.forms` 和 `django.oldforms`)，那么你要重写你的表单。良好地开始就是从阅读 表单文档 内容

#### 处理上传文件使用新的 API

代替上传文件的用法 -- 那就是，采用 `request.FILES` 文件请求入口 -- 与含有 `UploadedFile` 文件上传类的简单字典一样。老的字典句法不再有效了。

因此，看起来就像:

    def my_view(request):
        f = request.FILES["file_field_name"]
        ...

...你需要做出如下变更：

老版本 (0.96) | 新版本 (1.0)
--- | ---
`f['content']` | `f.read()`
`f['filename']` | `f.name`
`f['content-type']` | `f.content_type`

#### 文件字段使用新API处理

class:django.db.models.FileField 的内部实现已更改。一个明显的结果是您访问特殊属性(URL, filename, image size, 等等)的方式发生了变化。您需要进行以下更改，假设您的模型 `FileField` 调用 `myfile`:

老版本 (0.96) | 新版本 (1.0)
--- | ---
`myfile.get_content_filename()` | `myfile.content.path`
`myfile.get_content_url()` | `myfile.content.url`
`myfile.get_content_size()` | `myfile.content.size`
`myfile.save_content_file()` | `myfile.content.save()`
`myfile.get_content_width()` | `myfile.content.width`
`myfile.get_content_height()` | `myfile.content.height`

请注意 `width` 和 `height` 属性仅对:class:~django.db.models.ImageField 字段有意义。更多详细信息可以在 模型 API 文档中找到。

#### 使用 `Paginator` 而不是 `ObjectPaginator`

0.96 版本中的 `ObjectPaginator` 已被删除，并替换为一个改进版本: class:django.core.paginator.Paginator。

### 模板

#### 学会爱上自动转义

默认情况下，模板系统现在会自动对每个变量的输出进行HTML转义。要了解更多信息，请参阅 自动 HTML 转义。

要禁用单个变量的自动转义，请使用 `safe` 过滤器:

    This will be escaped: {{ data }}
    This will not be escaped: {{ data|safe }}

要禁用整个模板的自动转义，请将模板（或模板的特定部分）包装在 `autoescape` 标签:

    {% autoescape off %}
       ... unescaped template content here ...
    {% endautoescape %}

## 不常见的变化

以下更改是较小的、更本地化的更改。它们应该只影响更高级的用户，但是通读列表并检查代码中的这些东西可能是值得的。

### 信号

- 将``**kwargs``添加到任何注册的信号处理程序中。

- 通过 `Signal` 对象上的方法连接、断开连接和发送信号，而不是通过 `django.dispatch.dispatcher` 中的模块方法。

- 移除任何对 `Anonymous` 和 `Any` 发送者选项的使用；它们不再存在。你仍然可以通过使用 `sender=None` 接收来自任何发送者的信号。

- 将你声明的任何自定义信号转换为 `django.dispatch.Signal` 的实例，而不是匿名对象。

以下是你需要进行的代码更改的快速摘要：

老版本 (0.96) | 新版本 (1.0)
--- | ---
`def callback(sender)` | `def callback(sender, **kwargs)`
`sig = object()` | `sig = django.dispatch.Signal()`
`dispatcher.connect(callback, sig)` | `sig.connect(callback)`
`dispatcher.send(sig, sender)` | `sig.send(sender)`
`dispatcher.connect(callback, sig, sender=Any)` | `sig.connect(callback, sender=None)`

### 评论

如果你使用的是Django 0.96版的` Django .contrib.comments`应用程序，你需要升级到1.0版引入的新评论应用程序。详细信息请参见升级指导书。

### 模板标签

#### `spaceless` 标签

`spaceless` 模板标签现在会移除 HTML 标签之间的 所有 空格，而不再保留单个空格。

### 本地风味

#### 美国本地风味

`django.contrib.localflavor.usa` 已重命名为 `django.contrib.localflavor.us`。此更改是为了与其他本地化风格的命名方案匹配。要迁移你的代码，你只需要更改导入语句。

### 会话

#### 获取新的会话密钥

`SessionBase.get_new_session_key()` 已重命名为 `_get_new_session_key()`。`get_new_session_object()` 不再存在。

### 辅助工具

#### 加载一行不再调用"save()"

以前加载一行数据会自动运行模型的`save()`方法。现在情况不再是这样了，所以任何由``save()``自动填充的字段(例如:时间戳)现在都需要在任何fixture中显示值。

### 配置

#### 更好的异常处理

旧的 `EnvironmentError` 现已拆分为 `ImportError` （当 Django 无法找到设置模块时）和 `RuntimeError` （当你尝试在已经使用过设置之后重新配置它们时）。

#### `LOGIN_URL` 已移动

`LOGIN_URL` 常量已从 `django.contrib.auth` 移动到 `settings` 模块。不再使用 `from django.contrib.auth import LOGIN_URL`，请改用 `settings.LOGIN_URL`。

#### `APPEND_SLASH` 的行为已更新。

在 0.96 版本中，如果一个 URL 不以斜杠结尾，或者其路径的最后一个组件中没有句点，并且 `APPEND_SLASH` 为 True，Django 会重定向到相同的 URL，但在末尾添加斜杠。现在，Django 会检查去掉尾部斜杠的模式是否与你的 URL 模式中的某些内容匹配。如果匹配成功，不会进行重定向，因为假定你是有意捕获该模式的。

对大多数人来说，这不需要进行任何更改。然而，有些人的 URL 模式看起来像这样：

    r"/some_prefix/(.*)$"

以前，这些模式会被重定向以添加尾部斜杠。如果你希望这些 URL 始终带有斜杠，请将模式改写为：

    r"/some_prefix/(.*/)$"

### 较小的模型更改

#### 与``get()``不同的异常

现在，管理器返回 `MultipleObjectsReturned` 异常，而不再返回 `AssertionError` 异常：

老版本 (0.96):

    try:
        Model.objects.get(...)
    except AssertionError:
        handle_the_error()

新版本 (1.0):

    try:
        Model.objects.get(...)
    except Model.MultipleObjectsReturned:
        handle_the_error()

#### `LazyDate` 已删除。

`LazyDate` 帮助类不再存在。

默认字段值和查询参数都可以是可调用对象，因此可以将 `LazyDate` 的实例替换为对 `datetime.datetime.now` 的引用：

老版本 (0.96):

    class Article(models.Model):
        title = models.CharField(maxlength=100)
        published = models.DateField(default=LazyDate())

新版本 (1.0):

    import datetime

    class Article(models.Model):
        title = models.CharField(max_length=100)
        published = models.DateField(default=datetime.datetime.now)

#### `DecimalField` 是新的，而 `FloatField` 现在是真正的浮点数字段。

老版本 (0.96):

    class MyModel(models.Model):
        field_name = models.FloatField(max_digits=10, decimal_places=3)
        ...

新版本 (1.0):

    class MyModel(models.Model):
        field_name = models.DecimalField(max_digits=10, decimal_places=3)
        ...

如果你忘记进行这个更改，你会看到关于 `FloatField` 在 `__init__` 中不接受 `max_digits` 属性的错误，因为新的 `FloatField` 不接受与精度相关的参数。

如果你使用的是 MySQL 或 PostgreSQL，不需要进一步的更改。对于 `DecimalField`，数据库列类型与旧的 `FloatField` 相同。

如果你使用的是 SQLite，你需要强制数据库将相应的列视为十进制类型，而不是浮点数。要做到这一点，你需要在对代码进行更改并更新 Django 代码后重新加载数据。

Warning

首先备份你的数据库!!!!

对于SQLite，这意味着要复制存储数据库的单个文件(该文件的名称是``settings.py``文件中的``DATABASE_NAME``)。

要升级每个应用程序以使用 `DecimalField`，你可以按照以下代码进行操作，将代码中的 `<app>` 替换为每个应用程序的名称：

    $ ./manage.py dumpdata --format=xml <app> > data-dump.xml
    $ ./manage.py reset <app>
    $ ./manage.py loaddata data-dump.xml

注意：

1. 在这个过程的第一步中，重要的是记住要使用 XML 格式。我们利用了 XML 数据转储的一个特性，使得将浮点数转换为 SQLite 中的十进制数成为可能。

2. 在第二步中，你将被要求确认你是否准备好丢失相关应用程序的数据。选择是；我们将在第三步中恢复这些数据。

3. 在这个更改之前，Django 附带的应用程序中没有使用 `DecimalField`，所以你不需要担心为任何标准的 Django 模型执行此过程。

如果上述过程出现问题，只需将备份的数据库文件复制到原始文件上，然后重新开始。

### 国际化

#### `django.views.i18n.set_language()` 现在要求使用 POST 请求。

以前使用的是 GET 请求。旧的行为意味着可以通过 GET 请求更改状态（用于显示站点的区域设置），这违反了 HTTP 规范的建议。调用此视图的代码必须确保现在进行的是 POST 请求，而不是 GET 请求。这意味着你不能再使用链接来访问视图，而是必须使用某种表单提交方式（例如按钮）。

#### `_()` 不再位于内置函数中

`_()` （其名称为单个下划线的可调用对象）不再被自动添加到内置函数中，也就是说，它不再在每个模块中自动可用。

如果你之前依赖于 `_()` 总是存在，现在应该显式导入 `ugettext` 或 `ugettext_lazy` （如果适用），并自己将其别名为 `_`：

    from django.utils.translation import ugettext as _

### HTTP 请求/响应对象

#### 对于 `HttpRequest` 的字典访问：

`HttpRequest` objects no longer directly support dictionary-style access; previously, both `GET` and `POST` data were directly available on the `HttpRequest` object (e.g., you could check for a piece of form data by using `if 'some_form_key' in request` or by reading `request['some_form_key']`). This is no longer supported; if you need access to the combined `GET` and `POST` data, use `request.REQUEST` instead.

然而，强烈建议你始终明确查找你期望接收的请求类型的字典（`request.GET` 或 `request.POST`）；依赖于组合的 `request.REQUEST` 字典可能会掩盖传入数据的来源。

#### 访问``HTTPResponse`` headers

`django.http.HttpResponse.headers` 已重命名为 `_headers`，并且 `HttpResponse` 现在直接支持包含检查。所以使用 `if header in response:` 而不是 `if header in response.headers:`。

### 通用关系

#### 通用关系已从核心中移出。

通用关联类 -- `GenericForeignKey` 和 `GenericRelation` -- 已移动到 `django.contrib.contenttypes` 模块。

### 测试中

#### `django.test.Client.login()` 发生了变化

老版本 (0.96):

    from django.test import Client

    c = Client()
    c.login("/path/to/login", "myuser", "mypassword")

新版本 (1.0):

    # ... same as above, but then:
    c.login(username="myuser", password="mypassword")

### 管理命令

#### 从你的代码中运行管理命令

`django.core.management` 已经进行了大规模的重构。

你的代码中对管理服务的调用现在需要使用 `call_command`。例如，如果你有一些调用 flush 和 load_data 的测试代码：

    from django.core import management

    management.flush(verbosity=0, interactive=False)
    management.load_data(["test_data"], verbosity=0)

...你需要更改这段代码为：

    from django.core import management

    management.call_command("flush", verbosity=0, interactive=False)
    management.call_command("loaddata", "test_data", verbosity=0)

#### 现在子命令必须在选项之前。

`django-admin.py` 和 `manage.py` 现在要求子命令必须在选项之前。因此：

    $ django-admin.py --settings=foo.bar runserver

...不再起作用，应该改为：

    $ django-admin.py runserver --settings=foo.bar

### 联合

#### `Feed.__init__` 发生了变化

syndication 框架的 `Feed` 类的 `__init__()` 方法现在将 `HttpRequest` 对象作为其第二个参数，而不是 feed 的 URL。这允许 syndication 框架在不需要 sites 框架的情况下工作。这只影响到子类化 `Feed` 并覆盖 `__init__()` 方法的代码，以及直接调用 `Feed.__init__()` 的代码。

### 数据结构

#### `SortedDictFromList` 已被移除

`django.newforms.forms.SortedDictFromList` 已被移除。`django.utils.datastructures.SortedDict` 现在可以通过元组序列进行实例化。

要更新你的代码：

1. 在原先使用 `django.newforms.forms.SortedDictFromList` 的地方，现在应该使用 `django.utils.datastructures.SortedDict`。

2. 因为 `django.utils.datastructures.SortedDict.copy` 不像 `SortedDictFromList.copy()` 那样返回深拷贝，如果你依赖于深拷贝，你需要更新你的代码。通过直接使用 `copy.deepcopy` 来实现这一点。

### 数据库后端函数

#### 数据库后端函数已经改名。

几乎所有数据库后端级别的函数都已被重命名和/或移动。虽然这些函数没有被文档化，但如果你使用了其中的任何一个函数，你需要修改你的代码，所有这些函数都在 `django.db` 中：

老版本 (0.96) | 新版本 (1.0)
--- | ---
`backend.get_autoinc_sql` | `connection.ops.autoinc_sql`
`backend.get_date_extract_sql` | `connection.ops.date_extract_sql`
`backend.get_date_trunc_sql` | `connection.ops.date_trunc_sql`
`backend.get_datetime_cast_sql` | `connection.ops.datetime_cast_sql`
`backend.get_deferrable_sql` | `connection.ops.deferrable_sql`
`backend.get_drop_foreignkey_sql` | `connection.ops.drop_foreignkey_sql`
`backend.get_fulltext_search_sql` | `connection.ops.fulltext_search_sql`
`backend.get_last_insert_id` | `connection.ops.last_insert_id`
`backend.get_limit_offset_sql` | `connection.ops.limit_offset_sql`
`backend.get_max_name_length` | `connection.ops.max_name_length`
`backend.get_pk_default_value` | `connection.ops.pk_default_value`
`backend.get_random_function_sql` | `connection.ops.random_function_sql`
`backend.get_sql_flush` | `connection.ops.sql_flush`
`backend.get_sql_sequence_reset` | `connection.ops.sequence_reset_sql`
`backend.get_start_transaction_sql` | `connection.ops.start_transaction_sql`
`backend.get_tablespace_sql` | `connection.ops.tablespace_sql`
`backend.quote_name` | `connection.ops.quote_name`
`backend.get_query_set_class` | `connection.ops.query_set_class`
`backend.get_field_cast_sql` | `connection.ops.field_cast_sql`
`backend.get_drop_sequence` | `connection.ops.drop_sequence_sql`
`backend.OPERATOR_MAPPING` | `connection.operators`
`backend.allows_group_by_ordinal` | `connection.features.allows_group_by_ordinal`
`backend.allows_unique_and_pk` | `connection.features.allows_unique_and_pk`
`backend.autoindexes_primary_keys` | `connection.features.autoindexes_primary_keys`
`backend.needs_datetime_string_cast` | `connection.features.needs_datetime_string_cast`
`backend.needs_upper_for_iops` | `connection.features.needs_upper_for_iops`
`backend.supports_constraints` | `connection.features.supports_constraints`
`backend.supports_tablespaces` | `connection.features.supports_tablespaces`
`backend.uses_case_insensitive_names` | `connection.features.uses_case_insensitive_names`
`backend.uses_custom_queryset` | `connection.features.uses_custom_queryset`