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', чтобы получить другие поля беседы.

Надеюсь, это поможет!

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