第二章-数据库结构
第二章 数据库结构
本章节覆盖以下话题:
- 使用模型mixin
- 使用相对URL方法创建一个模型mixin
- 创建模型mixin以处理日期的创建和修改
简介
当你新建新的app时,要做的第一件事就是创建表现数据库结构的模型。我们假设你之前已经创建了Django的app,要是没有话马上创建一个,而且你也阅读并了解Django的官方教程。本章,我会向你演示在项目中的一些使数据库结构在不同
使用模型mixin
在Python这样的面向对象语言中,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,生成另外的路径,然后处理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):
"""
A replacement for get_absolute_url()
Models extending this mixin should have either get_url or
get_url_path implemented.
"""
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)
#...
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="{{ idea.get_url_path }}">{{ idea.title }}</a>。对于电子邮件,RSS订阅或者API可以使用,<a href="{{ idea.get_url }}>"{{ idea.title }}</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添加了四个字段到模型扩展:
参阅
略
创建模型mixin以处理通用关系
除了外键关系或者对对关系这样的常见数据库关系之外,Django还提供了一种关联一个模型到任意模型的实例。此概念称为通用关系。每个通用关系都有一个关联模型的内容类型,而且这个内容类型也保存为该模型实例的ID。
于此技法,我们会向你演示如何将通用关系的创建归纳为模型mixin。
准备开始
工作原理
在文本编辑器中打开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,
)