Метод django objects.create слишком медленный Как сделать быстрее?

сопоставлены несколько таблиц и, когда я создаю пост-запрос,

это занимает около 2~3 секунд. Есть ли способы исправить это?

Полагаю, это занимает много времени на:

  1. objects.create

  2. for loop

  3. product.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)

view:

@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)

Вы можете инстанцировать order_items без получения продукта, если у вас есть достаточное доверие к идентификаторам продуктов в i['id']

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; она имеет ряд предостережений.

Вы можете запустить цикл, обновляющий поле товарного запаса с помощью выражения F для вычитания из текущего значения в строке товара без фактического получения объекта товара из БД

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'])

Рассматривайте это как мозговой штурм. Я не совсем уверен, что это правильно, и я действительно не знаю, ускорит ли это работу намного, немного или вообще. Мне будет интересно узнать, если вы попробуете это сделать.

Вернуться на верх