Метод django objects.create слишком медленный Как сделать быстрее?
сопоставлены несколько таблиц и, когда я создаю пост-запрос,
это занимает около 2~3 секунд. Есть ли способы исправить это?
Полагаю, это занимает много времени на:
objects.create
for loop
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'])
Рассматривайте это как мозговой штурм. Я не совсем уверен, что это правильно, и я действительно не знаю, ускорит ли это работу намного, немного или вообще. Мне будет интересно узнать, если вы попробуете это сделать.