Django QuerySets, поле «Сумма отфильтрованных связанных объектов» без возврата NoneType - PullRequest
0 голосов
/ 03 декабря 2018

Есть ли способ выполнить сумму аннотации для отфильтрованных связанных полей модели без возврата Нет, когда все связанные результаты отфильтрованы?

Например,в приведенных ниже моделях я хотел бы иметь годовые и ежемесячные суммы пожертвований, но исключая пожертвования, которые не были успешными или были возвращены.

Я начал использовать Case / When, чтобы избежать проблемы получения None, а не 0 дляпользователи без пожертвований.Но мне все еще нужно исключить неудачные и возвращенные пожертвования.

Чтение Документы Джанго , я обнаружил, что вы можете установить пользовательский _base_manager , но затем "Менеджеры баз не используются при запросах к связанным моделям" .

Есть ли другой подход, который я мог бы использовать?

models.py

class Profile(Model):
    user = OneToOneField(
        User,
        on_delete=CASCADE,
        related_name='profile'
    )

    has_donor_access = BooleanField(
        default=False
    )

    ...

    objects = Manager()
    account_objects = AccountManager()


    def __str__(self):
        return "{0} :: {1}".format(
            self.user.get_full_name(), self.user.get_username())

class Donation(Model):
    user = ForeignKey(
        User,
        blank=True,
        null=True,
        on_delete=SET_NULL,
        related_name='donations',
        verbose_name=_('donor')
    )

    creation_date = DateTimeField(
        auto_now_add=True
    )

    amount = DecimalField(
        decimal_places=2,
        max_digits=9
    )

    is_success = NullBooleanField(
        default=True
    )

    is_refunded = NullBooleanField(
        default=False
    )

    ...

    def __str__(self):
        user_profile = self.user.profile if self.user else "Anonymous"
        return "{0} :: ${1} :: {2}".format(
            user_profile, self.amount, self.creation_date)

Manager.py

class AccountQuerySet(QuerySet):
    def with_donation_stats(self):
        a_month_ago = now() - relativedelta(months=1)
        a_year_ago = now() - relativedelta(years=1)

        return self.annotate(
            _yearly_donations_sum=Case(
                When(
                    Q(user__donations__isnull=False),
                    then=Sum(
                        'user__donations__amount',
                        filter=(
                            Q(user__donations__is_success=True)
                            & Q(user__donations__is_refunded=False)
                            & Q(user__donations__creation_date__gte=a_year_ago)
                        )
                    )
                ),
                default=0,
                output_field=PositiveIntegerField()
            ),
            _monthly_donations_sum=Case(
                When(
                    Q(user__donations__isnull=False),
                    then=Sum(
                        'user__donations__amount',
                        filter=(
                            Q(user__donations__is_success=True)
                            & Q(user__donations__is_refunded=False)
                            & Q(user__donations__creation_date__gte=a_month_ago)
                        )
                    )
                ),
                default=0,
                output_field=PositiveIntegerField()
            )
        )
    )

class AccountManager(Manager):
    def get_queryset(self):
        return AccountQuerySet(
            self.model,
            using=self._db
        )

    def with_donation_stats(self):
        return self.get_queryset().with_donation_stats()
...