Thursday, 9 September 2021

Creating annotation based on subquery with parameters

I have two models in my app, Event and Registration, with the most important fields below:

class Event(models.Model):
    ...
    capacity = models.IntegerField()
    ...
class Registration(models.Model):
    timestamp = models.DateTimeField(auto_now_add=True)
    event = models.ForeignKey(Event, on_delete=models.CASCADE)
    uses_bus = models.BooleanField(default=False)
    is_vegan = models.BooleanField(default=False)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        default=None)

Update

This could be solved by implementing a field on the Registration model indicating whether the registration is one of the #capacity registrations. Is that possible to implement?

The original problem

Now, in either a field or in the admin, I am trying to get the number of registrations where the user has marked uses_bus and the number of registrations where the user has marked is_vegan. This could be something like this:

class EventAdmin(admin.ModelAdmin):
    list_display = ('title', 'date', 'nr_registrations', 'nr_bus')
    inlines = [RegistrationInline]

    def get_queryset(self, request):
        queryset = super(EventAdmin, self).get_queryset(request)
        return queryset.annotate(
            nr_registrations = Count('registration'),
            nr_bus = Count('registration', filter=Q(registration__uses_bus=True)),
            nr_vegan = Count('registration', filter=Q(registration__is_vegan=True)),
        )

    def nr_registrations(self, obj):
        return obj.nr_registrations
    
    def nr_bus(self, obj):
        return obj.nr_bus
    
    def nr_vegan(self, obj):
        return obj.nr_vegan

but this obviously finds the total number of bus registrations, not the number of bus registrations for the ones that can actually attend the event because of the capacity constraints.

In SQL I would solve this like this:

SELECT SUM(uses_bus)
FROM (SELECT CAST(uses_bus AS INTEGER)
     FROM events_registration WHERE event_id = *event id* ORDER BY id LIMIT *capacity*)
AS a

but I can't do this, because even if I would use RawSQL I could not use F() to pass the event id and capacity as parameters.

Is this possible to solve? I am also open to augmenting the models, adding fields etc.



from Creating annotation based on subquery with parameters

No comments:

Post a Comment