在建立数据库时,通常会把各个对象的属性放在一个一个表中,通过表之间的关系即外键来描述和约束业务逻辑。
Django 的 ORM 模型简化了一些数据库的操作,特别是外键,以及查询等功能,使得我们不用再写复杂的sql语句。
表和关系的逻辑清晰并且可以跨数据库平台使用。
下面主要记录一下 Django 在数据库中是怎样处理 ForeignKey 的。
app 都是 Pinax 中所集成的。
1、ManyToManyField
class Url(models.Model):
.........
class Meta:
verbose_name = _('url')
verbose_name_plural = _('url')
class Rule(models.Model):
.........
allowed = models.ManyToManyField(Url, blank=True, related_name="allowed",
help_text=_("The URLs which are allowed "
"to be accessed by bots."))
disallowed = models.ManyToManyField(Url, blank=True, related_name="disallowed",
help_text=_("The URLs which are not "
"allowed to be accessed "
"by bots."))
sites = models.ManyToManyField(Site)
如上是 robots.models
在数据库中表现为
图[1]
robots_rule 和 robots_url 两个表对应两个 model
robots_rule_allowed, robots_rule_disallowed, robots_rule_sites 三个表分别对应 ManyToManyField
如果在 rule 中查询 rule.allowed,Django 会自动锁定外键对应的表为 robots_rule_allowed 通过查询这个表得到对应的 url_id(多个),然后去 robots_url 表中提取相应记录。
2、ForeignKey
class ThreadedComment(models.Model):
.......
# Generic Foreign Key Fields
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField(_('object ID'))
content_object = generic.GenericForeignKey()
# Hierarchy Field
parent = models.ForeignKey('self', null=True, blank=True, default=None, related_name='children')
# User Field
user = models.ForeignKey(User)
先看 ForeignKey 字段。
在表 threadedcomments_threadedcomment 中的结构如下(注意这个表名是由 <app_label>_<models_model> 组成)
图[2]
parent 是指向自身表的约束(用来表示一组回复,可以呈现 tree 形式的回复),允许为空,字段值为父 item 的 pk 值(即 id )。
查询时,Django 会提取 parent_id 值,然后直接在自身表中搜索到父 id,即被回复的评论。
注,所有的 model 不用给出 id 字段描述,因为 Django 会自动给出,而且做外键关联都是由 id 字段唯一确定的,当然这可以改...看document。
user 是指向另一个表(Django 默认app User,用于登陆后台 admin)的外键,提取 user_id 值,再去 User 表中查询即可。
ForeignKey 的 related_name='children' 标注了反向关系名称。
例如,这个例子中指向的是 parent,而 parent 中没有任何标注,但是我们可以通过 <parent对象>.<children>+<_set>
即 parent对象.children_set() 来得到所有的 children 关联记录。
3、GenericForeignKey
这个东西比较好用,在我没有知道这个好东西之前,解决一个表关联多个外键时,是通过定义一个 type 字段来确定当前记录中的哪个外键是有效的。
当然他这个原理也是这样的,不过多加了一个全局的 ContentType 表,使整体结构更加清晰。
例如,我们还是拿上面的评论来做例子。为了使数据表结构清晰,评论都放在一个表中,那么,一个评论就可能是 blog 的, tweets 的,也可能是 shop 的。
那就要定义一个 ContentType GenericForeignKey 来动态存储和得到外键所关联的表信息。
content_type 是关联到 ContentType model 上的一个外键,实际上是一个 integer ,表示所关联的表类别,例如 blog post 表。
object_id 标识的是所关联表中记录的 id 值,例如 post 表中的一个 blog 记录。
content_type, object_id 是默认的值,如果需要更改可以在创建 GenericForeignKey 时传参。
下面看看 django_content_type 表的结构
图[3]
app_label, model 组合是 UNIQUE
app_label 是 app 的名称。
model 是 app models 中的 model 名称。
图[2]中content_type_id 和 object_id 分别对应的就是 model 中声明的 content_type 和 object_id 字段。
通过content_type_id 可以在图[3]django_content_type 表中查询到对应关联的
名称name(verbose_name) 名称app_label(app名称) model名称(model的小写)
通过 app_label, model 两个字段的拼接就可以得到表名,例如 id=41 blog_post 即为 content_type_id=41 对应的数据表,
说明 comment(id=1-7) 对应的是 blog app 的 post 数据表。
OK就是这样,其实很简单的东西,框架非常清晰,看文档的时候晕晕的。