Django效率:ManyToManyField

时间:2014-03-05 00:12:42

标签: django

在Django中,假设我有一个经常调用的函数:

def blockUser(id_to_block, user):
   to_block = Usr.objects.get(pk=id_to_block)
   user.blocked.add(to_block)
   user.save()

user的类型为Usr,使用models.Model和blocked创建的自定义类是ManyToManyField。

现在我读它的方式,Django将从数据库中提取to_block,然后将to_block添加到user.blocked,两次访问数据库。由于ManyToManyField只是一个包含from_idto_id的表,并且我已经知道了两个ID,我是否可以通过一次数据库访问来完成它?

我的问题是,鉴于在其他地方没有使用to_block这一事实,Django确实会为此进行两次数据库查询吗?如果是这样,我怎么能通过一个数据库查询实现这个目标?

非常感谢!

2 个答案:

答案 0 :(得分:0)

你可以使用Django的select_related()。来自文档:

  

这是一种性能提升,导致(有时更大)   查询但意味着以后使用外键关系不会   需要数据库查询

更新

使用ManyToMany字段使用prefetch_related()。与select_related()相同的原则。来自文档:

  

这允许它预取多对多和多对一对象   除了外键之外,不能使用select_related来完成   和select_related支持的一对一关系。

答案 1 :(得分:0)

您无需获取to_block对象即可将其添加到被阻止的ManyToManyField。使用主键也可以。 此外,添加关系后,您无需在用户对象上调用保存。

def blockUser(id_to_block, user):
   user.blocked.add(id_to_block)

然而,这会进行2次SQL查询。

  • 首先检查用户是否已阻止to_block用户。
  • 之后它会进行实际插入。

如果要避免检查完整性,可以直接创建中间模型的实例。

def blockUser(id_to_block, user):
    # create an instance of the intermediate model
    block_entry = Usr.blocked.through(user_id=user.id, blocked_id=id_to_block)    
    try:
        with transaction.commit_on_success():
            block_entry.save()
    except IntegrityError:
        pass

blocked_id可能不是正确的field_name。