Django QuerySet,在不返回NoneType

时间:2018-12-03 01:25:09

标签: django django-queryset django-managers django-related-manager django-annotate

是否有一种方法可以在 过滤的相关模型字段 上执行注记总和,而在所有相关结果都被过滤掉后不返回None?

例如,对于下面的模型,我希望获得年度和每月的捐赠金额,但不包括未成功捐赠或已退款的捐赠。

我开始使用Case / When来避免没有捐赠的用户收到None而不是0的问题。但是我仍然需要排除不成功和已退还的捐款。

阅读Django's docs时,我发现您可以设置一个 自定义_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)

managers.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()

0 个答案:

没有答案