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
相关产品推荐
相关产品推荐

