Как я могу отфильтровать набор запросов Django по последней из связанных моделей? - PullRequest
1 голос
/ 16 мая 2019

Представьте, что у меня есть следующие 2 модели в надуманном примере:

class User(models.Model):
    name = models.CharField()

class Login(models.Model):
    user = models.ForeignKey(User, related_name='logins')
    success = models.BooleanField()
    datetime = models.DateTimeField()

    class Meta:
        get_latest_by = 'datetime'

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

Я знаю, что следующее не работает, но оно иллюстрирует то, что я хочу получить:

User.objects.filter(login__latest__success=False)

Я предполагаю, что могу сделать это с объектами Q, и / или Case When, и / или какой-либо другой формойаннотации и фильтрации, но я не могу разобраться.

Ответы [ 3 ]

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

Мы можем использовать Subquery здесь:

from django.db.models import <b>OuterRef, Subquery</b>

<b>latest_login</b> = Subquery(Login.objects.filter(
    user=OuterRef('pk')
).order_by('-datetime').values('success')[:1])

User.objects.annotate(
    <b>latest_login=latest_login</b>
).filter(<b>latest_login=False</b>)

Это сгенерирует запрос, который выглядит следующим образом:

SELECT auth_user.*, (
    SELECT U0.success
    FROM login U0
    WHERE U0.user_id = auth_user.id
    ORDER BY U0.datetime DESC
    LIMIT 1
  ) AS latest_login
FROM auth_user
WHERE (
    SELECT U0.success
    FROM login U0
    WHERE U0.user_id = auth_user.id
    <b>ORDER BY U0.datetime</b>
    <b>DESC LIMIT 1</b>
  ) <b>= False</b>

Таким образом, результатом Subquery является success самого последнего Login объекта, и если это False, мы добавляем связанный User к QuerySet.

0 голосов
/ 17 мая 2019

Вы можете сначала аннотировать максимальные даты, а затем фильтровать на основе успеха и максимальной даты, используя F выражений :

User.objects.annotate(max_date=Max('logins__datetime'))\
    .filter(logins__datetime=F('max_date'), logins__success=False)
0 голосов
/ 17 мая 2019

для проверки bool используйте success=False и для получения последнего использования latest() ваш фильтр был выглядеть так:

User.objects.filter(success=False).latest()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...