ManyToManyField
- 一对多,models.ForeignKey(ColorDic)
- 一对一,models.OneToOneField(OneModel)
- 多对多,authors = models.ManyToManyField(Author)
应用场景:
- 一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了)。
例如:与同事合作开发过程中两人都用到一某个表,这个表原有含10列数据,经过一段时间之后,发现10列无法满足自己的需求,需要为原来的表再添加5列数据,但是为了不影响同事的使用,可以使用一对一关系设计表,新的表中只需要包含要新加的这5列数据即可。
- 一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)。
例如:创建用户信息时候,用户信息中包含用户类型(普通用户、管理员、超级管理员),用户与用户类型就属于一对多关系。一个用户对应一种用户类型,但一种用户类型可以包含多个用户。
- 多对多:在某表中创建一行数据时,有一个可以多选的下拉框。
例如:创建用户信息,需要用户选择爱好,一个用户可以有多个爱好,一种爱好可以被多个用户选择。
2. 举例详解
#!/usr/bin/env python# -*- coding: utf-8 -*-from __future__ import unicode_literalsfrom django.db import modelsclass Author(models.Model):name = models.CharField(max_length=30)class Publisher(models.Model):name = models.CharField(max_length=50)class Book(models.Model):name = models.CharField(max_length=50)#一本书由一家出版社发布,一个出版社发布多本书。属于一对多关系,用ForeignKey()pub = models.ForeignKey(Publisher)#一本书可以由多个作者合写,一个作者可以写多本书,属于多对多关系,用ManyToManyFieldauthors = models.ManyToMany(Author)
生成结果:
一共生成了4张表:
web_author(作者表)web_publisher(出版社表)
web_book_authors(记录book与author多对多关系表。多对多关系要借助第三张表建立关系)
web_book(book表,其中pub_id体现书与出版社之间的一对多关系)
3. 表关系进阶
1. 关联尚未定义的Model
class Book(models.Model):name = models.CharField(max_length = 50)#如果Publisher与Author在Book后面定义,需要使用model 的名称,而不是使用 model 对象本身pub = models.ForeignKey('Publisher')authors = models.ManyToManyField('Author')class Publisher(models.Model):name = models.CharField(max_length = 50)class Author(models.Model):name = models.CharField(max_length = 30)
2. Model关联自身
1) Model可以与自身做多对一关系
class People(models.Model):name = models.CharField(max_length = 30)leader = models.ForeignKey('self', blank=True, null=True)
说明:一个领导有多个下属,一个下属对应一个直接领导,同时领导也是领导的下属。就属于多对一关系,且需要与自身做多对一关系。且注 意,设计这表时要设置blank=True和null=True.2) Model可以与自身做多对多关系
class Person(models.Model):friends = models.ManyToManyField('self')
用来定义一对一关系。笼统地讲,它与声明了 unique=True 的 ForeignKey 非常相似,不同的是使用反向关联的时候,得到的不是一个对象列表,而是一个单独的对象。在某个 model 扩展自另一个 model 时,这个字段是非常有用的;例如: 多表继承 (Multi-tableinheritance) 就是通过在子 model 中添加一个指向父 model 的一对一关联而实现的。必须给该字段一个参数:被关联的 model 类。工作方式和 ForeignKey 一样,连递归关联 (recursive) 和 延后关联 (lazy) 都一样。 此外,OneToOneField 接受 ForeignKey 可接受的参数,只有一个参数是 OnetoOneField 专有的:OneToOneField.parent_link,如果为 True,并且作用于继承自某个父 model 的子 model 上(这里不能是延后继承,父 model 必须真实存在 ),那么该字段就会变成指向父类实例的引用(或者叫链接),而不是象其他OneToOneField 那样用于扩展父类并继承父类属性。
from django.db import models, transaction, IntegrityErrorclass Place(models.Model):name = models.CharField(max_length=50)address = models.CharField(max_length=80)def __unicode__(self):return u"%s the place" % self.nameclass Restaurant(models.Model):place = models.OneToOneField(Place, primary_key=True)serves_hot_dogs = models.BooleanField()serves_pizza = models.BooleanField()def __unicode__(self):return u"%s the restaurant" % self.place.nameclass Waiter(models.Model):restaurant = models.ForeignKey(Restaurant)name = models.CharField(max_length=50)def __unicode__(self):return u"%s the waiter at %s" % (self.name, self.restaurant)
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')>>> p1.save()>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)>>> r.save()>>> p1.restaurant<Restaurant: Demon Dogs the restaurant>>>> Place.objects.get(restaurant__place__name__startswith="Demon")<Place: Demon Dogs the place>>>> Waiter.objects.filter(restaurant__place__name__startswith="Demon")
今天重看 Flask 时,发现对backref仍然没有理解透彻。查阅文档后发现,以前试图孤立地理解backref是问题之源,backref是与relationship配合使用的。
一对多关系
db.relationship()用于在两个表之间建立一对多关系。例如书中 roles 表中一个 User 角色,可以对应 users 表中多个实际的普通用户。实现这种关系时,要在“多”这一侧加入一个外键,指向“一”这一侧联接的记录。
class Role(db.Model):
# ...
users = db.relationship('User', backref='role')
class User(db.Model):
# ...
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
relationship & ForeighKey
大多数情况下, db.relationship() 都能自行找到关系中的外键, 但有时却无法决定把 哪一列作为外键。 例如, 如果 User 模型中有两个或以上的列定义为 Role 模型的外键, SQLAlchemy 就不知道该使用哪列。如果无法决定外键,你就要为 db.relationship() 提供额外参数,从而确定所用外键。(见书 P49)
relationship & backref
通过db.relationship(),Role 模型有了一个可以获得对应角色所有用户的属性users。默认是列表形式,lazy='dynamic'时返回的是一个 query 对象。即relationship提供了 Role 对 User 的访问。
而backref正好相反,提供了 User 对 Role 的访问。
不妨设一个 Role 实例为 user_role,一个 User 实例为 u。relationship 使 user_role.users 可以访问所有符合角色的用户,而 backref 使 u.role 可以获得用户对应的角色。
一对一关系
除了一对多之外, 还有几种其他的关系类型。一对一关系可以用前面介绍的一对多关系表示, 但调用 db.relationship() 时要把 uselist 设为 False , 把“多”变成“一”。
多对多关系
如果你想要用多对多关系,你需要定义一个用于关系的辅助表。对于这个辅助表, 强烈建议 不 使用模型,而是采用一个实际的表:
tags = db.Table('tags',
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
db.Column('page_id', db.Integer, db.ForeignKey('page.id'))
)
class Page(db.Model):
id = db.Column(db.Integer, primary_key=True)
tags = db.relationship('Tag', secondary=tags,
backref=db.backref('pages', lazy='dynamic'))
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
这里我们配置 Page.tags 加载后作为标签的列表,因为我们并不期望每页出现 太多的标签。而每个 tag 的页面列表( Tag.pages )是一个动态的反向引用。 正如上面提到的,这意味着你会得到一个可以发起 select 的查询对象。




浙公网安备 33010602011771号