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)