Reputation: 11
i'm new to django, please help me in below scenoria,
when i save the order inn admin panel the same_model function not working the order is saving with out a function.
The requirement is when i add order in admin panel when the quantity of orderedproduct is graeter the actual quantity it should through an error else it should reduce the ordered quantity from the product.
The order is working but save_model function is not working
admin.py
from django.contrib import admin
from django.contrib import messages
from django.utils.translation import ngettext
from .models import Category, Product, Order, OrderItem
from django.utils.html import format_html
class OrderItemInline(admin.TabularInline):
model = OrderItem
extra = 1
class OrderAdmin(admin.ModelAdmin):
inlines = [OrderItemInline]
list_display = ('id', 'user', 'order_date')
def save_model(self, request, obj, form, change):
errors = []
super().save_model(request, obj, form, change)
for order_item in obj.orderitem_set.all():
print(order_item.quantity)
if order_item.quantity > order_item.product.quantity:
errors.append(
f"Order quantity ({order_item.quantity}) for {order_item.product.name} exceeds available quantity ({order_item.product.quantity})."
)
else:
order_item.product.quantity -= order_item.quantity
order_item.product.save()
if errors:
# If there are errors, display a message and rollback the order creation
message = ngettext(
"The order was not saved due to the following error:",
"The order was not saved due to the following errors:",
len(errors),
)
messages.error(request, f"{message} {', '.join(errors)}")
obj.delete() # Rollback the order creation
admin.site.register(Category)
class ProductAdmin(admin.ModelAdmin):
list_display = ['display_image', 'name', 'quantity', 'price', 'category']
def display_image(self, obj):
if obj.image:
return format_html('<img src="{}" width="50" height="50" />'.format(obj.image.url))
return "No Image"
display_image.short_description = 'Image'
admin.site.register(Product,ProductAdmin)
admin.site.register(Order, OrderAdmin)
models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products', null=True)
image = models.ImageField(upload_to='static/product_images/', null=True, blank=True)
quantity = models.PositiveIntegerField(default=0)
def __str__(self):
return f"{self.name} - Quantity: {self.quantity}"
class Cart(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
products = models.ManyToManyField(Product, through='CartItem')
def __str__(self):
return f"Cart for {self.user.username}"
class CartItem(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
def __str__(self):
return f"{self.quantity} x {self.product.name} in Cart"
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
products = models.ManyToManyField(Product, through='OrderItem')
order_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Order #{self.id} by {self.user.username}"
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
def __str__(self):
return f"{self.quantity} x {self.product.name} in Order"
Upvotes: 1
Views: 174
Reputation: 476493
You need to trigger this after .save_related(…)
[Django-doc], since at that point, the many to many fields are saved:
class OrderAdmin(admin.ModelAdmin):
inlines = [OrderItemInline]
list_display = ('id', 'user', 'order_date')
def save_related(self, request, form, formsets, change):
super().save_related(request, request, form, formsets, change)
errors = []
for order_item in obj.orderitem_set.all():
print(order_item.quantity)
if order_item.quantity > order_item.product.quantity:
errors.append(
f"Order quantity ({order_item.quantity}) for {order_item.product.name} exceeds available quantity ({order_item.product.quantity})."
)
else:
order_item.product.quantity -= order_item.quantity
order_item.product.save()
if errors:
# If there are errors, display a message and rollback the order creation
message = ngettext(
'The order was not saved due to the following error:',
'The order was not saved due to the following errors:',
len(errors),
)
messages.error(request, f"{message} {', '.join(errors)}")
obj.delete() # Rollback the order creation
That being said, validation should not be done as a post-processing step: you can inject a custom ModelForm
in the `ModelAdmin and formsets, and do the validation at that point.
Here you will for example subtract the quantities of all items, and if then an error occurs, you forget to increase it back. While you can fix that, it is likely that in the future, more such things will fail (and race conditions will occur), so usually it is better to Look Before You Leap (LBYL) [wiki] here.
Upvotes: 0