通过筛选的自联接字段将聚合添加到Admin list_display

时间:2016-02-10 20:51:00

标签: django django-admin django-orm

我想用一个有趣的值来扩充我的模特管理员之一。鉴于这样的模型:

class Participant(models.Model):
    pass

class Registration(models.Model):
    participant = models.ForeignKey(Participant)
    is_going = models.BooleanField(verbose_name='Is going')

现在,我想显示Registration Participant is_going False的其他SELECT reg.*, COUNT(past.id) AS not_going_num FROM registrations AS reg, registrations AS past WHERE past.participant_id = reg.participant_id AND past.is_going = False 的数量。所以,类似于这个SQL查询:

Admin

我想我可以根据Django Admin, Show Aggregate Values From Related Model扩展queryset()的{​​{1}}方法,并使用额外的Count进行注释,但我仍然无法弄清楚如何处理自我加入并过滤到这个。

我查看了Self join with django ORMDjango self join , How to convert this query to ORM query,但前者正在进行SELECT *而后者似乎有数据模型问题。

有关如何解决此问题的任何建议?

2 个答案:

答案 0 :(得分:4)

请参阅以前版本答案的编辑历史记录。

下面的管理员实施将为每个Registration模型显示“Not Going Count”。 “Not Going Count”是注册is_going=False的{​​{1}}计数。

participant

以下是对QuerySet的更全面的解释:

@admin.register(Registration)
class RegistrationAdmin(admin.ModelAdmin):

    list_display = ['id', 'participant', 'is_going', 'ng_count']

    def ng_count(self, obj):
        return obj.not_going_count
    ng_count.short_description = 'Not Going Count'

    def get_queryset(self, request):
        qs = super(RegistrationAdmin, self).get_queryset(request)
        qs = qs.filter(participant__registration__isnull=False)
        qs = qs.annotate(not_going_count=Sum(
            Case(
                When(participant__registration__is_going=False, then=1),
                default=0,
                output_field=models.IntegerField())
            ))
        return qs

过滤器使Django执行两个连接 - qs = qs.filter(participant__registration__isnull=False) 到参与者表,以及INNER JOIN到注册表。

LEFT OUTER JOIN

这是一个标准聚合,用于qs = qs.annotate(not_going_count=Sum( Case( When(participant__registration__is_going=False, then=1), default=0, output_field=models.IntegerField()) ) )) 计算SUM。这转换为SQL

is_going=False

为每个注册模型生成总和,并且总和属于注册的参与者。

答案 1 :(得分:0)

我可能会误解,但你可以为单个参与者做的事情:

participant = Participant.objects.get(id=1)
not_going_count = Registration.objects.filter(participant=participant,
                                              is_going=False).count()

对于所有参与者,

from django.db.models import Count
Registration.objects.filter(is_going=False).values('participant') \
                                           .annotate(not_going_num=Count('participant'))

关于aggregating for each item in a queryset的Django doc。