第二章 数据库结构
第二章 数据库结构
本章节覆盖以下议题:
- 使用模型 mixin
- 使用相对 URL 方法创建一个模型 mixin
- 创建一个模型 mixin 以处理日期的创建和修改
- 创建一个模型 mixin 以处理 meta 标签
- 创建一个模型 mixin 以处理通用关系
- 处理多语言字段
- 使用 South 迁移 (译者注:Django1.7 中已经有了自己迁移模块,故内容将略去)
- 使用 South 将一个外键改变为多対多字段
引言
当你新建新的 app 时,要做的第一件事就是创建表现数据库结构的模型。我们假设你之前已经创建了 Django 的 app,要是没有话马上创建一个,而且你也阅读并了解 Django 的官方教程。本章,我会向你演示一些让数据库结构在项目的不同应用中保持一致的有趣的技术。然后我将向你演示创建模型字段以处理数据库中的数据的国际化。本章的最后,我会向你演示在开发的过程中如何使用迁移来改变数据库结构。
使用模型 mixin
在 Python 这样的面向对象语言中,mixin 类可以被视为一个实现功能的接口。当一个模型扩展了一个 mixin,它就实现了接口,并包括了 mixin 的所有字段,特性,和方法。当你想要在不同的模型中重复地使用通用功能时,可以使用 Django 模型的 mixin。
预热
要开始的话,你需要创建一些可重复使用的 mixin。mixin 的某些典型例子会在后面章节展示。一个保存模型 mixin 的好地方就是 utils
模块。
提示
如果你要创建一个与他人共享的重复使用 app,那就要把模型 mixin 放在 app 里,比如放在应用的
base.py
文件中。
具体做法
在任何想要使用的 mixin 的 Django 应用中,创建 models.py
文件,并输入下面的代码:
#demo_app/models.py
# -*- coding: UTF-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from utils.models import UrlMixin
from utils.models import CreationModificationMixin
from utils.models import MetaTagsMixin
class Idea(UrlMixin, CreationModificationMixin, MetaTagsMixin):
title = models.CharField(_("Title"), max_length=200)
content = models.TextField(_("Content"))
class Meta:
verbose_name = _("Idea")
verbose_name_plural = _("Ideas")
def __unicode__(self):
return self.title
工作原理
Django 的模型继承支持三种类型的继承:抽象基类,多重继承,以及代理模型。模型 mixin 是拥有特定字段,属性,和方法的抽象模型类。当你创建前面的例子所示 Idea
这样的模型时,它从 UrlMixin
, CreationModificationMixin
和 MetaTagsMixin
继承了所有功能。所有的抽象类字段都作为所扩展模型的字段被保存在相同的数据库表中。
还有更多呢
为了学习更多不同类型的模型继承,参考 Django 官方文档 https://docs.djangoproject.com/en/dev/topics/db/models/#model-inheritance。
参见
- 使用相对 URL 方法创建一个模型 mixin 技巧
- 创建模型 mixin 以处理日期的创建和修改
- 创建模型 mixin 以处理 meta 标签
使用相对 URL 方法创建一个模型 mixin
每个模型都有自己的页面,定义 get_absolute_url()
方法是很好的做法。这个方法可以用在模板中,它也可以用在 Django admin 站点中以预览所保存的项目。然而, get_absolute_url
很不明确,因为它实际上返回的是 URL 路径而不是完整的 URL。在这个做法,我会向你演示如何创建一个模型 mixin,默认它允许你定义 URL 路径或者完整的 URL,以生成一个开箱即用的 URL,并处理 get_absolute_url
方法的设置事宜。
预备!
如果你还没有完成创保存 mixin 的 utils
包。然后,在 utils
包内(可选的是,如果创建了一个重复使用的应用,那么你需要把 base.py
放在应用中)创建 models.py
文件。
具体做法
按步骤地执行以下命令:
- 在
utils
包的models.py
文件中添加以下内容:
#utils/models.py
# -*- coding: UTF-8 -*-
import urlparse
from django.db import models
from django.contrib.sites.models import Site
from django.conf import settings
class UrlMixin(models.Model):
"""
替换 get_absolute_url()。模型扩展该 mixin 便可以执行 get_url 或者 get_url_path。
"""
class Meta:
abstract = True
def get_url(self):
if hasattr(self.get_url_path, "dont_recurse"):
raise NotImplementedError
try:
path = self.get_url_path()
except NotImplementedError:
raise
website_url = getattr(settings, "DEFAULT_WEBSITE_URL", "http://127.0.0.1:8000")
return website_url + path
get_url.dont_recurse = True
def get_url_path(self):
if hasattr(self.get_url, "dont_recurse"):
raise NotImplementedError
try:
url = self.get_url()
except NotImplementedError:
raise
bits = urlparse.urlparse(url)
return urlparse.urlunparse(("", "") + bits[2:])
get_url_path.dont_recurse = True
def get_absolute_url(self):
return self.get_url_path()
- 为了在应用中使用 mixin,需要把它从
utils
包导入,然后在模型类中继承 mixin,并定义get_absolute_url()
方法如下:
# demo_app/models.py
# -*- coding: UTF-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
from utils.models import UrlMixin
class Idea(UrlMixin):
title = models.CharField(_("Title"), max_length=200)
#...
def get_url_path(self):
return reverse("idea_details", kwargs={
"idea_id": str(self.pk)
})
- 如果你在临时或者生产环境中检查该代码,抑或你在本地运行不同的 IP 或者端口的服务器,那么你需要在本地设置的
DEFAULT_WEBSITE_URL
中设置如下内容:
#settings.py
# ...
DEFAULT_WEBSITE_URL = "http://www.example.com"
工作原理
UrlMixin
是一个拥有三种方法的抽象模型: get_url()
, get_url_path() ,
get_absolute_url 。
get_url() 或者
get_url_path() 方法期待在所扩展的模型类中被重写,比如,
Idea 类。你可定义
get_url ,它是一个到对象的完整 URL,
get_url_path 会把它剥离到路径。你也可以定义
get_url_path ,它是到对象的绝对路径,然后
get_url 会添加网站的 URL 到路径的开始。
get_absolute_url 方法会模仿
get_url_path`。
提示
通常的经验总是重写
get_url_path()
方法。当你在同一个网站中需要到一个对象的链接,可以在模板中,使用
<a href=""></a>
。对于电子邮件,RSS 订阅或者 API 可以使用,<a href=">"</a>
。
参阅
使用模型 mixin
创建处理数据生成和修改的模型 mixin
创建模型 mixin 以处理元标签
创建模型 mixin 处理通用关系
创建处理数据生成和修改的模型 mixin
在模型中对于模型实例的创建和修改来说,一种常见的行为就是拥有时间戳。该方法中,我会向你演示如何给创建保存、修改模型的日期和时间。使用这样的 mixin,可以保证所有的模型对于时间戳都使用相同的字段,以及拥有同样的行为。
准备开始
如果你还没有完成创保存 mixin 的 utils
包。然后,在 utils
包内(可选的是,如果创建了一个重复使用的应用,那么你需要把 base.py
放在应用中)创建 models.py
文件。
如何做
打开 utils
包中的 models.py
文件,并输入以下的代码:
#utils/models.py
# -*- coding: UTF-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now as timezone_now
class CreationModificationDateMixin(models.Model):
"""
可以创建和修改日期和时间的抽象基类。
"""
created = models.DateTimeField(
_("creation date and time"),
editable=False,
)
modified = models.DateTimeField(
_("modification date and time"),
null=True,
editable=False,
)
def save(self, *args, **kwargs):
if not self.pk:
self.created = timezone_now()
else:
#为了保证我们一直拥有创建数据,添加下面的条件语句
if not self.created:
self.created = timezone_now()
self.modified = timezone_now()
super(CreationModificationDateMixin, self).save(*args, **kwargs)
save.alters_data = True
class Meta:
abstract = True
工作原理
CreationModificationDateMixin
类是一个抽象模型,这意味着它所扩展的模型类会在同一个数据表中创建所有字段,即,没有一对一关系让表变得难以处理。该 mixin 拥有两个日期-时间字段,以及一个在保存扩展模型会调用的 save()
方法。 save()
方法检查模型是否拥有主键,这是一种新建但还未保存的实例的情况。否则,如果主键存在,修改的日期就会被设置为当前的日期和时间。
作为选择,你可以不使用 save()
方法,而使用 auto_now_add
和 auto_now
属性来 created
和 修改
字段以自动地创建和修改时间戳。
参阅
使用模型 mixin
创建模型 mixin 以处理 meta 标签
创建模型 mixin 以处理通用关系
创建模型 mixin 以处理 meta 标签
如果你想要为搜索引擎而优化网站,那么你不仅需要个每个页面都设置语义装饰,而且也需要合适的元标签。为了最大的灵活性,你需要有一种对在网站中有自己页面的所有对象都定义指定的元标签的方法。于此技法中,我们会向你演示如何对字段和方法创建一个 mixin 以关联到元标签。
准备开始喽!
和前面的做法一样,确保你为 mixin 备好了 utils
包。用你最喜欢的编辑器打开 models.py
文件。
具体做法
于 models.py
文件写入以下内容:
#utils/models.py
# -*- coding: UTF-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.template.defaultfilters import escape
from django.utils.safestring import mark_safe
class MetaTagsMixin(models.Model):
"""
用于<head>元素中由抽象基类所构成的元标签
"""
meta_keywords = models.CharField(
_("Keywords"),
max_length=255,
blank=True,
help_text=_("Separate keywords by comma."),
)
meta_description = models.CharField(
_("Description"),
max_length=255,
blank=True,
)
meta_author = models.CharField(
_("Author"),
max_length=255,
blank=True,
)
meta_copyright = models.CharField(
_("Copyright"),
max_length=255,
blank=True,
)
class Meta:
abstract = True
def get_meta_keyword(self):
tag = u" "
if self.meta_keywords:
tag = u'<meta name="keywords" content="{}".format(escape(self.meta_author)) />
return mark_safe(tag)
def get_meta_description(self):
tag = u" "
if self.meta_description:
tag = u'<meta name="description" content="{1}".format(escape(self.meta_author)) />
return mark_safe(tag)
def get_meta_author(self):
tag = u" "
if self.get_meta_author:
tag = u'<meta name="author" content="{1}".format(escape(self.meta_author)) />'
def get_meta_copyright(self):
tag = u" "
if self.meta_copyright:
tag = u'<meta name="copyright" content="{}".format(escape(self.meta_copyright)) />'
return mark_safe(tag)
def get_meta_tags(self):
return mark_safe(u" ".join(
self.get_meta_keyword(),
self.get_meta_description(),
self.get_meta_author(),
self.get_meta_copyright(),
))
工作原理
该 mixin 添加了四个字段到模型扩展: meta_keywords, meta_description, meta_author
和 meta_copyright
。在 HTML 中渲染元标签的方法也添加了。
如果你在 Idea
这样的模型中使用 mixin,它出现在本章的第一个方法,那么你可以在目的是渲染所有元标签的详细页面模板的 HEAD
部分中写入以下内容:
{{ ieda.get_meta_tags }}
你也可以利用下面的行来渲染一个特定的元标签:
{{ idea.get_meta_description }}
或许你也注意到了代码片段,渲染的元标签被标记为安全,即,它们没有被转义而且我们也不要使用 safe
模板过滤器。只有来自数据库中的值才被转义,以保证最终的 HTML 成型良好。
参见
使用模型 mixin
创建一个模型 mixin 以处理日期的创建和修改
创建一个模型 mixin 以处理通用关系
创建模型 mixin 以处理通用关系
除了外键关系或者对对关系这样的正常的数据库关系之外,Django 还提供了一种关联一个模型到任意模型的实例。此概念称为通用关系。每个通用关系都有一个关联模型的内容类型,而且这个内容类型保存为该模型实例的 ID。
该方法中,我们会向你演示如何将通用关系的创建归纳为模型 mixin。
准备开始
要让该方法正常运行,你需要安装 contenttypes
应用。默认它应该位于 INSTALLED_APPS
目录中:
INSTALLED_APPS = (
# ...
"django.contrib.contenttypes",
)
再者,要确保你已经创建了放置模型 mixin 的 utils
包。
工作原理
在文本编辑器中打开 utils
包中的 models.py
文件,并输入以下的内容:
“
#utils/models.py
# -*- coding: UTF-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.core.exceptions import FieldError
def object_relation_mixin_factory(
prefix=None,
prefix_verbose=None,
add_related_name=False,
limit_content_type_choices_to={},
limit_object_choices_to={},
is_required=False,
)
""" 返回一个使用含有动态字段名称的“Content type - object Id”的 mixin 类的通用外键。
该函数只是一个类生成器。
参数:
prefix:前缀用来添加到字段的前面
prefix_verbose:前缀的冗余名称,用来生成 Admin 中内容对象的字段列的 title
add_related_name:表示一个布尔值,
"""
工作原理
参见
The Creating a model mixin with URL-related methods recipe
The Creating a model mixin to handle creation and modification dates recipe
The Creating a model mixin to take care of meta tags recipe
处理多语言字段
预热
具体做法
工作原理
使用 South 迁移
略。Django1.7 已经自带迁移模块。
使用 South 将一个外键改变为多対多字段
略。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论