gpt4 book ai didi

python - Django:如何使用子查询注释 M2M 或 OneToMany 字段?

转载 作者:太空宇宙 更新时间:2023-11-03 13:56:34 24 4
gpt4 key购买 nike

我有 Order 对象和 OrderOperation 对象,它们表示对订单的操作(创建、修改、取消)。

从概念上讲,一个订单有 1 到多个订单操作。每次对订单进行操作时,都会在此操作中计算总数。这意味着当我需要查找订单的属性时,我只是使用子查询获取最后一个订单操作属性。

简化代码

class OrderOperation(models.Model):
order = models.ForeignKey(Order)
total = DecimalField(max_digits=9, decimal_places=2)

class Order(models.Model)
# ...

class OrderQuerySet(query.Queryset):

@staticmethod
def _last_oo(field):
return Subquery(OrderOperation.objects
.filter(order_id=OuterRef("pk"))
.order_by('-id')
.values(field)
[:1])

def annotated_total(self):
return self.annotate(oo_total=self._last_oo('total'))

这样,我可以运行 my_order_total = Order.objects.annotated_total()[0].oo_total。效果很好。

问题

计算总数很容易,因为它是一个简单的值。但是,当存在M2M或OneToMany字段时,此方法不起作用。例如,使用上面的例子,让我们添加这个字段:

class OrderOperation(models.Model):
order = models.ForeignKey(Order)
total = DecimalField(max_digits=9, decimal_places=2)
ordered_articles = models.ManyToManyField(Article,through='orders.OrderedArticle')

像下面这样写是行不通的,因为它只返回 1 个外键(不是所有 FK 的列表):

def annotated_ordered_articles(self):
return self.annotate(oo_ordered_articles=self._last_oo('ordered_articles'))

目的

整个目的是允许用户在所有订单中搜索,在输入中提供列表或文章。例如:“请找出至少包含第 42 条或第 43 条的所有订单”,或“请找出恰好包含第 42 条和第 43 条的所有订单”等。

如果我能得到类似的东西:

>>> Order.objects.annotated_ordered_articles()[0].oo_ordered_articles
<ArticleQuerySet [<Article: Article42>, <Article: Article43>]>

甚至:

>>> Order.objects.annotated_ordered_articles()[0].oo_ordered_articles
[42,43]

那会解决我的问题。

我目前的想法

  • 可能类似于 ArrayAgg (我正在使用 pgSQL)可以解决问题,但我不确定如何在我的案例中使用它。
  • 也许这与 values() 方法有关,该方法似乎并非旨在处理文档中所述的 M2M 和 1TM 关系:

values() and values_list() are both intended as optimizations for a specific use case: retrieving a subset of data without the overhead of creating a model instance. This metaphor falls apart when dealing with many-to-many and other multivalued relations (such as the one-to-many relation of a reverse foreign key) because the “one row, one object” assumption doesn’t hold.

最佳答案

如果您只想从所有文章中获取一个变量(即名称),

ArrayAgg 将非常有用。如果您需要更多,有更好的选择:

prefetch_related

相反,您可以预取每个 Order,最新的 OrderOperation 作为一个整体对象。这增加了无需额外魔法即可轻松从 OrderOperation 获取任何字段的能力。

唯一需要注意的是,您总是会得到一个包含一个操作的列表,或者当所选订单没有任何操作时会得到一个空列表。

为此,您应该使用 prefetch_related queryset 模型连同 Prefetch objectOrderOperation 的自定义查询。示例:

from django.db.models import Max, F, Prefetch

last_order_operation_qs = OrderOperation.objects.annotate(
lop_pk=Max('order__orderoperation__pk')
).filter(pk=F('lop_pk'))

orders = Order.objects.prefetch_related(
Prefetch('orderoperation_set', queryset=last_order_operation_qs, to_attr='last_operation')
)

然后您可以只使用 order.last_operation[0].ordered_articles 获取特定订单的所有订购商品。您可以将 prefetch_related('ordered_articles') 添加到第一个查询集以提高性能并减少对数据库的查询。

关于python - Django:如何使用子查询注释 M2M 或 OneToMany 字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54863449/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com