0

Problem

When I add an annotation based on the reverse field, then a double "LEFT OUTER JOIN" appears in the SQL result. As a result, the sum annotation is considered incorrect (duplicated depending on the number of back annotations)

How to make sum annotation correct?

Django 4.0.3
Python 3.10

Result SQL

SELECT 
  "frontend_book"."id", 
  "frontend_book"."title", 
  "frontend_book"."author_id", 
  COALESCE(
    SUM("frontend_sell"."price"), 
    0
  ) AS "total_profit_author", 
  COALESCE(
    SUM(T4."price"), 
    0
  ) AS "total_profit_resolver" 
FROM 
  "frontend_book" 
  INNER JOIN "frontend_human" ON (
    "frontend_book"."author_id" = "frontend_human"."id"
  ) 
  LEFT OUTER JOIN "frontend_sell" ON (
    "frontend_human"."id" = "frontend_sell"."author_id"
  ) 
  LEFT OUTER JOIN "frontend_sell" T4 ON ( <----- HERE
    "frontend_human"."id" = T4."resolver_id"
  ) 
GROUP BY 
  "frontend_book"."id"

Models

class Human(models.Model):
    name = models.CharField(
        'Name',
        max_length=200,
        blank=True,
    )

class Book(models.Model):
    title = models.CharField(
        'Title',
        max_length=200,
        blank=True,
    )

    author = models.ForeignKey(
        Human,
        verbose_name='author',
        related_name='books',
        on_delete=models.CASCADE,
    )

class Sell(models.Model):
    author = models.ForeignKey(
        Human,
        verbose_name='Author',
        related_name='author_sells',
        on_delete=models.CASCADE,
    )

    resolver = models.ForeignKey(
        Human,
        verbose_name='Resolver',
        related_name='resolver_sells',
        on_delete=models.CASCADE,
    )

    price = models.FloatField(
        'Price',
        default=0,
        blank=True
    )

Views

from rest_framework.response import Response
from rest_framework.views import APIView
from backend.api.frontend.models import Book
from django.db.models import Sum, FloatField


from .serializers import BookSerializer

class TestView(APIView):
    def get(self, request):

        qs = Book.objects.all()

        qs = qs.annotate(
            total_profit_author = Sum(
                'author__author_sells__price',
                output_field=FloatField(),
                default=0,
            ),
            total_profit_resolver = Sum(
                'author__resolver_sells__price',
                output_field=FloatField(),
                default=0,
            ),
        )


        return Response({
            "data": BookSerializer(qs, many=True).data,
        })

Serializers

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    total_profit_author = serializers.FloatField(read_only=True)
    total_profit_resolver = serializers.FloatField(read_only=True)

    class Meta:
        model = Book
        fields = '__all__'
Maxim
  • 1
  • 2

0 Answers0