Django序列化器中Prefetch Related引发查询过多的优化咨询
问题原因与优化方案
问题根源
你遇到的查询量激增,核心原因是get_number_of_items方法里的obj.orderitems.filter(orderid=obj.orderid)调用绕过了prefetch_related缓存,触发了N+1查询问题。
虽然你已经通过prefetch_related('orderitems')预加载了所有关联的SubOrder数据,但filter()方法会生成全新的QuerySet,不会复用已缓存的prefetch结果——每处理一个Order对象,就会发起一次新的数据库查询,订单数量越多,查询次数就成倍数增长。
另外这里的filter逻辑完全多余:obj.orderitems本身就是当前Order对象关联的所有SubOrder集合,不需要再通过orderid=obj.orderid重复过滤。
优化方案
方案1:直接复用prefetch缓存数据
修改get_number_of_items方法,去掉多余的filter,直接遍历预加载好的关联数据:
@staticmethod def get_number_of_items(obj): total_ordered_products_quantity = sum(order_item.product_quantity for order_item in obj.orderitems.all()) return total_ordered_products_quantity
这里obj.orderitems.all()会直接调用prefetch缓存的结果,不会触发新的数据库查询。
方案2:数据库层面计算总和(更高效)
如果订单及子订单数量较大,在Python内存中循环求和会占用更多资源,推荐直接在数据库层面用annotate计算总和,把计算压力转移给数据库:
- 更新
setup_eager_loading方法,增加annotate逻辑:
from django.db.models import Sum @staticmethod def setup_eager_loading(queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related('orderitems') queryset = queryset.select_related('user_reference', 'store_reference') # 数据库层面直接计算子订单product_quantity的总和 queryset = queryset.annotate(total_quantity=Sum('orderitems__product_quantity')) return queryset
- 修改序列化器,去掉SerializerMethodField,直接调用annotate生成的字段:
class AllOrderSellerSerializer(serializers.ModelSerializer): orderitems = AllOrderItemsSellerSerializer(many=True) store_reference = serializers.CharField(source="store_reference.store_reference") user_reference = serializers.CharField(source="user_reference.user_reference") # 直接使用数据库计算好的total_quantity字段 number_of_items = serializers.IntegerField(source='total_quantity') class Meta: model = Order fields = ["order_number", "order_request_number", "store_reference", "user_reference","number_of_items", "total_amount", "orderitems"]
这种方式不仅彻底避免了额外查询,还能利用数据库的计算能力提升效率。
验证建议
可以使用django-debug-toolbar工具查看优化后的数据库查询次数,确认查询量不再随订单数量增长而激增(正常情况下仅需2-3次查询)。
内容的提问来源于stack exchange,提问作者Aswany Mahendran
相关产品推荐
相关产品推荐

