Как можно удалить объект, если другой объект является emty?
Итак, я начинающий кодер, разрабатывающий проект электронной коммерции на Django. Мне удалось добавить функцию "delete item" для удаления товаров из корзины. Однако, когда товар удаляется, заказ не удаляется. OrderItem и Order - это две отдельные модели, я бы хотел, чтобы когда OrderItem полностью удален и корзина пуста, сам заказ тоже удалялся. Я прикреплю код моделей и функций.
Модель заказа:
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True, blank=True)
date_ordered = models.DateTimeField(auto_now_add=True)
complete = models.BooleanField(default=False)
transaction_id = models.CharField(max_length=100, null=True)
def __str__(self):
return str(self.id)
@property
def get_cart_total(self):
orderitems = self.orderitem_set.all()
total = sum([item.get_total for item in orderitems])
return total
@property
def get_cart_items(self):
orderitems = self.orderitem_set.all()
total = sum([item.quantity for item in orderitems])
return total
МодельOrderItem:
class OrderItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True)
quantity = models.IntegerField(default=0, null=True, blank=True)
date_added = models.DateTimeField(auto_now_add=True)
@property
def get_total(self):
total = self.product.price * self.quantity
return total
Функция для удаления элементов:
def eliminaritem(request, id):
if request.method == "POST":
item = OrderItem.objects.get(product = id)
item.delete()
return render(request, "store/deletecartitem.html", {'item': item})
if OrderItem.objects.all == None:
customer = request.user.customer
order = Order.objects.all(customer = customer)
order.delete()
Первая часть этой функции работает, однако то, как я подошел ко второму IF, ужасно. Итак, в основном, когда нет ни одного OrderItems, заказ должен быть удален. Всякий раз, когда товар добавляется в корзину, заказ создается
Моделирование не очень эффективно, я думаю. Я бы попытался сделать так, чтобы у пользователя был только один незавершенный заказ, иначе трудно понять что будет тем заказом, который вы ищете:
from django.db.models import Q
class Order(models.Model):
customer = models.ForeignKey(
Customer, on_delete=models.SET_NULL, null=True, blank=True
)
date_ordered = models.DateTimeField(auto_now_add=True)
complete = models.BooleanField(default=False)
transaction_id = models.CharField(max_length=100, null=True)
def __str__(self):
return f'{self.id}'
class Meta:
constraints = [
models.UniqueConstraint(
fields=('customer',),
condition=Q(complete=False),
name='incomplete_order_per_customer',
)
]
Теперь что касается представления, представление может иметь побочные эффекты только в том случае, если метод является запросом POST, PUT, PATCH или DELETE. Ваш второй if
не гарантирует этого, и поэтому не соответствует HTTP. Кроме того, ваше представление будет удалять OrderItem
, если существует точно один такой элемент. Это означает, что если два пользователя имеют одинаковые OrderItem
, то это приведет к ошибке, а если другой пользователь имеет такой OrderItem
, то другой пользователь может сделать такой запрос, чтобы получить этот OrderItem
удаленный
Вы можете определить OrderItem
для вошедшего клиента и удалить его с помощью:
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_http_methods
@login_required
@require_http_methods(['DELETE', 'POST'])
def eliminaritem(request, id):
order_items = OrderItem.objects.filter(
order__complete=False, order__customer__user=request.user
)
item = get_object_or_404(order_items, product_id=id)
item.delete()
if not order_items.exists():
Order.objects.filter(
customer__user=request.user, completed=False
).delete()
return redirect('name-of-some-view')
Note: You can limit views to a view to authenticated users with the
@login_required
decorator [Django-doc].
Note: In case of a successful POST request, you should make a
redirect
[Django-doc] to implement the Post/Redirect/Get pattern [wiki]. This avoids that you make the same POST request when the user refreshes the browser.
Я предполагаю, что вы используете представление eliminaritem()
для удаления элемента.
def eliminaritem(request, id):
if request.method == "POST":
item = OrderItem.objects.get(product = id)
item.delete()
return render(request, "store/deletecartitem.html", {'item': item})
if OrderItem.objects.all == None:
customer = request.user.customer
order = Order.objects.all(customer = customer)
order.delete()
Проблема здесь в том, что OrderItem.objects.all()
вернет все OrderItems всех заказов. До тех пор, пока где-то есть корзина с товаром, запрос никогда не будет пустым. Кроме того, поскольку вы возвращаетесь до того, как может быть выполнен второй if, код никогда не будет достигнут.
def eliminaritem(request, id):
if request.method == "POST":
item = OrderItem.objects.get(product = id)
# First get the order behind the item.
relevant_order = item.order
# Check if order is from that user before deletion.
if not relevant_order.customer == request.user
# return some 403 error
item.delete()
# After deletion of the item check if there are items left.
if not relevant_order.orderitem_set.all()
relevant_order.delete()
return render(request, "store/deletecartitem.html", {})
Я использовал ваш код, чтобы вы могли быстро внести изменения.