亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Django:使用傳播數據優化查詢

Django:使用傳播數據優化查詢

胡說叔叔 2021-11-16 15:25:48
我有Order對象和OrderOperation對象代表對訂單的操作(創建、修改、取消)。從概念上講,一個訂單有一對多的訂單操作。每次對訂單進行操作時,都會在此操作中計算總數。這意味著當我需要找到一個訂單的總數時,我只得到最后一個訂單操作的總數。簡化的代碼class OrderOperation(models.Model):    order = models.ForeignKey(Order)    total = DecimalField(max_digits=9, decimal_places=2)class Order(models.Model):    @property    def last_operation(self) -> Optional['OrderOperation']:        try:            qs = self.orderoperation_set.all()            return qs[len(qs) - 1]        except AssertionError:  # when there is a negative indexing (no operation)            # IndexError can not happen            return None    @property    def total(self) -> Optional[Decimal]:        last_operation = self.last_operation        return last_operation.total if last_operation else None問題由于接到的訂單很多,每次想做“總低于5歐元的訂單”之類的簡單過濾,需要很長時間,因為要瀏覽所有訂單,使用如下,明顯不好查詢:all_objects = Order.objects.all()Order.objects.prefetch_related('orderoperation_set').filter(    pk__in=[o.pk for o in all_objects if o.total <= some_value])我目前的想法/我嘗試過的數據非規范化?我可以簡單地創建一個total屬性 on Order,并在每次創建操作時將操作總數復制到訂單總數中。然后,Order.objects.filter(total__lte=some_value)會工作。但是,在我的數據庫中復制數據之前,我想確保沒有更簡單/更清潔的解決方案。使用 annotate() 方法?不知何故,我希望能夠做到:Order.objects.annotate(total=something_magical_here).filter(total__lte=some_value)。好像是不可能的。單獨過濾然后匹配?order_operations = OrderOperation.objects.filter(total__lte=some_value)orders = Order.objects.filter(orderoperation__in=order_operations)這非??欤^濾很糟糕,因為我沒有過濾最后的操作,而是這里的所有操作。這是錯誤的。還有其他想法嗎?謝謝。
查看完整描述

1 回答

?
隔江千里

TA貢獻1906條經驗 獲得超10個贊

使用 annotate() 方法

好像是不可能的。


當然,這是可能的 ;) 您可以使用子查詢或一些巧妙的條件表達式。假設您想從上次訂單操作中獲取總金額,以下是子查詢示例:


from django.db.models import Subquery, OuterRef


orders = Order.objects.annotate(

    total=Subquery(                             # [1]

        OrderOperation.objects \

            .filter(order_id=OuterRef("pk")) \  # [2]

            .order_by('-id') \                  # [3]

            .values('total') \                  # [4]

            [:1]                                # [5]

    )

)

上面代碼的解釋:

  1. 我們正在向結果列表添加新字段,稱為totaltaht 將由子查詢填充。您可以Order將此查詢集中的任何其他模型字段訪問它(在評估它之后,在模型實例中或在過濾和其他注釋中)。您可以從Django 文檔中了解注釋的工作原理。

  2. 子查詢應該只針對當前訂單的操作調用。OuterRefjust 將被替換為對結果 SQL 查詢中選定字段的引用。

  3. 我們想按操作id降序排序,因為我們確實想要最新的。如果您的操作中有其他字段需要按順序排序(例如創建日期),請在此處填寫。

  4. 該子查詢應該只total從操作返回值

  5. 我們只想要一個元素。它是使用切片符號而不是普通索引來獲取的,因為在 django 查詢集上使用索引將立即調用它。切片只是LIMIT向 SQL 查詢添加子句,而不調用它,這就是我們想要的。

現在您可以使用:

orders.filter(total__lte=some_value)

只獲取您想要的訂單。您還可以使用該注釋來


查看完整回答
反對 回復 2021-11-16
  • 1 回答
  • 0 關注
  • 146 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號