问题
I wants to create a field name:total which have the total price of all the products*quqantity. What I want is how can I do total = price*quantity
in django model. As you can see there can be more than one products.
I have join the OderItem model with Order through tabularinline.
class OrderItemInline(admin.TabularInline):
model = OrderItem
raw_id_fields = ['product']
class OrderAdmin(admin.ModelAdmin):
list_display = ['id','name', 'mobile_no','address','status', 'created']
list_editable = ['status']
list_per_page = 15
list_filter = ['status']
search_fields = ('id',)
inlines = [OrderItemInline]
admin.site.register(Order, OrderAdmin)
class Order(models.Model):
name = models.CharField(max_length=60)
email = models.EmailField(max_length=60,default=None, blank=True)
mobile_no = models.CharField(max_length=13, default=None)
address = models.CharField(max_length=150)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status_choices = (
('In Queue', 'In Queue'),
('Processing', 'Processing'),
('Ready', 'Ready'),
('Delivered', 'Delivered'),
('Paid', 'Paid'),
('Cancelled', 'Cancelled'),
)
status = models.CharField(max_length=15, choices=status_choices, default=status_choices[0][0])
class Meta:
ordering = ('created', )
def __str__(self):
return 'Order {}'.format(self.id)
def get_total_cost(self):
return sum(item.get_cost() for item in self.items.all())
class OrderItem(models.Model):
order = models.ForeignKey(Order, related_name='items',
on_delete=models.CASCADE)
product = models.ForeignKey(MenuVariant, related_name='order_items',
on_delete=models.CASCADE, default=None)
size = models.CharField(max_length=20, default=None)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.PositiveIntegerField(default=1)
# total = models.DecimalField(max_digits=10, decimal_places=2,default=0)
def __str__(self):
return '{}'.format(self.id)
def get_cost(self):
return self.price * self.quantity
回答1:
You can get the total price for an Order
directly with a database query, so there's no need to save it in the database:
from django.db.models import Sum, F, FloatField
orders = Order.objects.annotate(total=Sum(F('items__quantity')*F('items__price'), output_field=FloatField()))
This adds a total
field to each result of the query. So then:
price_of_first_order = orders.first().total
And if you want to display the total for an order
in the admin, subclass the ModelAdmin
and add get_total_cost
to the readonly_fields
, as explained here:
A read-only field can not only display data from a model’s field, it can also display the output of a model’s method or a method of the ModelAdmin class itself. This is very similar to the way ModelAdmin.list_display behaves.
回答2:
While I, like dirkgroten don't quite see the point of saving the value in a model field, you could add a field total=models.IntegerField(null=True, blank=True)
to your Order
model and use a post_save
to update the value.
So, since you already have a get_total_cost()
we can do something like:
def order_pre_save(sender, instance, **kwargs):
instance.total_cost = instance.get_total_cost()
pre_save.connect(order_pre_save, sender=Order)
EDIT: As dirkgroten points out you'll need the post_save
on the OrderItem
:
def order_item_pre_save(sender, instance, **kwargs):
instance.order.total_cost = instance.order.get_total_cost()
instance.order.save()
post_save.connect(order_item_pre_save, sender=OrderItem)
And you'd also need to update the value on delete:
def order_item_pre_delete(sender, instance, **kwargs):
instance.order.total_cost = instance.order.get_total_cost() - instance.get_cost()
instance.order.save()
post_delete.connect(order_item_pre_delete, sender=OrderItem)
来源:https://stackoverflow.com/questions/55472952/how-to-create-a-field-in-django-model-using-values-of-other-fields-of-same-model