将你的应用从 Django 0.96 移植到 1.0 — Django 文档

来自菜鸟教程
Django/docs/3.2.x/releases/1.0-porting-guide
跳转至:导航、​搜索

将您的应用程序从 Django 0.96 移植到 1.0

Django 1.0 在某些方面打破了与 0.96 的兼容性。

本指南将帮助您将 0.96 项目和应用程序移植到 1.0。 本文档的第一部分包括运行 1.0 所需的常见更改。 如果在完成第一部分后您的代码仍然中断,请查看 不太常见的更改 部分以获取一系列不太常见的兼容性问题的列表。

也可以看看

1.0 发行说明。 该文档更深入地解释了 1.0 中的新功能; 移植指南更关心帮助您快速更新代码。


常见变化

本节介绍大多数用户需要在 0.96 和 1.0 之间进行的更改。

使用 Unicode

将字符串文字 ('foo') 更改为 Unicode 文字 (u'foo')。 Django 现在自始至终都使用 Unicode 字符串。 在大多数地方,原始字符串将继续工作,但更新为使用 Unicode 文字将防止一些晦涩的问题。

有关完整详细信息,请参阅 Unicode 数据


型号

对模型文件的常见更改:

将 maxlength 重命名为 max_length

maxlength 参数重命名为 max_length(已更改为与表单字段一致):


将 __str__ 替换为 __unicode__

将模型的 __str__ 函数替换为 __unicode__ 方法,并确保在该方法中 使用 Unicode (u'foo')。


移除 prepopulated_from

删除模型字段上的 prepopulated_from 参数。 它不再有效并已移至 admin.py 中的 ModelAdmin 类。 有关管理员更改的更多详细信息,请参阅下面的 管理员


移除 core

从模型字段中删除 core 参数。 不再需要它,因为等效功能( 内联编辑 的一部分)现在由管理界面以不同方式处理。 在进入下面的 管理 部分之前,您不必担心内联编辑。 现在,删除对 core 的所有引用。


将 class Admin: 替换为 admin.py

从模型中删除所有内部 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'}),
    )

也可以看看


网址

更新您的根 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.newforms 替换为 django.forms – Django 1.0 将 newforms 模块(在 0.96 中引入)重命名为普通的 formsoldforms 模块也被移除。

如果您已经在使用 newforms 库,并且您使用了我们推荐的 import 语句语法,那么您所要做的就是更改导入语句。

老的:

from django import newforms as forms

新的:

from django import forms

如果您使用旧的表单系统(以前称为 django.formsdjango.oldforms),则必须重新编写表单。 一个好的起点是 表单文档


使用新 API 处理上传的文件

使用新的 UploadedFile 替换上传文件的使用 - 即 request.FILES 中的条目 - 作为简单字典。 旧的字典语法不再有效。

因此,在如下视图中:

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 处理文件字段

django.db.models.FileField 的内部实现已经改变。 一个明显的结果是您访问这些模型字段的特殊属性(URL、文件名、图像大小等)的方式发生了变化。 您需要进行以下更改,假设您模型的 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 属性仅对 ImageField 字段有意义。 更多细节可以在 模型 API 文档中找到。


使用 Paginator 代替 ObjectPaginator

0.96 中的 ObjectPaginator 已被移除并替换为改进版本 django.core.paginator.Paginator


模板

学会喜欢自动转义

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

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

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

要禁用整个模板的自动转义,请将模板(或仅模板的特定部分)包装在 :ttag:`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 中引入的新评论应用程序。 有关详细信息,请参阅升级指南。


模板标签

:ttag:`spaceless` 标签

spaceless 模板标签现在删除了 HTML 标签之间的 all 空格,而不是保留一个空格。


地方风味

我们 地方风味

django.contrib.localflavor.usa 已重命名为 django.contrib.localflavor.us。 进行此更改是为了匹配其他本地风味的命名方案。 要迁移您的代码,您需要做的就是更改导入。


会话

获取新的会话密钥

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


灯具

加载一行不再调用 save()

以前,加载一行会自动运行模型的 save() 方法。 现在不再是这种情况,因此任何由 save() 自动填充的字段(例如:时间戳)现在都需要在任何装置中显式值。


设置

更好的例外

当 Django 无法找到设置模块时,旧的 EnvironmentError 已拆分为 ImportError,当您尝试重新配置已使用设置后的设置时,已拆分为 RuntimeError


:setting:`LOGIN_URL` 已移动

:setting:`LOGIN_URL` 常量从 django.contrib.auth 移动到 settings 模块。 而不是使用from django.contrib.auth import LOGIN_URL参考 :setting:`settings.LOGIN_URL ` .


:setting:`APPEND_SLASH` 行为已更新

在 0.96 中,如果 URL 不以斜杠结尾或在其路径的最后一部分中没有句点,并且 :setting:`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,则需要强制数据库将适当的列查看为十进制类型,而不是浮点数。 为此,您需要重新加载数据。 在更改为在代码中使用 DecimalField 并更新 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. DecimalField 在进行此更改之前未在 Django 附带的任何应用程序中使用,因此您无需担心对任何标准 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 标头

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__ 已更改

联合框架的 Feed 类的 __init__() 方法现在采用 HttpRequest 对象作为其第二个参数,而不是提要的 URL。 这允许联合框架在不需要站点框架的情况下工作。 这只影响子类 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