【问题标题】:Django 2.0 - order a queryset by a field on the prefetch-related attributeDjango 2.0 - 通过预取相关属性上的字段对查询集进行排序
【发布时间】:2018-09-26 13:10:05
【问题描述】:

我的目标是在一个模型上运行查询,但根据通过 prefetch_related 获取的另一个模型中的字段对结果进行排序。

假设我有两个模型:

class ModelA(models.Model):
   ...some fields...

class ModelB(models.Model):
    ...some fields...
    model_a = models.ForeignKey(ModelA, db_column='id')
    year = models.IntegerField()

我试过了:

ModelA.objects.filter(...).prefetch_related(
    Prefetch(
        'modelb_set',
        queryset=ModelB.objects.filter().order_by('-year'),
        to_attr="modelb_date"
    )
).order_by('-modelb_date')

但这失败了,因为modelb_date 不是ModelA 上的一个字段,它是一个列表。我想要的是根据最新的关联date 字段(来自ModelB)订购ModelA 查询集。也就是说,如果ModelA 的实例One 具有modelb_date 属性= [x, y, z] 其中x.year = 2017 并且ModelA 的实例Two 具有modelb_date@8 属性= @9876543 v.year = 2018 那么查询将在 One 之前排序实例 Two

我正在使用 Django 2.0、python 3.6 和 Oracle 12c。

有人可以帮忙吗?谢谢!

【问题讨论】:

  • 这没有多大意义,因为 one ModelA 将映射到 multiple ModelBs,所以这不是一个好的标准.您需要“折叠”这些值(例如,minimummaximum 等)
  • 另外作为参考,一个写两个下划线,所以modelb__date
  • 假设我需要得到一个按所述排序的ModelA 查询集,我是否需要使用额外的查询和CaseWhen 做一些更复杂的事情?
  • 不,只是用Min(..)注解,然后按注解排序。
  • 我很困惑; Min 只能在 ModelA 的字段上工作,对吧?我正在尝试在ModelB 上找到一个字段的Min。我可以用RawSQL 做到这一点,但它运行速度很慢......

标签: django orm


【解决方案1】:

如果您想通过相关模型的某个值进行排序,并且具有 一对多 关系,那么您需要某种方式来首先“折叠 " 相关数据:例如取minimummaximum(根据数据类型,sumaverage 等也可能是有效的选项)。

例如,如果我们想根据相关ModelB 项的最小 dateModelAs 进行排序,那么我们可以使用.annotate(..).order_by(..) 组合,比如:

ModelA.objects.filter(...).prefetch_related(
    Prefetch(
        'modelb_set',
        queryset=ModelB.objects.filter().order_by('-year'),
        to_attr="modelb_date"
    )
).annotate(
    first_date=Min('modelb__date')
).order_by('-first_date')

所以这里我们将ModelAs 与first_date降序 的顺序排序,而first_date 是相关date 的最低date

【讨论】:

  • 不适用于 Django - 2.2.14。对于这里的情况: 'first_date' field does not exist for Model 会出现错误。
  • @TusharMittal:这可能是因为您没有正确使用.annotate(...)。请注意,您需要 .annotate(..) ModelA 模型,而不是 Prefetching。
  • 好的,这里提到的答案不起作用。这个问题仍然没有解决。如果我们想在另一个模型中使用 ModelA Left Outer Join 与 ModelB 进行基于字段的排序。我们如何做到这一点?最后,原始 SQL 似乎是唯一的选择。
  • @Willem Van Onsem 在你的回答中,有错字吗? annotate(first_date=Min('modelb__date'))你指的是Min的to_attr,对吧? annotate(first_date=Min('modelb_date')) 你的意思是:Min('modelb_date')Min('modelb__date') 喜欢 1 或 2 个下划线?
  • @TusharMittal:不,不是to_attrmodelb__date(带两个下划线),是指modelbdate字段。您是否在 BAForeignKey 中设置了 related_name
猜你喜欢
  • 2011-11-11
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 2020-08-28
  • 2015-08-31
  • 2016-02-22
  • 2016-04-21
  • 2014-08-26
相关资源
最近更新 更多