将你的应用从 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 管理员模块文件里;查看 管理员 内容了解更多细节。

例如

下面的一个例子 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"}),
    )

参见

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

请注意 widthheight 属性仅对: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 中的模块方法。
  • 移除任何对 AnonymousAny 发送者选项的使用;它们不再存在。你仍然可以通过使用 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 代码后重新加载数据。

警告

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

对于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 请求。这意味着你不能再使用链接来访问视图,而是必须使用某种表单提交方式(例如按钮)。

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

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

如果你之前依赖于 _() 总是存在,现在应该显式导入 ugettextugettext_lazy (如果适用),并自己将其别名为 _

from django.utils.translation import ugettext as _

HTTP 请求/响应对象

对于 HttpRequest 的字典访问:

HttpRequest 对象不再直接支持字典风格的访问;以前,GETPOST 数据都可以直接在 HttpRequest 对象上使用(例如,你可以通过 if 'some_form_key' in request 或读取 request['some_form_key'] 来检查表单数据。不再支持这种方式;如果你需要访问组合的 GETPOST 数据,请使用 request.REQUEST

然而,强烈建议你始终明确查找你期望接收的请求类型的字典(request.GETrequest.POST);依赖于组合的 request.REQUEST 字典可能会掩盖传入数据的来源。

访问``HTTPResponse`` headers

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

通用关系

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

通用关联类 -- GenericForeignKeyGenericRelation -- 已移动到 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.pymanage.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
Back to Top