You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中objects.create过慢:多表关联下单接口优化咨询

Django多表关联下单功能性能优化求助

我在Django中实现了关联Product、Order、OrderItem、ShippingAddress模型的下单功能,发送POST请求创建订单时耗时约2~3秒。我推测耗时主要集中在以下几点:

  • objects.create操作
  • 循环遍历逻辑
  • product.objects.get查询

目前找不到更优的实现方式,希望能得到优化方案。

模型代码

class Order(models.Model):
    user = models.ForeignKey(User, on_delete= models.CASCADE)
    order_date = models.DateTimeField(auto_now=True)
    is_paid = models.BooleanField(default=False)
    paid_at = models.DateTimeField(auto_now=False, null=True, blank=True)
    delivery_code = models.CharField(max_length=255, null=True, blank=True)
    is_delivered = models.BooleanField(default=False)
    delivered_date = models.DateTimeField(auto_now=False, null=True, blank=True)
    total_price = models.DecimalField(max_digits=7, decimal_places=2, null=True)   
    shipping_price = models.DecimalField(max_digits=7, decimal_places=2, null=True)   
    payment_method = models.CharField(max_length=255,null=True) 
    
    def __str__(self):
        return str(self.user)
    
class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete= models.CASCADE, null=True, blank=True)
    product = models.ForeignKey(Product, on_delete= models.CASCADE)
    name = models.CharField(max_length=200, null=True)
    image = models.CharField(max_length=255, null=True)
    qty = models.IntegerField(default=0, null=True)
    price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
    
    def image_preview(self):
        if self.image:
            return mark_safe('<img src="{0}" width="55" height="55" />'.format(self.image))
        else:
            return '(No image)'
    
    def __str__(self):
        return str(self.product)


class ShippingAddress(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    order = models.OneToOneField(Order, on_delete=models.CASCADE, null=True, blank=True)
    address = models.CharField(max_length=255, null=False)
    city = models.CharField(max_length=255, null=False)
    postal_code = models.CharField(max_length=255, null=False)
    country = models.CharField(max_length=255, null=False)
    
    def __str__(self):
        return str(self.user)

视图代码

@permission_classes(IsAuthenticated)
@api_view(['POST'])
def OrderCreate(request):
    data = request.data
    user = request.user
    order_items = data['orderItems']
    #1.create order
    order = Order.objects.create(
        user = user,
        total_price = data['totalPrice'],
        shipping_price = data['shippingPrice'],
        payment_method = data['paymentMethod']
    )
    
    #2.create orderItems 
    for i in order_items:
        product = Product.objects.get(id=i['id'])
        
        order_item = OrderItem.objects.create(
            order = order,
            product = product,
            name = i['name'],
            qty = i['qty'],
            price = i['price'],
            image = i['image']
        )
        
        #3. update stock
        product.stock -= i['qty']
        product.save()
        
    #4.create shipping address
    
    shipping_address = ShippingAddress.objects.create(
    user = user,
    order = order,
    address = data['shippingAddress']['address'],
    city = data['shippingAddress']['city'],
    postal_code = data['shippingAddress']['postalCode'],
    country = data['shippingAddress']['country'],
        
    )

    #5.serializing and save

    serializer = OrderSerializer(order, many=False)
    return Response(serializer.data)

优化方案

1. 批量查询Product,避免循环内多次get

循环内每次调用Product.objects.get(id=i['id'])都会发起单独的数据库查询,订单商品越多,查询次数越多。可以先收集所有商品ID,一次性查询并映射:

# 收集所有商品ID
product_ids = [item['id'] for item in order_items]
# 批量查询,用字典映射ID到实例
products = Product.objects.in_bulk(product_ids)

# 后续循环直接从字典取实例
for i in order_items:
    product = products[i['id']]
    # ...后续逻辑

2. 批量创建OrderItem,减少数据库写入次数

当前循环内每次OrderItem.objects.create都会单独写入数据库,改用bulk_create批量创建,大幅减少数据库交互次数:

order_items_list = []
for i in order_items:
    product = products[i['id']]
    order_item = OrderItem(
        order=order,
        product=product,
        name=i['name'],
        qty=i['qty'],
        price=i['price'],
        image=i['image']
    )
    order_items_list.append(order_item)
# 批量创建所有订单项
OrderItem.objects.bulk_create(order_items_list)

3. 批量更新商品库存,避免循环内save

循环内每次product.save()都是一次数据库写入,改用F()表达式直接在数据库层面做库存减法,既减少请求次数,还能避免并发更新问题:

from django.db.models import F

# 批量更新库存
for i in order_items:
    Product.objects.filter(id=i['id']).update(stock=F('stock') - i['qty'])

4. 事务包裹下单逻辑,提升性能并保证原子性

把整个下单流程放在数据库事务中,减少数据库提交次数,同时确保所有操作要么全部成功,要么全部回滚,避免数据不一致:

from django.db import transaction

@permission_classes(IsAuthenticated)
@api_view(['POST'])
def OrderCreate(request):
    data = request.data
    user = request.user
    order_items = data['orderItems']
    
    with transaction.atomic():
        # 1. 创建订单
        order = Order.objects.create(
            user=user,
            total_price=data['totalPrice'],
            shipping_price=data['shippingPrice'],
            payment_method=data['paymentMethod']
        )
        
        # 2. 批量查询商品
        product_ids = [item['id'] for item in order_items]
        products = Product.objects.in_bulk(product_ids)
        
        # 3. 批量创建订单项
        order_items_list = []
        for i in order_items:
            product = products[i['id']]
            order_item = OrderItem(
                order=order,
                product=product,
                name=i['name'],
                qty=i['qty'],
                price=i['price'],
                image=i['image']
            )
            order_items_list.append(order_item)
        OrderItem.objects.bulk_create(order_items_list)
        
        # 4. 批量更新库存
        for i in order_items:
            Product.objects.filter(id=i['id']).update(stock=F('stock') - i['qty'])
        
        # 5. 创建收货地址
        ShippingAddress.objects.create(
            user=user,
            order=order,
            address=data['shippingAddress']['address'],
            city=data['shippingAddress']['city'],
            postal_code=data['shippingAddress']['postalCode'],
            country=data['shippingAddress']['country'],
        )
    
    serializer = OrderSerializer(order, many=False)
    return Response(serializer.data)

5. 可选:索引优化

确保Product模型的id字段有索引(Django主键默认已加索引),如果后续有其他查询场景,可针对性添加字段索引,进一步提升查询速度。

内容的提问来源于stack exchange,提问作者Luke.Lee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 19:45:41