Создание аннотации на основе подзапроса с параметрами
В моем приложении есть две модели, Event
и Registration
, с наиболее важными полями ниже:
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)
Теперь, либо в поле, либо в админке, я пытаюсь получить количество регистраций, где пользователь отметил uses_bus
и количество регистраций, где пользователь отметил is_vegan
. Это может быть что-то вроде этого:
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
но это, очевидно, определяет общее количество регистраций на автобус, а не количество регистраций на автобус для тех, кто действительно может посетить мероприятие из-за ограничений по вместимости.
В SQL я бы решил это следующим образом:
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
но я не могу этого сделать, потому что даже если бы я использовал RawSQL, я не мог бы использовать F() для передачи идентификатора события и мощности в качестве параметров.
Возможно ли это решить? Я также открыт для расширения моделей, добавления полей и т.д.
BrianD уже сказал, вы можете проверить capacity и calculated no, чтобы получить количество регистраций на автобус для тех, кто действительно может посетить мероприятие из-за ограничений по вместимости.
def nr_bus(self, obj):
if obj.capacity <= obj.nr_bus:
return obj.capacity
return obj.nr_bus
def nr_vegan(self, obj):
if obj.capacity <= obj.nr_vegan:
return obj.capacity
return obj.nr_vegan