0

I have this model called Order which save the information about the user and another model called OrderItem which save the products which the user had ordered. i have join the OrderItem model with Order using Tabular Inline. Now what i want is to calculate the total price by multiplying the price and product quantity(which are the fields of OrderItem model) and save the total price in Order model as shown in the picture.
model admin view

I have asked this question before but didn't get the satisfactory answer.

from django.contrib import admin
from .models import Order, OrderItem
from pizza_app.models import UserTypes
from django.db.models.signals import pre_save
from django.db.models import Sum, F, FloatField

class OrderItemInline(admin.TabularInline):
    model = OrderItem


class OrderAdmin(admin.ModelAdmin):
    list_display = ['id','name','total' ,'mobile_no','address','status', 'created']
    list_editable = ['status']
    list_per_page = 10
    list_filter = ['status']
    readonly_fields= ['total']
    search_fields = ('id','mobile_no','name',)
    inlines = [OrderItemInline]
   


    def get_form(self, request, obj=None, **kwargs):
        form = super(OrderAdmin,self).get_form(request, obj,**kwargs)
        form.base_fields['Assigned_to'].queryset = form.base_fields['Assigned_to'].queryset.filter(staff_status='Delivery Boy')
        return form

admin.site.register(Order, OrderAdmin)
from django.db import models
from pizza_app.models import MenuVariant
from django.urls import reverse
from django.db.models import Sum, F, FloatField
from pizza_app.models import UserTypes
# from django.contrib.auth.models import User
from django.db.models.signals import pre_save,post_save

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])
    
    total = models.DecimalField(max_digits=10,decimal_places=2,default=0)

    Assigned_to = models.OneToOneField(UserTypes, on_delete=models.CASCADE,default=None, blank=True, null=True)


    class Meta:
        ordering = ('created', )

    def __str__(self):
        return 'Order {}'.format(self.id)


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)
    size = models.CharField(max_length=20, default=None)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.PositiveIntegerField(default=1)


    def __str__(self):
        return '{}'.format(self.id)
M_FarhanZia
  • 63
  • 1
  • 1
  • 13
  • 1
    Possible duplicate of [How to create a field in django model using values of other fields of same model?](https://stackoverflow.com/questions/55472952/how-to-create-a-field-in-django-model-using-values-of-other-fields-of-same-model) – dirkgroten Apr 09 '19 at 10:31
  • @dirkgroten i have mentioned that this question is already asked by me by i didnt get the satisfactory answer i remember u answered on that question but i didnt understand the solution that u gave. I asked for explanation but u didnt reply – M_FarhanZia Apr 09 '19 at 10:47
  • I updated my answer by telling you to add a readonly field to your ModelAdmin. Which is the same as the answer given below by @ruddra. Your question is asking us to write code for you, which is not the purpose of SO. You should at least show us what you've tried to display the total in the admin, so we can see why it's not working. – dirkgroten Apr 09 '19 at 10:56

1 Answers1

0

Rather than making a Model Field, you can make a method inside OrderAdmin to generate total in runtime. Then show it in the adminsite. Like this:

from django.db.models import Sum, F, ExpressionWrapper, DecimalField


class OrderAdmin(admin.ModelAdmin):
    list_display = ['id','name','order_total' ,'mobile_no','address','status', 'created']
    list_editable = ['status']
    list_per_page = 10
    list_filter = ['status']
    readonly_fields= ['order_total']
    search_fields = ('id','mobile_no','name',)
    inlines = [OrderItemInline]

    def order_total(self, obj):
         return obj.items.annotate(price_per_item=ExpressionWrapper(F('quantity')*F('price'), output_field=DecimalField())).aggregate(sum_total=Sum('price_per_item')).get('sum_total')

In total_order method in OrderAdmin, I am calculating the total of that order using aggregation. Then I have added the total_order to both fields and readonly_fields in OrderAdmin.

ruddra
  • 50,746
  • 7
  • 78
  • 101
  • it is giving this error ExpressionWrapper' object has no attribute 'aggregate' – M_FarhanZia Apr 09 '19 at 11:29
  • @M_FarhanZia I missed a bracket. Updated the answer – ruddra Apr 09 '19 at 11:31
  • according to dirkgroten if i add the get_total to readonly fields it will show the total in admin get_total is a function in Order model which calculate total But when i add get_total in readonly field it show a empty field can u tell why? – M_FarhanZia Apr 09 '19 at 11:43
  • I am not sure what @dirkgroten have said, but it could be that you need to update the queryset in `OrderAdmin`. Probably something like this: https://stackoverflow.com/a/12354293/2696165 . But I am out of context, so this could be something else, I am not sure. – ruddra Apr 09 '19 at 12:11