I want to take two fields from my model, one is a float, and the other is a ManyToMany and do an arithmetic operation on them and add them in a field with annotate
At one point it worked for me and then it didn't.
I don't know what would be the correct way I show some things from my files.
models.py
from django.db.models import F, DecimalField
class Tax(models.Model):
name = models.CharField(max_length=50)
porcent = models.IntegerField()
def __str__(self):
return self.name
class Product(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
code_number = models.IntegerField(verbose_name='Bar Code')
image = models.ImageField(upload_to='products/')
purchase_price = models.DecimalField(max_digits=10, decimal_places=2)
sale_price= models.DecimalField(max_digits=10, decimal_places=2)
tax = models.ManyToManyField(Tax, blank=True, related_name="tax")
description = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
views.py
from django.shortcuts import render
from .models import Tax, Product, Invoice
from django.db.models import F, DecimalField
class InvoiceDashboard(View):
def get(self, request, *args, **kwargs):
products=Product.objects.all().annotate(amount_tax = ((F('sale_price') / 100) * F('tax__porcent'))).annotate(
price_total=(F('sale_price') F('amount_tax'))
)
context = {
'products': products,
}
return render(request, 'pos/cashier.html', context)
pos/cashier.html
{% extends 'pos/base.html' %}
{% block content %}
<tbody id="table_p">
{% for product in products %}
<tr id="{{ product.id }}" data-id="{{ product.id }}" data-saleprice="{{ product.sale_price }}" data-codenumber="{{ product.code_number }}" data-amounttax="{{ product.amount_tax|floatformat:2 }}">
<th scope="row">{{ product.code_number }}</th>
<td><img src="/media/{{ product.image }}" width="60" height="60"/>{{ product.title }}</td>
<td><input id="{{ product.id }}" type="number" value="1" placeholder="1" min="1" max="100" disabled></td>
<td id="{{ product.sale_price }}">{{ product.sale_price }}</td>
<td>{% for tax in product.tax.all %}{{ tax }} {% endfor %} | {{ product.amount_tax|floatformat:2 }}</td>
<td id="{{ product.price_total }}">{{ product.price_total|floatformat:2 }}</td>
<td >
<div >
<input type="checkbox" id="{{ product.id }}">
</div>
</td>
</tr>
{% endfor %}
</tbody>
{% endblock %}
How can I get the percentage of each of the Taxes in a Product Object?
CodePudding user response:
I would wager a wild guess and say your Tax
objects have interger fields without a value.
You haven't specified a default.
Secondly if its not what I said above, because it's a ManyToMany
relationship, it doesn't necessarily mean you created any tax objects.
E.g. simply by creating a Product
, doesn't create any related Tax
objects.
Therefore trying to aggregate or annotate an integer across the ManyToMany
will result in a NULL
value.
CodePudding user response:
I think the problem with your code above is that in your annotation using tax__porcent, you are not reflecting the fact that their are (potentially) multiple porcent values because it's a many to many relationship between product and tax. So a product won't have single tax_amount, but one for every tax porcent value. Unfortunately, annotations tend to label the calling model, not the related models.
There's two methods I can think of that would handle this. One is to use a through table (docs) for your manytomany. That will enable you to grab one record for each product/tax combination that you can then annotate to your hearts content.
The other method, whch is probably simpler if you want to keep your existing db structure, is to split the annotation process out of the queryset. You can add fields to querysets before sending them to your template, so something like the following might work, as long as you don't requery later:
views.py
products=Product.objects.all().prefetch_related('tax')
for product in products:
for tax in product.tax.all():
tax.amount_tax = (product.sale_price / 100) * tax.porcent
tax.price_total = product.sale_price tax.amount_tax
template.html
<td>
{% for tax in product.tax.all %}
{{tax}}{{ tax.amount_tax | floatformat:2}}
{% endfor %}
</td>