Django自定义预取和条件注释

时间:2017-04-25 10:15:28

标签: django django-queryset

我使用自定义预取对象只获取一些相关对象,例如:

unreleased_prefetch = Prefetch("chants", Chant.objects.with_audio())
teams = Team.objects.public().prefetch_related(unreleased_prefetch)

这很好用,但我也想知道这些对象的数量并按这些过滤。我很高兴我现在可以将queryset用作Prefetch对象的参数(因为我大量使用自定义QuerySet / Managers)。

我是否可以重用此查询,我使用条件注释以相同的方式传递给Prefetch对象?

到目前为止,我的条件注释非常难看并且看起来像这样(它与我原来的chant with_audio自定义查询/过滤器一样):

.annotate(
    unreleased_count=Count(Case(
        When(chants__has_audio_versions=True, chants__has_audio=True, chants__flag_reject=False,
             chants__active=False, then=1),
        output_field=IntegerField()))
).filter(unreleased_count__gt=0)

它有效,但非常丑陋并且具有重复的逻辑。 有没有办法将queryset传递给我以同样的方式我可以将它传递给prefetch以避免重复?

1 个答案:

答案 0 :(得分:1)

不是说这是最佳做法或任何事情,但希望提供一种处理这种情况的潜在方法。

假设您有一个ChantQuerySet类:

class ChantQuerySet(models.QuerySet):
    def with_audio(self):
        return self.filter(chants__has_audio_versions=True, chants__has_audio=True,
                           chants__flag_reject=False, chants__active=False)

你用作经理做下面的事情,可能是:

class Chant(models.Model):
    # ...
    objects = ChantQuerySet.as_manager()

我建议将过滤器存储在QuerySet

from django.db.models import Q

class ChantQuerySet(models.QuerySet):
    @property
    def with_audio_filter(self):
        return Q(chants__has_audio_versions=True, chants__has_audio=True,
                 chants__flag_reject=False, chants__active=False)

    def with_audio(self):
        return self.filter(self.with_audio_filter)

这使您能够执行此操作:

Chant.objects.annotate(
    unreleased_count=Count(Case(
        When(ChantQuerySet.with_audio_filter, then=1),
        output_field=IntegerField()))
).filter(unreleased_count__gt=0)

现在,您可以在一个地方更改过滤器,如果需要,可以在任何地方更改它。对我来说,将此过滤器存储在QuerySet中是有意义的,我个人认为没有错,但那只是我。

我改变的一件事是,要么with_audio_filter属性被缓存,要么在初始化ChantQuerySet时将其存储在构造函数的字段中。