Фильтр набора запросов Django для самых последних в прошлом и всего в будущем - PullRequest
2 голосов
/ 15 мая 2019

Контекст

У меня есть модели ContentBuild & ContentBuildDeviceGroupLink вот так:

# models.py

class ContentBuild(models.Model):
    content_build_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)


class ContentBuildDeviceGroupLink(models.Model):
    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    content_build_uuid = models.ForeignKey(ContentBuild, on_delete=models.CASCADE, related_name='content_build_deploy')
    group_uuid = models.ForeignKey(DeviceGroup, on_delete=models.CASCADE)
    preload_date = UnixDateTimeField()
    release_date = UnixDateTimeField()

Цель

Я хочу вернуть самый последний ContentBuildDeviceGroupLink объект с release_date в прошлом, вместе со всеми ContentBuildDeviceGroupLink объектами с release_date в будущем.

Что я пробовал

Я пытался использовать Q object так:

in_future = Q(release_date__gte=timezone.now())
recent_past = Q(release_date__lt=timezone.now()).order_by('-release_date')[0]

ContentBuildDeviceGroupLink.objects
.filter(in_future & recent_past)

Это не работает и выдает ошибку:

'datetime.datetime' object has no attribute 'order_by'

Как мне отфильтровать объект с release_date в самом последнем прошлом И все объекты с release_date в будущем?

EDIT

Я изменил свой подход, чтобы сначала получить объект с самым последним прошлым, а затем отфильтровать все объекты, которые больше или равны этой дате. Тем не менее, приведенный ниже набор запросов работает нормально, когда существует только 1 ContentBuild, но возвращает все более старые объекты, когда существует более 1 связанных ContentBuild.

recent_past = ContentBuildDeviceGroupLink.objects
    .filter(release_date__lte=timezone.now()).order_by('release_date').first()

ContentBuildDeviceGroupLink.objects.filter(release_date__gt=recent_past.release_date)

Ответы [ 2 ]

2 голосов
/ 15 мая 2019

Вы используете order_by не с набором запросов.Попробуйте это.

in_future = Q(release_date__gte=timezone.now())
recent_past = Q(release_date__lt=timezone.now())

ContentBuildDeviceGroupLink.objects.filter(
    in_future & recent_past
).order_by('-release_date').first()
1 голос
/ 15 мая 2019

Ваша вторая попытка была близка, но имеет две проблемы:

  1. поскольку вы вынимаете объект first из упорядоченного списка, это парень с самым старым release_date, а не с самым последним
  2. использование gt вместо gte кажется небольшим подозрением ... Я удивлен, что что-то обнаружилось, когда был только один ContentBuild

Чтобы получить его в одном запросе к базе данных, у вас есть несколько вариантов в зависимости от версии django. Если вы работаете с django> = 2.0, вы можете сделать это следующим образом (у меня нет проекта, на котором я могу это проверить прямо сейчас):

from django.db.models import Max, Q, F

ContentBuildDeviceGroupLink.objects.annotate(
    start_date=Max('release_date', filter=Q(release_date__lt=timezone.now())
).filter(release_date__gte=F('start_date'))

Если вы работаете с django <2.0, попробуйте что-то вроде этого: </p>

from django.db.models import Subquery, F

recent_dates = ContentBuildDeviceGroupLink.objects.filter(
    release_date__lte=timezone.now()
).order_by('-release_date').values('release_date')

ContentBuildDeviceGroupLink.objects.annotate(
    start_time=Subquery(recent_dates[:1])
).filter(release_date__gte=F('start_time'))

Возможно, есть более разумный способ, но, похоже, это сработает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...