Django Rest视图优化具有多级深度的查询集

euoag5mw  于 2023-07-01  发布在  Go
关注(0)|答案(1)|浏览(125)

我有一个视图,它需要来自第三、第四级关系中许多相关对象的数据。
下面是视图:

class OrderViewSet(ModelViewSet):
    queryset = Order.objects.select_related().prefetch_related().order_by('-id')

现在,在序列化器中,我必须获取此订单的相关项目的库存,然后对其执行某些计算。

class OrderListSerializer(ModelSerializer):
    shortages = SerializerMethodField()
    def get_shortages(self, obj):
        item = obj.order_line.product.item
        stock =  (
                order_line.product.item.item_instances.filter(stock_quantity__gt=0)
                .aggregate(stock=Coalesce(Sum("stock_quantity"), 0, output_field=FloatField()))
                .get("stock", 0)
            )
        required_stock = self.calculate_required_stock()
        if stock < required_stock: .....

在上面的代码中,order_line.product.item.item_instances行表示4个模型,如果我用('order_line__product__item')('order_line__product__item__item_instances')填充查询集的prefetch_related,发送到数据库的查询仍然是以千为单位的。
我怎样才能改善这种情况。

更新新增示例机型

class Order(models.Model):
    order_line = models.ForeignKey(
        "OrderLine",
        on_delete=models.CASCADE,
        related_name="orders",
    )
    type = models.CharField(choices=TYPE_CHOICES, max_length=13, blank=True)
    start_date = models.DateField(blank=True, null=True)
    ....................

class OrderLine(models.Model):
    sales_order = models.ForeignKey(
        "SalesOrder",
        on_delete=models.CASCADE,
        related_name="sales_order_lines",
    )
    product = models.ForeignKey("Product", on_delete=models.PROTECT)
    quantities = models.JSONField(null=True, blank=True)
    ..............................

class Product(models.Model):
    design = models.ForeignKey(
        Design, on_delete=models.PROTECT, related_name="design_products"
    )
    item = models.ForeignKey(
        Item, on_delete=models.PROTECT, related_name="item_products"
    )
    ...............................

class Item(models.Model):
    shrinkage = models.FloatField(default=0.0)
    ..............................

class ItemInstance(models.Model):
    item = models.ForeignKey(
        Item, on_delete=models.PROTECT, related_name="item_instances"
    )
    stock_quantity = models.FloatField(
        default=0.0, blank=True, validators=[MinValueValidator(0)]
    )
u5rb5r59

u5rb5r591#

prefetch_related将填充order_line.product.item.item_instances上的一些缓存。如果您尝试对查询集执行.filter或任何其他操作,从而使.prefetch_related调用无效,则此缓存将被丢弃。
你可以这样做:

class SumSubquery(Subquery):
    template = (
        "(SELECT SUM(%(field_to_get_sum_from)s) FROM (%(subquery)s) "
        "as subquery)"
    )

    def __init__(self, field_to_get_sum_from, queryset, output_field):
        super().__init__(
            queryset,
            output_field,
            field_to_get_sum_from=field_to_get_sum_from,
        )

item_instance_qs = ItemInstance.objects.all()
item_instance_qs = item_instance_qs.filter(item__item_products__orderline_set__orders__id=OuterRef("pk"))
item_instance_qs = item_instance_qs.values("stock_quantity")

qs = Order.objects.all()
qs = qs.annotate(stock=SumSubquery("sotck_quantity", item_instance_qs, IntegerField()))

class OrderListSerializer(ModelSerializer):
    shortages = SerializerMethodField()
    def get_shortages(self, obj):
        stock =  obj.stock
        required_stock = self.calculate_required_stock()
        if stock < required_stock: .....

相关问题