Django使用相关模型的自定义管理器过滤相关字段

时间:2017-06-15 03:46:09

标签: python django django-models django-orm

如何在通过相关字段过滤时从自定义管理器查询集应用注释和过滤器?这里有一些代码来证明我的意思。

经理和模特

from django.db.models import Value, BooleanField

class OtherModelManager(Manager):
    def get_queryset(self):
        return super(OtherModelManager, self).get_queryset().annotate(
            some_flag=Value(True, output_field=BooleanField())
        ).filter(
            disabled=False
        )

class MyModel(Model):
    other_model = ForeignKey(OtherModel)

class OtherModel(Model):
    disabled = BooleanField()

    objects = OtherModelManager()

尝试使用管理器

过滤相关字段
# This should only give me MyModel objects with related 
# OtherModel objects that have the some_flag annotation 
# set to True and disabled=False
my_model = MyModel.objects.filter(some_flag=True)

如果您尝试上述代码,则会收到以下错误:

TypeError: Related Field got invalid lookup: some_flag

为了进一步澄清,基本上同样的问题被报告为一个错误,但没有回应如何实现这一目标:https://code.djangoproject.com/ticket/26393

我知道这可以通过直接在MyModel过滤器中直接使用管理器中的过滤器和注释来实现,但重点是保持此DRY并确保此行为重复无处不在访问此模型(除非明确指示不这样做)。

3 个答案:

答案 0 :(得分:3)

如何运行嵌套查询(或两个查询,以防你的后端是MySQL;性能)。

第一个获取相关OtherModel个对象的pk。

第二个过滤获取的pks上的Model个对象。

other_model_pks = OtherModel.objects.filter(some_flag=...).values_list('pk', flat=True)
my_model = MyModel.objects.filter(other_model__in=other_model_pks)
# use (...__in=list(other_model_pks)) for MySQL to avoid a nested query.

答案 1 :(得分:2)

我不认为你想要的是什么。

1)我认为你错过了解注释的作用。

  

为QuerySet中的每个项生成聚合

     

生成汇总值的第二种方法是生成一个   QuerySet中每个对象的独立摘要。例如,如果你   正在检索书籍列表,您可能想知道有多少作者   为每本书做出了贡献。每本书都有多对多的关系   与作者;我们想要总结每本书的这种关系   在QuerySet中。

     

可以使用 annotate()子句生成每个对象的摘要。   当指定 annotate()子句时, QuerySet 中的每个对象   将使用指定的值进行注释。

     

这些注释的语法与用于注释的语法相同    aggregate()子句。 annotate()的每个参数都描述了一个聚合   这是要计算的。

所以当你说:

MyModel.objects.annotate(other_model__some_flag=Value(True, output_field=BooleanField()))

您不是some_flag的注释other_model 即你没有:mymodel.other_model.some_flag

您正在other_model__some_flag注释mymodel 即你将拥有:mymodel.other_model__some_flag

2)我不确定SQL对您有多熟悉,但为了保持MyModel.objects.filter(other_model__some_flag=True)可能,即在执行JOINS时保留注释,{ {1}}必须ORM超过JOIN,例如:

subquery

这将是超级慢的,我并不感到惊讶他们没有这样做。

可能的解决方案

请勿对您的字段进行注释,但请将其添加为模型中的常规字段。

答案 2 :(得分:1)

简化的答案是模型对字段集合具有权威性,而管理者对模型集合具有权威性。在你努力使它干涸的时候,你做了WET,因为你改变了经理的场地收集。

为了解决这个问题,你必须教会模型有关查找的信息,并且需要使用Lookup API进行修复。

现在我假设您实际上没有使用固定值进行注释,因此如果该注释实际上可以简化为字段,那么您可能只是完成它,因为最终它需要映射到数据库表示。