Django query: застрял при попытке отобразить только один экземпляр модели на каждый id
Я пишу модуль беседы для приложения django и у меня не получается создать боковое меню, которое отображается для каждой беседы:
- имя получателя
- последнее сообщение в разговоре
- временная метка этого последнего сообщения
Я изо всех сил стараюсь написать точный запрос.
conversations = ChatRoom.objects.filter(building=building.building_id, participants__in=[user]).prefetch_related(
'participants','chat_set').order_by('-chat__timestamp')
Проблема с этим запросом в том, что он возвращает один объект чата на сообщение, и поэтому в шаблоне следующий код:
<ul class="flex flex-col space-y-1 mt-4 -mx-2 overflow-y-auto" style="height:300px">
<h2 class="my-2 mb-2 ml-2 text-lg text-gray-600">Chats</h2>
{% for convo in conversations %}
<li>
{% if convo.chat_set.last.content %}
{% for participant in convo.participants.all %}
{% if participant.id != request.user.id %}
<a href="{% url 'room' room_id=convo.id %}"
class="flex items-center px-3 py-2 text-sm transition duration-150 ease-in-out border-b border-gray-300 cursor-pointer hover:bg-gray-100 focus:outline-none">
<div class="w-10 h-10 rounded-full border-2 border-black flex justify-center items-center m-2">
<span> {{ participant.username|first|upper }}</span>
</div>
<div class="w-full pb-2">
<div class="flex justify-between">
<span class="block ml-2 font-semibold text-gray-600"> {{ participant.username }}</span>
<span class="block ml-2 text-sm text-gray-600">{{ convo.chat_set.last.timestamp}}</span>
</div>
<span class="block ml-2 text-sm text-gray-600">{{ convo.chat_set.last.content }}</span>
</div>
</a>
{% endif %}
{% endfor %}
</li>
{% for %}
{% endfor %}
</ul>
показывает одну строку на каждое отправленное сообщение, вместо того, чтобы показывать одну строку на беседу с последним сообщением в ней.
Честно говоря, я понятия не имею, как изменить запрос на бэкенде (попытка метода dinstinct()
не работает, и я также не нахожу способа, как изменить то, что у меня есть на фронте, чтобы показывать только один элемент за разговор.
Любая помощь приветствуется, и я готов поделиться дополнительной информацией, если потребуется.
Вы можете использовать метод annotate() в своем запросе, чтобы получить последнее сообщение для каждой беседы, а затем использовать его в своем шаблоне для отображения информации. Вот пример того, как вы можете изменить свой запрос:
from django.db.models import Max
conversations = ChatRoom.objects.filter(building=building.building_id, participants__in=[user]) \
.prefetch_related('participants', 'chat_set') \
.annotate(latest_message=Max('chat_set__timestamp')) \
.order_by('-latest_message')
Метод annotate() позволяет добавить в набор запросов новое поле, которое вычисляется по заданному выражению. В данном случае мы добавляем новое поле под названием latest_message, которое вычисляется путем взятия максимальной временной метки всех сообщений в беседе.
Тогда в своем шаблоне вы можете использовать convo.latest_message для доступа к метке времени последнего сообщения, и convo.chat_set.last.content для доступа к содержимому последнего сообщения.
Вы также можете использовать метод distinct() в своем запросе, чтобы исключить дублирующиеся результаты, например, так:
conversations = ChatRoom.objects.filter(building=building.building_id, participants__in=[user]) \
.prefetch_related('participants', 'chat_set') \
.annotate(latest_message=Max('chat_set__timestamp')) \
.order_by('-latest_message') \
.distinct()
Вы также можете использовать метод values() для группировки по разговорам и выбора максимальной метки времени, а затем использовать это в своем шаблоне.
conversations = ChatRoom.objects.filter(building=building.building_id, participants__in=[user]) \
.prefetch_related('participants') \
.values('id') \
.annotate(latest_message=Max('chat_set__timestamp')) \
.order_by('-latest_message')
В этом случае вам придется использовать поле 'id', чтобы получить другие поля беседы.
Надеюсь, это поможет!