将你的应用从 Django 0.96 移植到 1.0Link to this heading

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

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

常见变更Link to this heading

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

使用 Unicode 字符集Link to this heading

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

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

模型Link to this heading

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

maxlength 重命名为 max_lengthLink to this heading

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

__unicode__ 代替 __str__Link to this heading

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

移除 prepopulated_fromLink to this heading

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

移除 coreLink to this heading

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

admin.py 替换 class Admin:Link to this heading

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

例如Link to this heading

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

老版本 (0.96) models.py:

Python
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:

Python
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``管理员模块:

Python
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)

管理员Link to this heading

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

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

使用新的行内句法Link to this heading

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

老版本 (0.96):

Python
class Parent(models.Model):
    ...

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

新版本 (1.0):

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

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

admin.site.register(Parent, ParentAdmin)

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

简化 fields,或使用 fieldsetsLink to this heading

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

老版本 (0.96):

Python
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):

Python
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'}),
    )

URLsLink to this heading

更新你的根路径 urls.py 模块Link to this heading

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

老版本 (0.96) urls.py:

Python
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:

Python
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 ...
)

视图Link to this heading

使用 django.forms 来代替 newforms 用法Link to this heading

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

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

老版本:

Python
from django import newforms as forms

新版本:

Python
from django import forms

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

处理上传文件使用新的 APILink to this heading

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

因此,看起来就像:

Python
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处理Link to this heading

class:django.db.models.FileField 的内部实现已更改。一个明显的结果是您访问特殊属性(URL, filename, image size, 等等)的方式发生了变化。您需要进行以下更改,假设您的模型: class:~django.db.models.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 字段有意义。更多详细信息可以在: doc:model API</ref/models/fields> documentation 文档中找到。

使用 Paginator 而不是 ObjectPaginatorLink to this heading

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

模板Link to this heading

学会爱上自动转义Link to this heading

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

要禁用单个变量的自动转义,请使用:tfilter:safe filter:

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

要禁用整个模板的自动转义,请将模板(或模板的特定部分)包装在:ttag:autoescape tag:

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

不常见的变化Link to this heading

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

信号Link to this heading

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

  • 通过:class:`~django.dispatch.Signal`对象上的方法连接、断开连接和发送信号,而不是通过``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)

评论Link to this heading

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.

模板标签Link to this heading

spaceless 标签Link to this heading

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

Local flavorsLink to this heading

U.S. local flavorLink to this heading

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.

会话Link to this heading

Getting a new session keyLink to this heading

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

辅助工具Link to this heading

Loading a row no longer calls save()Link to this heading

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.

配置Link to this heading

Better exceptionsLink to this heading

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 movedLink to this heading

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 updatedLink to this heading

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:

Python
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:

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

Smaller model changesLink to this heading

Different exception from get()Link to this heading

Managers now return a MultipleObjectsReturned exception instead of AssertionError:

老版本 (0.96):

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

新版本 (1.0):

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

LazyDate has been firedLink to this heading

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):

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

新版本 (1.0):

Python
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 floatLink to this heading

老版本 (0.96):

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

新版本 (1.0):

Python
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.

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

Shell
$ ./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.

国际化Link to this heading

django.views.i18n.set_language() now requires a POST requestLink to this heading

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 builtinsLink to this heading

_() (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:

Python
from django.utils.translation import ugettext as _

HTTP request/response objectsLink to this heading

Dictionary access to HttpRequestLink to this heading

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 headersLink to this heading

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:.

通用关系Link to this heading

Generic relations have been moved out of coreLink to this heading

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

测试中Link to this heading

django.test.Client.login() has changedLink to this heading

老版本 (0.96):

Python
from django.test import Client
c = Client()
c.login('/path/to/login','myuser','mypassword')

新版本 (1.0):

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

Management commandsLink to this heading

从你的代码中运行管理命令Link to this heading

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:

Python
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:

Python
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 optionsLink to this heading

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

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

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

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

联合Link to this heading

Feed.__init__ has changedLink to this heading

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 structuresLink to this heading

SortedDictFromList is goneLink to this heading

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 functionsLink to this heading

Database backend functions have been renamedLink to this heading

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