多对一(ForeignKey)
多对一关系由外键实现,外键要定义在“多”的一方,如:
models.ForeignKey(<主表>, ...)
如果要关联的主表在另一个app中,要显式的指出:
models.ForeignKey(’<app名称>.<主表>‘, ...)
可创建自己关联自己的外键——比如,在评论系统中,一条评论可被多次引用自身并继续评论:
class Comment(models.Model):
title = models.Charfield(max_length=128)
text = models.TextField()
parent_comment = models.ForeignKey(’self’, on_delete=models.CASCADE)
...
如果在子模型中存在多个外键指向同一个主模型,必须给他们加上不同的related_name,用于反向查询。
外键字段在数据库中的列名是<外键名>_id,用来储存主表对象的id,因此,在得到主表id的情况下可以不调用外键而直接筛选:
QuerySet = <外键表>.filter(<外键名>_id=<主表id>)
#获得与“<主表id>=xxx的主对象”相关联的子对象
多对一的字段参数
on_delete=models.XXX
当主对象被删除时,关联的子对象的操作(因为主键不能随意删除,外键可以直接删除)
models.CASCADE:同时删除关联的子对象,Django默认
models.PROTECT:阻止CASCADE删除关联的子对象,并弹出ProtectedError异常
models.SET_NULL:将子对象的外键字段设为null(外键字段需设置null=True)
models.SET_DEFAULT:将子对象的外键字段设为默认值(外键字段需设置default=’xxx’)
models.DO_NOTHING:什么也不做
models.SET():设置为一个传递给SET()的值或一个回调函数的返回值
注:on_delete是Django2.0之后新增的必选项。
related_name=’ ‘
通过主对象反向引用子对象的通道名称,默认名称是<小写子模型名>_set。
如:
class Car(models.Model):
factory = models.ForeignKey(
Factory,
on_delete=models.CASCADE,
related_name='car_by_factory',
)
此时Factory主表取出所有关联的Car对象:factory.car_by_factory.all()
如果不想给外键设置反向关联名称,就用related_name=’+’或以+号结尾
related_query_name=’ ‘
主对象反向关联查询的名称,默认查询名称是<外键列>__<主表的某列>。
如:
class Tag(models.Model):
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
related_query_name="tag",
)
name = models.CharField(max_length=255)
此时用tag作为查询名称:Article.objects.filter(tag__name="A")
limit_choices_to={}
限制外键能关联的对象,用于Django的表单模块和admin后台,可以传入字典、Q对象、一个返回字典或Q对象的函数。
如:
staff_member = models.ForeignKey(
User,
on_delete=models.CASCADE,
limit_choices_to={'is_staff': True},
)
此时,表单模块的staff_member字段列表中只会出现is_staff=True的Users对象,对admin后台非常有用。
to_field=’ ‘
让外键关联到指定的字段上(默认关联到主键),该字段必须有unique=True属性。
多对一的引用
子对象引用主对象:
<子对象>.<外键列>
主对象引用子对象:
<主对象>.<子表小写>_set
如:
<主对象>.<子表小写>_set.all() #所有关联子对象的集合
<主对象>.<子表小写>_set.create(<子表字段>=’xxx’, ...) #创建关联的子对象
<主对象>.<子表小写>_set.count() #查询关联子对象的数量
添加多对一关系:
<子对象>.<外键列> = <主对象>
<子对象>.save()
删除多对一关系:
<子对象>.<外键列> = None #外键列要设为null=True
多对一的查询
查询子对象的集合:
<子表>.objects.filter(<外键列>__<主表的某列>=’xxx’)
#获得与“<主表的某列>=xxx的主对象”相关联的子对象
查询主对象的集合:
<主表>.objects.filter(<子表小写> __<子表的某列>=’xxx’)
#获得与“<子表的某列>=xxx的子对象”相关联的主对象
多对多(ManyToManyField)
多对多字段可定义在任意一方,如:
models.ManyToManyField(‘<主表>’)
多对多字段不支持Django内置的validators验证功能。
null参数对ManyToManyField多对多字段无效,设置null=True毫无意义。
如果在子模型中存在多个多对多键指向同一个主模型,必须给他们加上不同的related_name,用于反向查询。
采用默认中间表的数据库结构:
<中间表id> <多对多表>_id <主表>_id
默认中间表通过保存两张表的id进行关联
采用自定义中间表并添加新字段的数据库结构:
<中间表id> <新字段1> <新字段2> <多对多表>_id <主表>_id
多对多字段的参数
related_name=’ ‘
主对象反向引用多对象时的名称,和多对一相同。
related_query_name=’ ‘
主对象反向关联查询的名称,和多对一相同。
through=’ ‘
自定义中间表,用于保存两表关系的附加数据,中间表要有两个外键字段分别指向关联的两个模型。
through_fields=(<字段1>, <字段2>)
当中间表有多个外键指向一个表的时候,用through_firlds指定中间表的两个连接字段,如:
class Person(models.Model):
name = models.CharField(max_length=32)
class Group(models.Model):
name = models.CharField(max_length=64)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person' ), #指定两个连接字段
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey( #另一个指向Person表的外键
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=128)
db_table=’ ‘
设置中间表的名称,默认为<多对多字段名>_<主表名>_<一串哈希码>
limit_choices_to={ }
限制多对多键关联的对象,和多对一相同,对于用through定义中间表的字段无效。
多对多的引用
多对象引用主对象:
<多对象>.<多键列>
主对象引用多对象:
<主对象>.<多表小写>_set
添加多对多关系:(默认中间表)
<多对象>.<多键列>.add(<主对象>) #可同时添加多个主对象
添加多对多关系:(自定义中间表)
a = <主对象>
b = <多对象>
membership = <中间表>(<主表外键列>=a, <多表外键列>=b, <字段1>=’xxx’, <字段2>=’xxx’, ...)
membership.save()
#先创建主表和多表的实例,再创建中间表的连接关系
#自定义中间表不能用add()、create()、remove()和set()操作对象的关系
删除多对多关系:
<多对象>.<多键列>.clear()
获取中间表的附加数据:
a = <主对象>
b = <多对象>
membership = <中间表>.objects.get(<主表外键列>=a, <多表外键列>=b)
membership.<字段1> #获取字段信息
一对一(OneToOneField)
反向关联的对象只有一个,多数用于从一个模型扩展出另一个模型的情况。
一对一通过子对象访问关联的主对象:
<子对象>.<一对一键>
一对一通过主对象访问关联的子对象:
<主对象>.<小写子模型名>
注意返回的是单个对象而不是集合。