# Django 3.2.11.dev 文档

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

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

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

参见

文档 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` 管理员模块文件里；查看 管理员 内容了解更多细节。

参见

对 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 u'%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'}),
        )

参见

- 更多关于变更的详细信息以及背后的原因都可以在 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`

#### Work with file fields using the new API

The internal implementation of `django.db.models.FileField` have changed. A visible result of this is that the way you access special attributes (URL, filename, image size, etc.) of these model fields has changed. You will need to make the following changes, assuming your model's `FileField` is called `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`

Note that the `width` and `height` attributes only make sense for `ImageField` fields. More details can be found in the model API documentation.

#### Use `Paginator` instead of `ObjectPaginator`

The `ObjectPaginator` in 0.96 has been removed and replaced with an improved version, `django.core.paginator.Paginator`.

### 模板

#### Learn to love autoescaping

By default, the template system now automatically HTML-escapes the output of every variable. To learn more, see 自动 HTML 转义.

To disable auto-escaping for an individual variable, use the `safe` filter:

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

To disable auto-escaping for an entire template, wrap the template (or just a particular section of the template) in the `autoescape` tag:

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

## Less-common changes

The following changes are smaller, more localized changes. They should only affect more advanced users, but it's probably worth reading through the list and checking your code for these things.

### 信号

- Add `**kwargs` to any registered signal handlers.
- Connect, disconnect, and send signals via methods on the `Signal` object instead of through module methods in `django.dispatch.dispatcher`.
- Remove any use of the `Anonymous` and `Any` sender options; they no longer exist. You can still receive signals sent by any sender by using `sender=None`
- Make any custom signals you've declared into instances of `django.dispatch.Signal` instead of anonymous objects.

Here's quick summary of the code changes you'll need to make:

老版本 (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)`

### 评论

If you were using Django 0.96's `django.contrib.comments` app, you'll need to upgrade to the new comments app introduced in 1.0. See the upgrade guide for details.

### 模板标签

#### `spaceless` 标签

The `spaceless` template tag now removes all spaces between HTML tags, instead of preserving a single space.

### Local flavors

#### U.S. local flavor

`django.contrib.localflavor.usa` has been renamed to `django.contrib.localflavor.us`. This change was made to match the naming scheme of other local flavors. To migrate your code, all you need to do is change the imports.

### 会话

#### Getting a new session key

`SessionBase.get_new_session_key()` has been renamed to `_get_new_session_key()`. `get_new_session_object()` no longer exists.

### 辅助工具

#### Loading a row no longer calls `save()`

Previously, loading a row automatically ran the model's `save()` method. This is no longer the case, so any fields (for example: timestamps) that were auto-populated by a `save()` now need explicit values in any fixture.

### 配置

#### Better exceptions

The old `EnvironmentError` has split into an `ImportError` when Django fails to find the settings module and a `RuntimeError` when you try to reconfigure settings after having already used them.

#### `LOGIN_URL` has moved

The `LOGIN_URL` constant moved from `django.contrib.auth` into the `settings` module. Instead of using `from django.contrib.auth import LOGIN_URL` refer to `settings.LOGIN_URL`.

#### `APPEND_SLASH` behavior has been updated

In 0.96, if a URL didn't end in a slash or have a period in the final component of its path, and `APPEND_SLASH` was True, Django would redirect to the same URL, but with a slash appended to the end. Now, Django checks to see whether the pattern without the trailing slash would be matched by something in your URL patterns. If so, no redirection takes place, because it is assumed you deliberately wanted to catch that pattern.

For most people, this won't require any changes. Some people, though, have URL patterns that look like this:

    r'/some_prefix/(.*)$'

Previously, those patterns would have been redirected to have a trailing slash. If you always want a slash on such URLs, rewrite the pattern as:

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

### Smaller model changes

#### Different exception from `get()`

Managers now return a `MultipleObjectsReturned` exception instead of `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` has been fired

The `LazyDate` helper class no longer exists.

Default field values and query arguments can both be callable objects, so instances of `LazyDate` can be replaced with a reference to `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` is new, and `FloatField` is now a proper float

老版本 (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)
        ...

If you forget to make this change, you will see errors about `FloatField` not taking a `max_digits` attribute in `__init__`, because the new `FloatField` takes no precision-related arguments.

If you're using MySQL or PostgreSQL, no further changes are needed. The database column types for `DecimalField` are the same as for the old `FloatField`.

If you're using SQLite, you need to force the database to view the appropriate columns as decimal types, rather than floats. To do this, you'll need to reload your data. Do this after you have made the change to using `DecimalField` in your code and updated the Django code.

警告

Back up your database first!

For SQLite, this means making a copy of the single file that stores the database (the name of that file is the `DATABASE_NAME` in your settings.py file).

To upgrade each application to use a `DecimalField`, you can do the following, replacing `<app>` in the code below with each app's name:

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

注意：

1. It's important that you remember to use XML format in the first step of this process. We are exploiting a feature of the XML data dumps that makes porting floats to decimals with SQLite possible.
2. In the second step you will be asked to confirm that you are prepared to lose the data for the application(s) in question. Say yes; we'll restore this data in the third step.
3. `DecimalField` is not used in any of the apps shipped with Django prior to this change being made, so you do not need to worry about performing this procedure for any of the standard Django models.

If something goes wrong in the above process, just copy your backed up database file over the original file and start again.

### 国际化

#### `django.views.i18n.set_language()` now requires a POST request

Previously, a GET request was used. The old behavior meant that state (the locale used to display the site) could be changed by a GET request, which is against the HTTP specification's recommendations. Code calling this view must ensure that a POST request is now made, instead of a GET. This means you can no longer use a link to access the view, but must use a form submission of some kind (e.g. a button).

#### `_()` is no longer in builtins

`_()` (the callable object whose name is a single underscore) is no longer monkeypatched into builtins -- that is, it's no longer available magically in every module.

If you were previously relying on `_()` always being present, you should now explicitly import `ugettext` or `ugettext_lazy`, if appropriate, and alias it to `_` yourself:

    from django.utils.translation import ugettext as _

### HTTP request/response objects

#### Dictionary access to `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.

It is strongly suggested, however, that you always explicitly look in the appropriate dictionary for the type of request you expect to receive (`request.GET` or `request.POST`); relying on the combined `request.REQUEST` dictionary can mask the origin of incoming data.

#### Accessing `HTTPResponse` headers

`django.http.HttpResponse.headers` has been renamed to `_headers` and `HttpResponse` now supports containment checking directly. So use `if header in response:` instead of `if header in response.headers:`.

### 通用关系

#### Generic relations have been moved out of core

The generic relation classes -- `GenericForeignKey` and `GenericRelation` -- have moved into the `django.contrib.contenttypes` module.

### 测试中

#### `django.test.Client.login()` has changed

老版本 (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')

### Management commands

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

`django.core.management` has been greatly refactored.

Calls to management services in your code now need to use `call_command`. For example, if you have some test code that calls flush and load_data:

    from django.core import management
    management.flush(verbosity=0, interactive=False)
    management.load_data(['test_data'], verbosity=0)

...you'll need to change this code to read:

    from django.core import management
    management.call_command('flush', verbosity=0, interactive=False)
    management.call_command('loaddata', 'test_data', verbosity=0)

#### Subcommands must now precede options

`django-admin.py` and `manage.py` now require subcommands to precede options. So:

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

...no longer works and should be changed to:

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

### 联合

#### `Feed.__init__` has changed

The `__init__()` method of the syndication framework's `Feed` class now takes an `HttpRequest` object as its second parameter, instead of the feed's URL. This allows the syndication framework to work without requiring the sites framework. This only affects code that subclasses `Feed` and overrides the `__init__()` method, and code that calls `Feed.__init__()` directly.

### Data structures

#### `SortedDictFromList` is gone

`django.newforms.forms.SortedDictFromList` was removed. `django.utils.datastructures.SortedDict` can now be instantiated with a sequence of tuples.

To update your code:

1. Use `django.utils.datastructures.SortedDict` wherever you were using `django.newforms.forms.SortedDictFromList`.
2. Because `django.utils.datastructures.SortedDict.copy` doesn't return a deepcopy as `SortedDictFromList.copy()` did, you will need to update your code if you were relying on a deepcopy. Do this by using `copy.deepcopy` directly.

### Database backend functions

#### Database backend functions have been renamed

Almost all of the database backend-level functions have been renamed and/or relocated. None of these were documented, but you'll need to change your code if you're using any of these functions, all of which are in `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`