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 значения.

Вернуться на верх