Django 1.11 фильтр по времени между последним и последним - PullRequest
3 голосов
/ 09 мая 2019

Давайте представим, что у нас есть модель Event:

class Event(models.Model):
    actor = models.ForeignKey(Actor, null=False, blank=False, on_delete=models.PROTECT)
    happened_datetime = models.DateTimeField(_('When did the event took place?'), blank=False, null=False)

и актерская модель:

class Actor(models.Model):
    name = models.CharField(_('Name'), max_length=100, blank=False, null=False, default='')

Через некоторое время у нас есть потенциально длинный список событий.

Как нам получить актеров, последние два события которых разделены на меньшее или большее, чем указанная временная дельта, например:

Дайте мне всех актеров, чьи последние и последующие события не разделены более чем на 10 дней

или

Дайте мне всех актеров, чьи последние и последующие события не разделены менее чем на 5 часов

Это Subquery путь? ExpressionWrapper с F выражениями?

Примечание: приведенные выше запросы означают, что мы должны также фильтровать по тем, кто вызвал как минимум два события, с чем-то похожим на это:

actors_with_at_least_two = Actor.objects.annotate(
    nb_events=Subquery(
        Event.objects.filter(
            actor=OuterRef('pk')
        ).values(
            'actor__pk'
        ).order_by().annotate(
            cnt=Count('*')
        ).values('cnt')[:1],
        output_field=models.IntegerField()
    )
).filter(
    nb_events__gte=2
)

Вышеописанное работает хорошо, но я борюсь с фильтрацией с помощью "самоанализа" на той же таблице (timedelta и т. Д.). Любые идеи / подсказки будут любезно оценены! : -)

Я пробовал со следующим:

events = Event.objects.annotate(
    delta_with_previous=Subquery(
        Event.objects.filter(
            actor=OuterRef('actor__pk')
        ).exclude(
            pk=OuterRef('pk')
        ).latest(
          'happened_datetime'
        ).values(
          'actor__pk'
        ).order_by().values(
          'happened_datetime'
        )[:1],
        output_field=models.DurationField
    )
).filter(
    delta_with_previous__lte=timedelta(days=2)
).order_by(
    'actor__pk'
)

Но, похоже, он работает недостаточно хорошо.

...