Django как использовать аннотацию с ManyToManyfield?
Я хочу взять два поля из моей модели, одно - float, а другое - ManyToMany, выполнить над ними арифметическую операцию и добавить их в поле с аннотацией
В какой-то момент он у меня сработал, а потом нет.
Я не знаю, как правильно показать некоторые вещи из моих файлов.
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 class="productO" 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 class="spinner" id="{{ product.id }}" type="number" value="1" placeholder="1" min="1" max="100" disabled></td>
<td class="sub-total-p" id="{{ product.sale_price }}">{{ product.sale_price }}</td>
<td>{% for tax in product.tax.all %}{{ tax }} {% endfor %} | {{ product.amount_tax|floatformat:2 }}</td>
<td class="total-p" id="{{ product.price_total }}">{{ product.price_total|floatformat:2 }}</td>
<td class="sub-select">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="{{ product.id }}">
</div>
</td>
</tr>
{% endfor %}
</tbody>
{% endblock %}
Как я могу получить процент от каждого из налогов в объекте продукта?
Я думаю, что проблема с вашим кодом выше в том, что в вашей аннотации, использующей tax__porcent, вы не отражаете тот факт, что их (потенциально) несколько значений porcent, потому что это отношение "многие ко многим" между продуктом и налогом. Таким образом, у продукта будет не одна сумма tax_amount, а одна для каждого значения налогового процента. К сожалению, аннотации обычно маркируют вызывающую модель, а не связанные модели.
Есть два метода, которые я могу предложить для решения этой проблемы. Первый - использовать сквозную таблицу (docs) для вашего manytomany. Это позволит вам получить одну запись для каждой комбинации продукта/налога, которую вы можете затем аннотировать по своему усмотрению.
Другой метод, который, вероятно, проще, если вы хотите сохранить существующую структуру базы данных, заключается в том, чтобы разделить процесс аннотирования от кверисета. Вы можете добавлять поля в наборы запросов перед отправкой их в шаблон, поэтому может сработать что-то вроде следующего, при условии, что вы не будете делать повторный запрос позже:
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>
Я бы рискнул предположить и сказал, что ваши Tax
объекты имеют промежуточные поля без значения.
Вы не указали значение по умолчанию.
Во-вторых, если это не то, что я сказал выше, поскольку это отношения ManyToMany
, это не обязательно означает, что вы создали какие-либо объекты налогообложения.
Например, простое создание Product
не создает никаких связанных Tax
объектов.
Поэтому попытка агрегировать или аннотировать целое число через ManyToMany
приведет к получению NULL
значения.