Django 无法确定使用一对一关系链接一对多的查询集

Posted

技术标签:

【中文标题】Django 无法确定使用一对一关系链接一对多的查询集【英文标题】:Django cannot determine queryset for chaining one-to-many with one-to-one relationship 【发布时间】:2018-05-28 00:18:25 【问题描述】:

我有一个系统,其中与数字 a 模型(例如 1 a -> many b)存在多对一关系,并且许多模型与另一个模型(例如 1 b -> 1 c)存在一对一关系)。画成这样:

  /--- b1 --- c1
 /
a ---- b2 --- c2
 \
  \--- b3 --- c3

我决定创建一个方法来收集与a 对应的所有c

给定一个与我的结构相同的模型系统,我能想到的最好的方法如下所示:Person.find_important_treats()

有没有更好的方法不涉及对数据库的这么多调用?

from django.db import models

class Person(models.Model): 
    """ The 'a' from my above example """

     def find_important_treats(self):
        return (pet.treat for pet in self.pets)

class Pet(models.Model):
    """ The 'b' from my above example """

    owner = models.ForeignKey(
        to=Person,
        related_name='pets'
    )

    favourite_treat = models.ForeignKey(
        to=Treat,
    )

class Treat(models.Model):
    """ The 'c' from my above example """
    pass

【问题讨论】:

可能会沿着这些思路找到答案Treat.objects.filter(pet__owner__pk = personId) 您能否更具体一点,您是对整个点心对象感兴趣还是只对点心 ID 感兴趣?它有很大的不同! @DhiaTN,我想要整个款待(贪婪的我),因为它有我想在我的模板中呈现的信息。 【参考方案1】:

以下应该做你所追求的:

def find_important_treats(self):
    return Treat.objects.filter(id__in=person.pets.values_list('treat_id'))

获取宠物拥有的Treats中的所有ids,然后返回。

【讨论】:

【参考方案2】:

根据您的用例,我建议两个几乎相似的解决方案:

使用缓存
    class Person(models.Model): 
        """ The 'a' from my above example """

         @property
         def iter_important_treats(self):
            return (pet.treat_id for pet in self.pets.all()) # will use the cached objects if they exist

    person = Person.objects.get(id=person_id).select_related('pets') # to cache the pets list within the person object avoiding future additional queries
    treats = Treat.objects.filter(id__in=person.iter_importent_treats)
不使用缓存:
class Person(models.Model): 
        """ The 'a' from my above example """

         @property
         def iter_important_treats(self):
            return iter(self.pets.values_list('treat_id', flat=True)) # caching will not affect the query behviour

    person = Person.objects.get(id=person_id)
    treats = Treat.objects.filter(id__in=person.iter_importent_treats)

注意:

    我们使用treat_id 而不是treat__id 来避免额外的连接查询,因为django 已经在Pet 对象级别保存了treat_id,但是如果您使用treat__id,那么您将强制进行连接查询。 将属性限制为 ID 的迭代器只是为了实现可逆性和可维护性

【讨论】:

以上是关于Django 无法确定使用一对一关系链接一对多的查询集的主要内容,如果未能解决你的问题,请参考以下文章

web框架开发-Django模型层-多表操作

如何迭代到 Django 关系一对多的列表

Django - 模型层 - 下

61.Django03

61.Django03

Django 一对多关系 url