<> = Removing the magic = "magic-removal" 分支力图扫除 Django 代码库中的近两年来积攒的缺陷. 大部分变化集中在数据库 API 及移除一些不必要的 magic, 其它一些变化主要在于提高了框架的简单性和可用性. 这些变化将被整合在下一个 Django 发行版中, 也就是0.92. 本文档说明了该分枝带来的变化. [[TOC(inline, RemovingTheMagic)]] == 如何得到这个分枝 == 欢迎试用! 通过 Subversion 你可以得到该分支. 地址是: http://code.djangoproject.com/svn/django/branches/magic-removal . === 在一个机器上同时运行两个版本的 Django === Here's one way to use Django's trunk and magic-removal branch on the same machine. This assumes Django's trunk (or a release such as 0.90 or 0.91) is installed: {{{ # Get the magic-removal code somewhere on your filesystem. In this example, we use /home/python/django. $ cd /home/python/django $ svn co http://code.djangoproject.com/svn/django/branches/magic-removal # This will have created a "magic-removal" directory. # Whenever you want to use magic-removal, set the environment variable {{{PYTHONPATH}}} to the directory containing magic-removal. export PYTHONPATH=/home/python/django/magic-removal }}} == 概述 == magic-removal 中最大的改变是: * 去除了另人费解的 {{{django.models}}} 包. 要使用 models, 不论它是否在 Python 路径中, 直接导入 model 类即可. 同样的,另人费解的 modules (比如教程中的 {{{django.models.polls}}}) 也不再存在了, 你现在直接操作 model class 就可以. * 删除了所有的自动复数行为. * 数据库 API 以不同的方式进行了改变. * 多个包, 比如 Django 模板系统(以前位于 {{{django.core.template}}}), 现在访问这些包更直接和更易记了. == Status == 希望这个消息不会让你昏倒, Django 仍在紧锣密鼓的开发当中. == 你需要做的数据库改变 == 要升级你以前安装的 Django , 你需要对数据库做一些改变. 特别是, 当你数据库已经有了很多数据时. === 对核心数据表进行了重新命名 === 我们对一系列核心 Django 表进行了重命名. 要更新 MySQL 和 SQLite, 执行这些SQL语句: {{{ ALTER TABLE auth_groups RENAME TO auth_group; ALTER TABLE auth_groups_permissions RENAME TO auth_group_permissions; ALTER TABLE auth_messages RENAME TO auth_message; ALTER TABLE auth_permissions RENAME TO auth_permission; ALTER TABLE auth_users RENAME TO auth_user; ALTER TABLE auth_users_groups RENAME TO auth_user_groups; ALTER TABLE auth_users_user_permissions RENAME TO auth_user_user_permissions; ALTER TABLE content_types RENAME TO django_content_type; ALTER TABLE core_sessions RENAME TO django_session; ALTER TABLE django_flatpages RENAME TO django_flatpage; ALTER TABLE django_flatpages_sites RENAME TO django_flatpage_sites; ALTER TABLE django_redirects RENAME TO django_redirect; ALTER TABLE sites RENAME TO django_site; DROP TABLE packages; ALTER TABLE django_content_type rename package to app_label; ALTER TABLE django_content_type rename python_module_name to model; }}} PostgreSQL 则不同, 要更新 PostgreSQL, 执行下列 SQL 语句: {{{ BEGIN; ALTER TABLE auth_groups RENAME TO auth_group; ALTER TABLE auth_groups_id_seq RENAME TO auth_group_id_seq; ALTER TABLE auth_group ALTER COLUMN id DROP DEFAULT; ALTER TABLE auth_group ALTER COLUMN id SET DEFAULT nextval('public.auth_group_id_seq'::text); ALTER TABLE auth_groups_permissions RENAME TO auth_group_permissions; ALTER TABLE auth_groups_permissions_id_seq RENAME TO auth_group_permissions_id_seq; ALTER TABLE auth_group_permissions ALTER COLUMN id DROP DEFAULT; ALTER TABLE auth_group_permissions ALTER COLUMN id SET DEFAULT nextval('public.auth_group_permissions_id_seq'::text); ALTER TABLE auth_messages RENAME TO auth_message; ALTER TABLE auth_messages_id_seq RENAME TO auth_message_id_seq; ALTER TABLE auth_message ALTER COLUMN id DROP DEFAULT; ALTER TABLE auth_message ALTER COLUMN id SET DEFAULT nextval('public.auth_message_id_seq'::text); ALTER TABLE auth_permissions RENAME TO auth_permission; ALTER TABLE auth_permissions_id_seq RENAME TO auth_permission_id_seq; ALTER TABLE auth_permission ALTER COLUMN id DROP DEFAULT; ALTER TABLE auth_permission ALTER COLUMN id SET DEFAULT nextval('public.auth_permission_id_seq'::text); ALTER TABLE auth_users RENAME TO auth_user; ALTER TABLE auth_users_id_seq RENAME TO auth_user_id_seq; ALTER TABLE auth_user ALTER COLUMN id DROP DEFAULT; ALTER TABLE auth_user ALTER COLUMN id SET DEFAULT nextval('public.auth_user_id_seq'::text); ALTER TABLE auth_users_groups RENAME TO auth_user_groups; ALTER TABLE auth_users_groups_id_seq RENAME TO auth_user_groups_id_seq; ALTER TABLE auth_user_groups ALTER COLUMN id DROP DEFAULT; ALTER TABLE auth_user_groups ALTER COLUMN id SET DEFAULT nextval('public.auth_user_groups_id_seq'::text); ALTER TABLE auth_users_user_permissions RENAME TO auth_user_user_permissions; ALTER TABLE auth_users_user_permissions_id_seq RENAME TO auth_user_user_permissions_id_seq; ALTER TABLE auth_user_user_permissions ALTER COLUMN id DROP DEFAULT; ALTER TABLE auth_user_user_permissions ALTER COLUMN id SET DEFAULT nextval('public.auth_user_user_permissions_id_seq'::text); ALTER TABLE content_types RENAME TO django_content_type; ALTER TABLE content_types_id_seq RENAME TO django_content_type_id_seq; ALTER TABLE django_content_type ALTER COLUMN id DROP DEFAULT; ALTER TABLE django_content_type ALTER COLUMN id SET DEFAULT nextval('public.django_content_type_id_seq'::text); ALTER TABLE core_sessions RENAME TO django_session; ALTER TABLE django_flatpages RENAME TO django_flatpage; ALTER TABLE django_flatpages_id_seq RENAME TO django_flatpage_id_seq; ALTER TABLE django_flatpage ALTER COLUMN id DROP DEFAULT; ALTER TABLE django_flatpage ALTER COLUMN id SET DEFAULT nextval('public.django_flatpage_id_seq'::text); ALTER TABLE django_flatpages_sites RENAME TO django_flatpage_sites; ALTER TABLE django_flatpages_sites_id_seq RENAME TO django_flatpage_sites_id_seq; ALTER TABLE django_flatpage_sites ALTER COLUMN id DROP DEFAULT; ALTER TABLE django_flatpage_sites ALTER COLUMN id SET DEFAULT nextval('public.django_flatpage_sites_id_seq'::text); ALTER TABLE django_redirects RENAME TO django_redirect; ALTER TABLE django_redirects_id_seq RENAME TO django_redirect_id_seq; ALTER TABLE django_redirect ALTER COLUMN id DROP DEFAULT; ALTER TABLE django_redirect ALTER COLUMN id SET DEFAULT nextval('public.django_redirect_id_seq'::text); ALTER TABLE auth_permission DROP COLUMN package; ALTER TABLE django_content_type DROP CONSTRAINT "$1"; DROP TABLE packages; ALTER TABLE sites RENAME TO django_site; ALTER TABLE sites_id_seq RENAME TO django_site_id_seq; ALTER TABLE django_site ALTER COLUMN id DROP DEFAULT; ALTER TABLE django_site ALTER COLUMN id SET DEFAULT nextval('public.django_site_id_seq'::text); ALTER TABLE django_content_type rename package to app_label; ALTER TABLE django_content_type rename python_module_name to model; COMMIT; }}} === Database 表命名方式做了改动 === 以前 Database 表的名字通过连接 {{{app_label}}} 和 {{{module_name}}} 来生成. 比如: {{{polls_polls}}}. 由于现在已经没有了 {{{module_name}}} 的概念, 数据表的名字现在被设计成通过连接 {{{app_label}}} 和 model 类的名字(小写)来自动生成.比如: {{{polls_poll}}}. 和以前一样, 这种行为可以通过在你的 model 中的内嵌类 {{{class Meta}}} 的指定{{{db_table}}}属性来改变.. 你要么显式的指定 {{{db_table}}} 以适应新的命名方式, 也可以直接在数据库中对数据表进行改名.我们建议设置 {{{db_table}}}, 因为这个更容易一些. == 你需要做的代码更改 == === Model 类 及 Field 类 重命名/重定位 === 导入 {{{models}}} 由原来的{{{ from django.core.meta }}}变更为{{{ from django.db.models}}}. {{{ #!python from django.db import models class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) }}} === 直接访问你的 model 类, 而不是通过那个令人费解的中间模块(magic modules) === 直接从定义model类的模块导入 model 类, 去掉了 {{{django.models.*}}}. {{{ #!python from myproject.people.models import Person p = Person(first_name='John', last_name='Smith') p.save() }}} === 名字空间简化 === {{{django.utils.httpwrappers}}} 变更为 {{{django.http}}}. {{{django.core.exceptions.Http404}}} 变更为 {{{django.http.Http404}}}. {{{django.core.template}}} 变更为 {{{django.template}}}. {{{django.core.formfields}}} 变更为 {{{django.forms}}}. {{{django.core.extensions}}} 变更为 {{{django.shortcuts}}}. {{{django.core.extensions.DjangoContext}}} 变更为 {{{django.template.RequestContext}}}. 需要从你的settings文件中的 {{{TEMPLATE_LOADERS}}} 设置值中的将 ".core" 删除, 旧代码: {{{ #!python TEMPLATE_LOADERS = ( 'django.core.template.loaders.filesystem.load_template_source', 'django.core.template.loaders.app_directories.load_template_source', # 'django.core.template.loaders.eggs.load_template_source', ) }}} 新代码: {{{ #!python TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load_template_source', 'django.template.loaders.app_directories.load_template_source', # 'django.template.loaders.eggs.load_template_source', ) }}} 自定义模板标签也需要做一个类似的修改.旧代码: {{{ #!python from django.core import template register = template.Library() }}} 新代码: {{{ #!python from django.template import Library register = Library() }}} "auth" 和 "core" models 被分开且被移动到 {{{django.contrib}}}, 具体如下: * {{{django.models.auth}}} 变更为 {{{django.contrib.auth.models}}}. * {{{django.models.core.sites}}} 变更为 {{{django.contrib.sites.models}}}. * {{{django.models.core.contenttypes}}} 变更为 {{{django.contrib.contenttypes.models}}}. * {{{django.models.core.packages}}} 变更为 {{{django.contrib.contenttypes.models}}}. (注意在 magic-removal 完成之前,那个 "packages" 正在逐步被去除.) 如果你正使用session的话, Session 中间件从 {{{django.middleware.sessions.SessionMiddleware}}} 被移动到 {{{django.contrib.sessions.middleware.SessionMiddleware}}}. 请确保更新你的 {{{MIDDLEWARE_CLASSES}}} 设置. 同样的, {{{Session}}} model 由 django/models/core.py 被移动到 django/contrib/sessions/models.py. 如果你正使用 {{{Session}}} model ,一定要注意这个改变. === 'packages' 模块不再存在 === Packages 不再存在 (它们实在多余). 如果已经完成依据 content-types 或 permissions 查询功能,你需要稍微修改一下你的代码: || 旧代码 || 新代码 || || {{{contenttypes.get_list(package__label__exact='foo')}}} || {{{ContentType.objects.filter(package__exact='foo')}}} || || {{{permissions.get_list(package__label__exact='foo')}}} || {{{Permission.objects.filter(package__exact='foo')}}} || === model 语法的变更 === * {{{class META}}} 变成了 {{{class Meta}}}. * 以下不再是 {{{class Meta}}} 的合法参数: * {{{module_name}}} * {{{admin}}} (参阅下文中的 "Moved admin options to 'class Admin'" 小节.) * {{{exceptions}}} (只要在将你的异常放到包含 models 的模块中访问即可) * {{{module_constants}}} (只要在将定义的常数放到包含 models 的模块中访问即可) * {{{where_constraints}}} (用一个自定管理器就可以. 参阅下文中的 "自定义管理器,及多管理器") === Moved admin options to 'class Admin' === 不要再从 {{{class META}}} 定义 {{{admin=meta.Admin}}} 参数 , 所有的管理选项都放到了 {{{class Admin}}} 内. 旧代码: {{{ #!python class Person(meta.Model): first_name = meta.CharField(maxlength=30) last_name = meta.CharField(maxlength=30) class META: admin = meta.Admin( list_display = ('first_name', 'last_name') ) }}} 新代码: {{{ #!python class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) class Admin: list_display = ('first_name', 'last_name') }}} 如果你使用 admin 界面却没有指定任何 admin 选项, 只要在那个内嵌类中放一个 {{{pass}}} 语句. 旧代码: {{{ #!python class Person(meta.Model): first_name = meta.CharField(maxlength=30) last_name = meta.CharField(maxlength=30) class META: admin = meta.Admin() }}} 新代码: {{{ #!python class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) class Admin: pass }}} === 数据库连接重命名/重定位 === 对于任何使用原始数据库连接的代码, 使用 {{{django.db.connection}}} ,不再使用 {{{django.core.db.db}}}. 旧代码: {{{ #!python from django.core.db import db cursor = db.cursor() }}} 新代码: {{{ #!python from django.db import connection cursor = connection.cursor() }}} 如果你需要那些 Backend-specific 函数, 他们现在位于 {{{django.db.backend}}}. 旧代码: {{{ #!python from django.core import db db.quote_name('foo') }}} 新代码: {{{ #!python from django.db import backend backend.quote_name('foo') }}} 同样的, 不同的 backend 功能已经分到三个不同的模块中 -- {{{base.py}}}, {{{creation.py}}} 和 {{{introspection.py}}}. 之所以这样修改是为了改善性能和节省内存, 这样一来, Django 日常使用就不必将内省模块载入内存. === Model 方法不再自动访问 datetime 和 db 模块 === 以前, 每个 model 方法都能访问 {{{datetime}}} 模块及 {{{db}}} 变量(表示当前的数据库连接). 现在你必须显式的导入它们. 旧代码: {{{ #!python def some_method(self): print datetime.datetime.now() cursor = db.cursor() cursor.execute("UPDATE something;") }}} 新代码: {{{ #!python import datetime from django.db import connection # ... def some_method(self): print datetime.datetime.now() cursor = connection.cursor() cursor.execute("UPDATE something;") }}} === Descriptor fields === 所有 "table-level" 的函数 -- (在表范围内得到数据记录的方式胜于 performing instance-specific tasks 的方式) -- 现在通过一个 model 类的 {{{objects}}} 属性访问.它们不是一个 model 实例对象的直接方法, 因为我们希望保持 "表范围" 和 "行范围" 两个不同的名字空间. 一个 model 类的 {{{objects}}} 属性是一个 {{{django.db.models.manager.Manager}}} 类的实例. 一个管理器有一系列方法,所有这些方法都返回一个 {{{QuerySet}}} 实例. * {{{all()}}} -- 返回一个包含数据表中所有对象的 {{{QuerySet}}} 对象. 它就象过去的老的 {{{get_list()}}} 方法. 它不接受任何参数. * {{{filter(**kwargs)}}} -- 返回一个 {{{QuerySet}}} 对象, 按给定的关键字参数过滤结果集. 查询参数的风格与以前一样, 比如 {{{pubdate__year=2005}}}, 一个小改动就是你现在可以在关键字中加省略 {{{__exact}}} 了(更加方便). 举例来说, {{{name='John'}}} 与 {{{name__exact='John'}}} 是等价的. 注意一点如果跨应用程序查询时你不能省略 {{{__exact}}}. * {{{exclude(**kwargs)}}} 类似 {{{filter()}}}, 不过它是当给定参数为假时返回结果集. * {{{order_by(*fieldnames)}}} -- 返回一个 {{{QuerySet}}} * {{{count()}}} -- 返回指定数据库中的对象个数. * {{{dates(field_name, kind)}}} -- 类似过去的用于日期字段的 {{{get_FIELD_list()}}} . 举例来说, 过去的 {{{get_pubdate_list('year')}}} 现在变成了 {{{dates('pubdate', 'year')}}}. * {{{delete()}}} -- 删除所有对象. * {{{distinct()}}} -- 返回一个无重复记录的 {{{QuerySet}}} . * {{{extra(select=None, where=None, params=None, tables=None)}}} -- 设置 {{{select}}}, {{{where}}}, {{{params}}} 和 {{{tables}}} 参数, 参数格式与以前相同. * {{{get(**kwargs)}}} -- 类似旧的 {{{get_object()}}}. 返回一个对象或引发 {{{DoesNotExist}}} 异常. * {{{in_bulk(id_list)}}} -- 类似旧的 {{{get_in_bulk()}}}. * {{{iterator()}}} -- 返回一个生成器以迭代结果集. * {{{select_related()}}} -- 返回一个带有 "select related" 选项的 {{{QuerySet}}}(未做改动). * {{{values(*fieldnames)}}} -- 类似旧的 {{{get_values()}}}. 每个 {{{QuerySet}}} 都有下列方法, 返回一个经过适当处理的当前结果集的克隆: * {{{filter(**kwargs)}}} * {{{order_by(*fieldnames)}}} * {{{iterator()}}} * {{{count()}}} * {{{get(**kwargs)}}} * {{{delete()}}} * {{{filter(**kwargs)}}} * {{{select_related()}}} * {{{order_by(*fieldnames)}}} * {{{distinct()}}} * {{{extra(select=None, where=None, params=None, tables=None)}}} 下面是一些例子,使用下面的 models: {{{ #!python class Reporter(models.Model): fname = models.CharField(maxlength=30) lname = models.CharField(maxlength=30) class Site(models.Model): name = models.CharField(maxlength=20) class Article(models.Model): headline = models.CharField(maxlength=50) reporter = models.ForeignKey(Reporter) pub_date = models.DateField() sites = models.ManyToManyField(Site) }}} || '''旧的语法''' || '''新的语法''' || || {{{reporters.get_list()}}} || {{{Reporter.objects.all()}}} || || {{{reporters.get_list(fname__exact='John')}}} || {{{Reporter.objects.filter(fname='John')}}} || || {{{reporters.get_list(order_by=('-lname', 'fname'))}}} || {{{Reporter.objects.order_by('-lname', 'fname')}}} || || {{{reporters.get_list(fname__exact='John', order_by=('lname',))}}} || {{{Reporter.objects.filter(fname='John').order_by('lname')}}} || || {{{reporters.get_object(pk=3)}}} || {{{Reporter.objects.get(pk=3)}}} || || {{{reporters.get_object(complex=(Q(...)|Q(...)))}}} || {{{Reporter.objects.get(Q(...)|Q(...))}}}|| || {{{reporters.get_object(fname__contains='John')}}} || {{{Reporter.objects.get(fname__contains='John')}}} || || {{{reporters.get_list(fname__ne='John')}}} || {{{Reporter.objects.exclude(fname='John')}}} (note that {{{ne}}} is no longer a valid lookup type) || || (not previously possible) || {{{Reporter.objects.exclude(fname__contains='n')}}} || || {{{reporters.get_list(distinct=True)}}} || {{{Reporter.objects.distinct()}}} || || {{{reporters.get_list(offset=10, limit=5)}}} || {{{Reporter.objects.all()[10:15]}}}|| || {{{reporters.get_values()}}} || {{{Reporter.objects.values()}}} || || {{{reporters.get_in_bulk([1, 2])}}} || {{{Reporter.objects.in_bulk([1, 2])}}} || || {{{reporters.get_in_bulk([1, 2], fname__exact='John')}}} || {{{Reporter.objects.filter(fname='John').in_bulk([1, 2])}}} || || '''Date lookup''' || || || {{{articles.get_pub_date_list('year')}}} || {{{Article.objects.dates('pub_date', 'year')}}} || || '''Latest-object lookup''' || || || {{{articles.get_latest()}}} (required {{{get_latest_by}}} in model) || {{{Article.objects.latest()}}} (with {{{get_latest_by}}} in model) || || (Not previously possible) || {{{Article.objects.latest('pub_date')}}} # Latest by pub_date (overrides {{{get_latest_by}}} field in model) || || '''Many-to-one related lookup''' || || || {{{article_obj.reporter_id}}} || {{{article_obj.reporter.id}}} || || {{{article_obj.get_reporter()}}} || {{{article_obj.reporter}}} || || {{{reporter_obj.get_article_list()}}} || {{{reporter_obj.article_set.all()}}} || || {{{reporter_obj.get_article_list(headline__exact='Hello')}}} || {{{reporter_obj.article_set.filter(headline='Hello')}}} || || {{{reporter_obj.get_article_count()}}} || {{{reporter_obj.article_set.count()}}} || || {{{reporter_obj.add_article(headline='Foo')}}} || {{{reporter_obj.article_set.add(headline='Foo')}}} || || (Alternate syntax) || {{{reporter_obj.article_set.add(article_obj)}}} || || ("values" lookup, etc., not previously possible) || {{{reporter_obj.article_set.values()}}} || || '''Many-to-many related lookup''' || || || {{{article_obj.get_site_list()}}} || {{{article_obj.sites.all()}}} || || {{{article_obj.set_sites([s1.id, s2.id])}}} || {{{article_obj.sites.clear(); article_obj.sites.add(s1); article_obj.sites.add(s2)}}} || || {{{article_obj.set_sites([s1.id]) # deletion}}} || {{{article_obj.sites.remove(s2)}}} || || {{{site_obj.get_reporter_list()}}} || {{{site_obj.reporter_set.all()}}} || 注意关联对象查询使用关联对象的默认管理器, 这意味着访问关联对象的API与通过管理器访问关联对象的API已经完全一致. 还要注意不能从以下实例中访问管理器: {{{ #!python p = Person.objects.get(pk=1) p.objects.all() # Raises AttributeError }}} === 覆盖默认的管理器名称: ('objects') === 如果一个 model 已经有一个 {{{objects}}} 属性, 你需要给 {{{objects}}} 管理器指定一个另外的名字. {{{ #!python class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) objects = models.TextField() people = models.Manager() p = Person(first_name='Mary', last_name='Jones', objects='Hello there.') p.save() p.objects == 'Hello there.' Person.people.all() }}} === 自定义管理器及多管理器 === 只要你愿意,你可以创建任意多个管理器. 在必要时(比如在 admin 中), Django 会按照管理器定义顺序使用第一个定义的管理器. 如果你定义了至少一个自定义管理器, 就不能使用默认的 "objects" 管理器. {{{ #!python class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) people = models.Manager() fun_people = SomeOtherManager() }}} 如果一个管理器需要访问它的关联 model 类, 应该使用 {{{self.model}}}. 例子: {{{ #!python class PersonManager(models.Manager): def get_fun_person(self): try: return self.get(fun=True) except self.model.DoesNotExist: print "Doesn't exist." }}} ==== 让 admin 使用自定义管理器 ==== 有时你需要使用一个不同的管理器以用于 admin 的特定显示(比如在 admin 中只显示符合某些条件的对象). 你可以通过在{{{Admin}}} 声明中定义 {{{manager}}} 选项来实现: {{{ #!python class LivingPeopleManager(models.Manager): def get_query_set(self): return super(LivingPeopleManager, self).get_query_set().filter(is_alive=True) class Person(models.Model): name = models.CharField(maxlength=50) is_alive = models.BooleanField() class Admin: manager = LivingPeopleManager() }}} (参阅 "覆盖默认的 QuerySets" 了解 QuerySets 的更多信息) === 添加了一个更强大的覆盖 model 方法的方式, 删除了硬编码的 _pre_save(), _post_save(), 等等. === Proper subclassing of methods now works, 你可以子类化自动的 {{{save()}}} 和 {{{delete()}}} 方法. 删除了 {{{_pre_save()}}}, {{{_post_save()}}}, {{{_pre_delete()}}} 和 {{{_post_delete()}}} 钩子. 例子: {{{ #!python class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) def save(self): self.do_something() super(Person, self).save() # Call the "real" save() method. self.do_something_else() }}} 你甚至可以跳过保存这一步 (如同 requested #1014). {{{ #!python class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) def save(self): if datetime.date.today() > datetime.date(2005, 1, 1): super(Person, self).save() # Call the "real" save() method. else: # Don't save. pass }}} === 改名! DoesNotExist 异常 === 不再是 {{{people.PersonDoesNotExist}}}, 现在是 {{{Person.DoesNotExist}}}. 旧代码: {{{ #!python from django.models.myapp import people try: people.get_object(pk=1) except people.PersonDoesNotExist: print "Not there" }}} 新代码: {{{ #!python from path.to.myapp.models import Person try: Person.objects.get(pk=1) except Person.DoesNotExist: print "Not there" }}} === admin URLconf path 变得更短 === URLconf 中的 {{{include}}} 变得更短. * 旧代码: {{{django.contrib.admin.urls.admin}}} * 新代码: {{{django.contrib.admin.urls}}} === get_object_or_404 及 get_list_or_404 现在接受 model 类做为参数,而不再是模块 === 旧代码: {{{ #!python get_object_or_404(polls, pk=1) }}} 新代码: {{{ #!python get_object_or_404(Poll, pk=1) }}} === 传递给 generic views 的参数做了改变 === 由于不再有 {{{module_name}}} 的概念, 传递给 [[http://www.djangoproject.com/documentation/generic_views/|generic views]] 的 "info_dicts" 不再接受 {{{"app_label"}}} 和 {{{"module_name"}}} 参数而是使用新的 {{{"queryset"}}} 参数(一个 {{{QuerySet}}} 的实例). 旧代码: {{{ #!python info_dict = { 'app_label': 'blog', 'module_name': 'entries' } }}} 新代码: {{{ #!python from myproject.blog.models import Entry info_dict = {'queryset': Entry.objects.all()} }}} === generic views 中的模板名字改变 === 由于不再有 {{{module_name}}} 的概念, [[http://www.djangoproject.com/documentation/generic_views/|generic views]] 不再创建基于 {{{"module_name"}}} 的模板.无论何处用到了 {{{module_name}}}, 现在他们都变成了 {{{model_name}}}, 一个 model 名字的小写版本. 注意在这里仍然保留了 {{{app_label}}}. 下面的例子假定 models 定义在 {{{myproject/blog/models.py}}}. * 旧的: {{{blog/entries_archive.html}}} * 新的: {{{blog/entry_archive.html}}} === 将设置文件 settings 移到一个实例中 === Settings 从一个专门的模块{{{django.conf.settings}}} 中被移动到一个{{{django.conf}}} 模块实例中.现在你需要导入 {{{settings}}} 对象并以该实例的一个属性的方式引用 settings . * 旧代码: {{{from django.conf.settings import LANGUAGE_CODE}}} * 新代码: {{{from django.conf import settings}}} Django machinery 封装可以通过变更 settings 实例(带有一个访问 per-thread 或 per-location 全局变量代理 proxy 实例使用这一功能. === 删除 SilentVariableFailure 异常 === 旧的行为: 模板中子类{{{django.core.template.SilentVariableFailure}}}的任何异常将静默. 新的行为: 模板系统中任何拥有 {{{silent_variable_failure}}} 属性的异常将静默. {{{django.core.template.SilentVariableFailure}}}不再存在. === request.user 现在设置成通过 middleware 访问 === 过去常常在 mod_python 和 wsgi 处理器中设置. 你需要在你的 settings.py 文件中的{{{MIDDLEWARE_CLASSES}}} 里的{{{"django.contrib.sessions.middleware.SessionMiddleware"}}} '''之后''' 的某处 添加 {{{"django.contrib.auth.middleware.AuthenticationMiddleware"}}} . 否则访问 {{{request.user}}} 会引发 {{{AttributeError}}}. === Authentication 做了整理,变得更加统一 === 以前, 授权系统至少要在四个不同的位置进行设置. 现在所有的东西都被整理到 {{{django.contrib.auth}}} 中. 如下: * {{{django.parts.auth.formfields.AuthenticationForm}}} 变更为 {{{django.contrib.auth.forms}}} * {{{django.parts.auth.anonymoususers.AnonymousUser}}} 变更为 {{{django.contrib.auth.models}}} * {{{django.views.auth.login.*}}} 变更为 {{{django.contrib.auth.views}}} * {{{django.views.decorators.auth.*}}} 变更为 {{{django.contrib.auth.decorators}}} * {{{django.views.registration.passwords.PasswordResetForm}}} 变更为 {{{django.contrib.auth.forms}}} * {{{django.views.registration.passwords.PasswordChangeForm}}} 变更为 {{{django.contrib.auth.forms}}} * {{{django.views.registration.passwords.password_reset}}} 变更为 {{{django.contrib.auth.views}}} * {{{django.views.registration.passwords.password_reset_done}}} 变更为 {{{django.contrib.auth.views}}} * {{{django.views.registration.passwords.password_change}}} 变更为 {{{django.contrib.auth.views}}} * {{{django.views.registration.passwords.password_change_done}}} 变更为 {{{django.contrib.auth.views}}} 如果你用到了这些类或函数, 你需要根据上文更新代码. === 操纵器界面的改动 === 旧的: {{{ #!python from django.core import formfields from django.models.PROJECT import MODELMODULE ... manipulator = MODELMODULE.AddManipulator() ... form = formfields.FormWrapper(manipulator, new_data, errors) }}} 新的: {{{ #!python from django.forms import FormWrapper from PROJECT.APP.models import MODELNAME ... manipulator = MODELNAME.AddManipulator() ... form = FormWrapper(manipulator, new_data, errors) }}} === django.VERSION === 变量 {{{django.VERSION}}} 由一个四元素 tuple 变更为一个三元素 tuple. Old: {{{VERSION = (0, 9, 1, 'magic-removal')}}} New: {{{VERSION = (0, 91, 'magic-removal')}}} == 你可以使用的新功能 == === Models 支持属性 === 与以前不同, 现在models也支持属性了. {{{ #!python from django.db import models class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) def _get_full_name(self): return "%s %s" % (self.first_name, self.last_name) full_name = property(_get_full_name) }}} === 你能覆盖默认的 QuerySets === 你能指定一个管理者使用的默认 QuerySet (参阅上文的 "Descriptor fields"). 例子: {{{ #!python class PublishedBookManager(models.Manager): def get_query_set(self): return super(PublishedBookManager, self).get_query_set().filter(is_published=True) class Book(models.Model): title = models.CharField(maxlength=50) author = models.CharField(maxlength=30) is_published = models.BooleanField() published_objects = PublishedBookManager() }}} == 团队仍然需要做的 == === 删除自动的 manipulators, 有利于 validation-aware models === '''状态: 尚未完工''' === 改变子类语法 === '''状态: 沿未完工''' 参阅 ModelInheritance