Django order_by("datetime") не работает

Я пытаюсь получить все объекты из "Чата", различающиеся по полям phone_number (CharField) и agent (ForeignKey) и упорядоченные по datetime DateTimeUTCField(default=datetime.now)

chats = self.get_queryset().order_by('phone_number', "agent","-datetime").distinct("phone_number", "agent")
for chat in chats:
    print(chat.datetime)                                                 

но когда я вижу порядок чатов, чаты не отсортированы по дате

2024-02-12 14:12:29.352192
2024-02-12 16:14:36.196964
2024-02-12 14:20:26.696088
2024-02-12 14:16:36.225550
2024-02-12 17:25:45.896345
2024-02-12 17:35:58.424937
2024-02-12 15:07:23.808822
2024-02-12 16:14:37.775311
2024-02-12 14:19:28.939897
2024-02-12 16:14:34.493954
2024-02-12 16:41:08.697777

Почему? Я думаю, это потому, что у меня также есть order_by phone_number и agent, но если я удалю их, django поднимает

SELECT DISTINCT ON expressions must match initial ORDER BY expressions
LINE 1: SELECT DISTINCT ON ("lolo_agents_chat"."phone_number", "lolo...

Наблюдаемое вами поведение связано с тем, как метод Django distinct() работает в сочетании с order_by() при использовании базы данных PostgreSQL. Когда вы указываете поля в методе distinct(), PostgreSQL требует, чтобы эти поля были первыми в списке метода order_by(). Поэтому при попытке изменить порядок queryset после применения distinct().

возникает ошибка.

Чтобы добиться желаемого, вы не можете напрямую использовать distinct() с order_by(), который соответствует вашим требованиям, из-за ограничений PostgreSQL. В качестве обходного пути можно использовать подзапрос, который сначала выберет последнее время для каждой пары phone_number и агента, а затем соединит этот результат с исходной таблицей, чтобы получить строки, соответствующие этим последним временам, например, так:

from django.db.models import Max, OuterRef, Subquery

latest_chats_subquery = Chat.objects \
    .values('phone_number', 'agent') \
    .annotate(latest_datetime=Max('datetime')) \
    .values('latest_datetime')

chats = Chat.objects.annotate(
    latest_datetime=Subquery(
        latest_chats_subquery.filter(
            phone_number=OuterRef('phone_number'),
            agent=OuterRef('agent')
        )[:1]
    )
).filter(
    datetime=OuterRef('latest_datetime')
).order_by('phone_number', 'agent', '-datetime')

for chat in chats:
    print(chat.datetime)

Вернуться на верх