【问题标题】:django objects.create method is too slow How to make faster?django objects.create 方法太慢了如何使更快?
【发布时间】:2022-10-15 01:39:11
【问题描述】:

映射了多个表,当我创建发布请求时,

大约需要2~3秒。有什么办法可以解决吗?

我想这需要很长时间:

  1. 对象.create

  2. for 循环

  3. 产品.objects.get

    但是,我无法找到更好的方法..

    楷模:

    #product、Order、OrderItems、ShippingAddress 已映射

    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)
    

【问题讨论】:

  • 在 for 循环中访问数据库不是一个好主意。您可以分别用Q 过滤器和bulk_update 替换视图函数for 循环中的product = Product.objects.get(id=i['id'])product.save() 行,并摆脱for 循环。
  • 你能分享这个数据列表吗order_items我对结构感到困惑。

标签: django django-rest-framework


【解决方案1】:

只要您对 i['id'] 中的产品 ID 有足够的信任,您就可以在不获取产品的情况下实例化 order_items

for i in order_items:
    # product = Product.objects.get(id=i['id'])
    
    order_item = OrderItem.objects.create(
        order = order,
        product_id  = i['id'],   # set the id (magic suffix) without fetching product
        name = i['name'],
        qty = i['qty'],
        price = i['price'],
        image = i['image']
    )

除了使用.create,您可以将这些order_items 实例化为未保存实例的列表并使用OrderItem.bulk_create 创建它们。阅读bulk_create documentation;它有许多警告。

然后,您可以运行一个循环更新产品库存字段,使用 F expression 从产品行中的当前值中减去,而无需实际从数据库中获取产品对象

for i in order_items:
    product_id = i['id']
    Product.objects.filter(
         pk = product_id
    ).update(
         stock = F('stock') - i['qty']
    )

如果您确实将所有产品实例获取到具有更新库存值的列表中,那么还有bulk_update 可以让您在单个数据库操作中应用所有更新的库存值。这可能比用 F 表达式一个一个地做更好。您也可以使用批量获取它们

Product.objects.filter( pk__in=[ i['id'] for i in order_items ] )

(警告,我不认为查询集包含与您提供 i['id'] 值的顺序相同的对象)

将此视为头脑风暴。我不完全确定这是正确的,我真的不知道它是否会加快速度很多,一点点,或者根本没有。我很想知道,如果你尝试一下。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-07
    • 1970-01-01
    • 2015-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    相关资源
    最近更新 更多