Evgeny Krasyuk
Evgeny Krasyuk

Reputation: 57

How to set django model field as method?

This is my model for user order

class Order(models.Model):
    """Order Models"""

    first_name = models.CharField(
        max_length=256,
        blank=False,
        verbose_name=_('Name'))

    total_order_cost = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=21,
        verbose_name=_('Total Order Cost'))

And my model for ordered items

class OrderItem(models.Model):
    """ Ordered Item Model """

    order = models.ForeignKey(Order,
        verbose_name=_('Order'))

    price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name=_('Price'))

    quantity = models.PositiveIntegerField(
        verbose_name=_('Quantity'))

    total_price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name=_('Total Price'))

    def get_cost(self):
        return self.price * self.quantity

    def save(self, *args, **kwargs):
        self.total_price = self.get_cost()
        super(OrderItem, self).save(*args, **kwargs)

Field total_price gets value from get_cost. How can I make field total_order_cost work the same way? I want it to get value from

def get_total_cost(self):
    return sum(item.get_cost() for item in self.items.all())

I can't overwrite method save for total_order_cost, because when I save order object - ordered items don't exist yet. My view:

def make_order(request):
    cart = Cart(request)

    if request.method == 'POST':
        form = OrderCreateForm(request.POST)

        if form.is_valid():
            order = form.save() # here I save order object

            for item in cart:   # and here I save ordered items
                OrderItem.objects.create(
                    order=order,
                    product=item.product,
                    price=item.product.price,
                    quantity=item.quantity)

            cart.clear()
            return HttpResponseRedirect('%s?status_message=%s'
                % (reverse('get_cart'), _('Thank you for your order!')))

    else:
        form = OrderCreateForm()

    return render(request, 'products/make_order.html', {
        'form': form})

UPDATE: I understood that I need to use

@property
def total_order_cost(self):
    return self.orderitem_set.aggregate(total=Sum(F('price') * F('quantity')))['total']

But I don't now how to use it. I tried it like this, but it doesn't work:

class Order(models.Model): 
    total_order_cost = models.DecimalField( db_column='total_order_cost', max_digits=10, decimal_places=2, verbose_name=_('Total Order Cost')) 
    @property 
    def total_order_cost(self): 
        return self.orderitem_set.aggregate(total=Sum(F('price') * F('quantity')))['total']

Upvotes: 0

Views: 3757

Answers (1)

Tom
Tom

Reputation: 22831

Unless you are running into performance problems, why store derived data at all? You could drop the total_price field from OrderItem and the total_order_cost from Order and simply query for it when needed using aggregation and F expressions. How about something like this:

@property
def total_order_cost(self):
    return self.orderitem_set.aggregate(total=Sum(F('price') * F('quantity')))['total']

Upvotes: 4

Related Questions