关联对象参考

class RelatedManager

“关联管理器” 用于处理一对多和多对多关系的管理器. 它应用于下面两种情况:

  • ForeignKey 关系的 “另一边”. 即:

    from django.db import models
    
    class Reporter(models.Model):
        # ...
        pass
    
    class Article(models.Model):
        reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
    

    上面代码例子中, reporter.article_set 将具有下文中的管理器方法.

  • ManyToManyField 关系的两边:

    class Topping(models.Model):
        # ...
        pass
    
    class Pizza(models.Model):
        toppings = models.ManyToManyField(Topping)
    

    这个例子中, topping.pizza_set 和 pizza.toppings 都将具有下面的管理器方法.

add(*objs, bulk=True)

添加给定模型对象到关联对象集.

例如:

>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.add(e) # 将 Entry e 与 Blog b 关联.

下上面例子中, 由于 ForeignKey 关系使用 QuerySet.update() 方法来保存更新. 所以这要求关联对象已经 save 到数据中.

可以使用 bulk=False 参数来指定管理器通过 e.save() 来执行更新.

在多对多关系中 add() 方法不会调用 save() 方法, 而是使用 QuerySet.bulk_create() 来更新关联关系. 如果在创建关联关系时需要添加一些自定义逻辑, 可以通过监听 m2m_changed 信号来实现.

Changed in Django 1.9:

新增 bulk 参数. 在此之间版本外键都是使用 save() 更新, 使用 bulk=False 参数来使用此行为.

create(**kwargs)

创建并保存一个新对象添加至关联对象集. 返回新创建的对象:

>>> b = Blog.objects.get(id=1)
>>> e = b.entry_set.create(
...     headline='Hello',
...     body_text='Hi',
...     pub_date=datetime.date(2005, 1, 1)
... )

# 重点 不用再调用 e.save() 来保存.

这等价于 (但更加简洁):

>>> b = Blog.objects.get(id=1)
>>> e = Entry(
...     blog=b,
...     headline='Hello',
...     body_text='Hi',
...     pub_date=datetime.date(2005, 1, 1)
... )
>>> e.save(force_insert=True)

注意, 并不需要传入模型中的关联关系参数. 在上面例子中并没有给 create() 传入 blog 参数, Django会自动将 Entry 对象的 blog 字段设置为 b.

remove(*objs)

从关联对象集中删除给的模型对象:

>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.remove(e) # 解除 Entry e 和 Blog b 关联.

和 add() 方法一样, 上面例子中是调用 e.save() 来执行更新. 但是 remove() 在删除多对多关系时是使用 QuerySet.delete() 方法而不会调用 save() 方法, 因此要在删除关系时执行一些自定义操作请监听 m2m_changed 信号.

对于 ForeignKey 关联对象, remove方法仅在设置了 null=True 时可以使用. 因为如果关联字段不能设置为 None (NULL), 在对象添加了另一个关联对象之前不能删除现有关联对象. 在上面例子中. 从 b.entry_set() 移除 e 就意味着设置 e.blog = None, 但是如果 blog 的 ForeignKey 没有设置 null=True 时这样做是不行的.

对于 ForeignKey 关联对象, 该方法接收一个 bulk 参数用于控制其如何执行更新操作. 如果设置为 True (默认值) 则会使用 QuerySet.update() . 如果设置为 bulk=False 则是调用每个实例的 save() 方法, 这个发送 pre_save 信号和 post_save 信号, 这比前者多一定性能损耗.

clear()

从关联对象集中删除所有对象:

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.clear()

注意这并不会删除关联的对象, 这是解除关联.

和 remove() 方法一样, clear() 方法仅在 ForeignKey 设置了 null=True 时才可用, 而且它也接收关键字参数 bulk.

set(objs, bulk=True, clear=False)
New in Django 1.9:

替换关联对象集:

>>> new_list = [obj1, obj2, obj3]
>>> e.related_set.set(new_list)

该方法接收一个 clear 参数用于控制如何执行替换操作. 如果设置为 False (默认值) 则是 remove() 新集合中不存在的值, 然后添加新值. 如果设置 clear=True 则是先调用 clear() 方法然后一次添加所有值.

参数 bulk 用于透传给 add() 方法.

注意由于 set() 是一个复合操作, 所以它也会受竞争条件影响, 比如在调用 clear() 之后 add() 之前又有新值被插入数据库.

注解

注意 add(), create(), remove(), clear() 和 set() 都会对所有类型的关联字段立即更新至数据库. 换句话说在两边都不需要再调用 save().

如果使用了 an intermediate model for a many-to-many relationship, add(), create(), remove(), 和 set() 方法都会清除预取的缓存.

直接赋值

通过赋值一个新的可迭代的对象, 关联对象集可以被整体替换掉:

>>> new_list = [obj1, obj2, obj3]
>>> e.related_set = new_list

如果外键关系设置``null=True``, 则在添加 new_list 的内容之前, 关联管理器将首先解除关联集中的现有的关联对象, 然后再添加新列表的内容. 否则, 新列表中的对象将直接添加到现有的关联对象集中.

Changed in Django 1.9:

在此之前版本中, 直接赋值是执行 clear() 然后 add(). 现在是执行带有 clear=False 参数的 set() 方法.

1.10 版后已移除: 不再建议直接赋值而是使用 set() 方法:

>>> e.related_set.set([obj1, obj2, obj3])

这可以防止直接赋值带来的隐式保存带来的困惑.