有关Q对象和外键的谜题

4 投票
2 回答
640 浏览
提问于 2025-04-15 16:29

我有一个这样的模型:

class Thing(models.Model):
    property1 = models.IntegerField()
    property2 = models.IntegerField()
    property3 = models.IntegerField()

class Subthing(models.Model):
    subproperty = models.IntegerField()
    thing = modelsForeignkey(Thing)
    main = models.BooleanField()

我有一个函数,这个函数接收一个过滤器列表,每个过滤器的格式是 {'type':某种类型, 'value':值x}。这个函数需要返回一个结果集,要求把所有过滤器的条件都满足:

final_q = Q()
for filter in filters:
        q = None
        if filter['type'] =='thing-property1':
            q = Q(property1=filter['value'])
        elif filter['type'] =='thing-property2':
            q = Q(property2=filter['value'])
        elif filter['type'] =='thing-property2':
            q = Q(property3=filter['value'])
        if q:
            final_q = final_q & q
return Thing.objects.filter(final_q).distinct()

每个子项(Subthing)都有一个布尔属性 'main'。每个主项(Thing)都有且仅有一个子项,其 'main' 属性为 True。

现在我需要添加一个过滤器,返回所有主项,这些主项的子项中有一个满足 main==Truesubproperty==filter['value'] 的条件。

我能在构建的 Q 对象中做到这一点吗?如果不能,还有其他方法吗?在我添加新过滤器之前,得到的查询集可能会很大,所以我希望有一种方法不需要逐个遍历结果。

2 个回答

1

使用这个(而不是一开始的 final_q=Q()

final_q=Q(subthing_set__main=True)
sub_vals = map(lambda v: v['value'], filters)
if sub_vals:
    final_q = final_q & Q(subthing_set__subproperty__in=sub_vals)

应该能帮你达到想要的效果,你也可以调整你的循环,先构建 sub_vals 列表,然后在循环结束后再应用它。

subthing_set 是一个自动添加的相关字段,用于访问与 Thing 相关的 Subthings。

你可以给它指定另一个相关名称,比如:

thing=models.ForeignKey(Thing,related_name='subthings')
2

如果你在Subthings和Thing之间的关系中明确给Subthings一个“related_name”,那么理解起来会简单一些。

class Subthing(models.Model):
    ...
    thing = models.ForeignKey(Thing, related_name='subthings')
    ...

现在,你可以使用Django的连接语法来构建你的Q对象:

Q(subthings__main=True) & Q(subthings__subproperty=filter['value'])

反向关系的默认名称是'subthing_set',但我觉得如果给它起个更好的名字,比如'subthings',会更容易理解。

撰写回答