У меня есть модель события с начальным DateTime и конечным DateTime, а также количеством участников.
Для каждого объекта Event я хочу получить аннотированную сумму ВСЕХ участников в любом событии, котороеперекрывается с началом DateTime.Это так, чтобы я мог убедиться, что не слишком много участников в любой момент времени.
class Event(models.Model):
start = models.DateTime()
end = models.DateTime()
participants = models.IntegerField()
Я читал о функциях Window, и, возможно, это сработало бы здесь, но я не могу получитьэто правильно.
Я пробовал это, но это не работает, потому что он пытается сгруппировать события с ОДНОМ ТОЧКОМ начала DateTime, а не перекрывать периоды времени начала и окончания с исходным событием начала DateTime.
starts = Event.objects.annotate(
participants_sum=Window(
expression=Sum('participants'),
partition_by=[F('start'),],
order_by=ExtractDay('start').asc(),
),
).values('participants', 'participants_sum', 'start')
Будем благодарны за любые рекомендации!
Большое спасибо @ endre-both с его / ее помощью, я смог решить большую проблему.
конечный результат Мне нужны значения каждого начала и конца перехода в моей таблице событий, чтобы я мог определить периоды времени при слишком большом количестве участников.Но я волновался, что объяснить это будет слишком сложно.
Вот что я закончил с
from django.contrib.gis.db import models
from django.db.models import F, Window, Sum
from django.utils import timezone
overlap_filter_start = Q(start__lte=OuterRef('start'), end__gte=OuterRef('start'))
overlap_filter_end = Q(start__lte=OuterRef('end'), end__gte=OuterRef('end'))
subquery_start = Subquery(Event.objects
.filter(overlap_filter_start)
.annotate(sum_participants=Window(expression=Sum('participants'),))
.values('sum_participants')[:1],
output_field=models.IntegerField()
)
subquery_end = Subquery(Event.objects
.filter(overlap_filter_end)
.annotate(sum_participants=Window(expression=Sum('participants'),))
.values('sum_participants')[:1],
output_field=models.IntegerField()
)
# Will eventually filter the dates I'm checking over specific date ranges rather than the entire Event table
# but for simplicity, filtering from yesterday to tomorrow
before = timezone.now().date() - timezone.timedelta(days=1)
after = timezone.now().date() + timezone.timedelta(days=1)
events_start = Event.objects.filter(start__date__lte=after, start__date__gte=before).annotate(simultaneous_participants=subquery_start)
events_end = Event.objects.filter(end__date__lte=after, end__date__gte=before).annotate(simultaneous_participants=subquery_end)
# Here I combine the queries for *start* transition moments and *end* transition moments, and rename the DateTime I'm looking at to *moment*, and make sure to only return distinct moments (since two equal moments will have the same number of participants)
events = events_start.annotate(moment=F('start')).values('moment', 'simultaneous_participants').union(
events_end.annotate(moment=F('end')).values('moment', 'simultaneous_participants')).order_by('moment').distinct()
for event in events:
print(event)
print(events.count())
Теперь я могу взять полученный сравнительно небольшой результирующий набор запросов и процесс в Python, чтобы определитьгде число участников становится слишком большим, и когда оно падает до приемлемых уровней.
Возможно, есть более эффективный способ приблизиться к этому, но я очень доволен этим.Гораздо лучше, чем пытаться выполнить всю тяжелую работу в Python.
В результате получается что-то вроде этого:
{'simultaneous_participants': 45, 'moment': datetime.datetime(2019, 3, 23, 7, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 45, 'moment': datetime.datetime(2019, 3, 23, 11, 30, tzinfo=<UTC>)}
{'simultaneous_participants': 40, 'moment': datetime.datetime(2019, 3, 23, 14, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 40, 'moment': datetime.datetime(2019, 3, 23, 15, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 35, 'moment': datetime.datetime(2019, 3, 23, 16, 30, tzinfo=<UTC>)}
{'simultaneous_participants': 85, 'moment': datetime.datetime(2019, 3, 24, 19, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 125, 'moment': datetime.datetime(2019, 3, 25, 12, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 90, 'moment': datetime.datetime(2019, 3, 25, 12, 30, tzinfo=<UTC>)}
{'simultaneous_participants': 135, 'moment': datetime.datetime(2019, 3, 25, 13, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 110, 'moment': datetime.datetime(2019, 3, 25, 18, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 160, 'moment': datetime.datetime(2019, 3, 25, 19, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 160, 'moment': datetime.datetime(2019, 3, 25, 20, 30, tzinfo=<UTC>)}
{'simultaneous_participants': 115, 'moment': datetime.datetime(2019, 3, 25, 22, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 80, 'moment': datetime.datetime(2019, 3, 25, 23, 30, tzinfo=<UTC>)}
14