Evgenii Legotckoi
Evgenii Legotckoi30. Dezember 2017 10:50

Django - Tutorial 029. Private Nachrichten und Chats auf der Site hinzufügen - Teil 1

Gemäß der etablierten Tradition werde ich Ihnen über meine Erfahrungen bei der Implementierung neuer Funktionen auf der Website berichten. Im Moment handelt es sich bei dieser Funktionalität um private Nachrichten zwischen Benutzern. Natürlich funktioniert es jetzt nicht so gut wie in bekannten sozialen Netzwerken ... aber am Ende wird alles funktionieren. Hauptsache Forenfeedback bitte.

So. Ich wollte unbedingt persönliche Nachrichten auf der Website hinzufügen, zumal ich dies bereits vor sechs Monaten erwähnt habe. Bleibt die Frage, wie man das überhaupt umsetzt. Bei der Suche im Internet bin ich auf eine Option gestoßen, wenn das nächste Datenmodell gebildet wird.

  • ID -Nachrichten
  • from_user - Absender
  • to_user - Empfänger
  • pub_date - Datum des Beitrags
  • Nachricht - Nachrichteninhalt

Ich habe versucht, diese Option zu implementieren, aber ich wurde davon abgehalten, dass ich plötzlich nach privaten Nachrichten chatten möchte? Warum also nicht gleich den Grundstein für Chats legen?


Chat- und Nachrichtenmodelle

Es wäre eine großartige Option für die Weiterentwicklung der Ressource. Aber in diesem Fall müssen wir zwei Modelle Chat und Message erstellen.

# -*- coding: utf-8 -*-

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _


class Chat(models.Model):
    DIALOG = 'D'
    CHAT = 'C'
    CHAT_TYPE_CHOICES = (
        (DIALOG, _('Dialog')),
        (CHAT, _('Chat'))
    )

    type = models.CharField(
        _('Тип'),
        max_length=1,
        choices=CHAT_TYPE_CHOICES,
        default=DIALOG
    )
    members = models.ManyToManyField(User, verbose_name=_("Участник"))

    @models.permalink
    def get_absolute_url(self):
        return 'users:messages', (), {'chat_id': self.pk }


class Message(models.Model):
    chat = models.ForeignKey(Chat, verbose_name=_("Чат"))
    author = models.ForeignKey(User, verbose_name=_("Пользователь"))
    message = models.TextField(_("Сообщение"))
    pub_date = models.DateTimeField(_('Дата сообщения'), default=timezone.now)
    is_readed = models.BooleanField(_('Прочитано'), default=False)

    class Meta:
        ordering=['pub_date']

    def __str__(self):
        return self.message

Wenn mit dem Message -Modell alles klar sein sollte. Es gibt den Text der Nachricht, den Status, ob sie gelesen wurde, den Autor der Nachricht und den Chat, in dem sie gesendet wurde, sowie das Datum, an dem die Nachricht gesendet wurde.

Das Chat -Modell wird also etwas komplizierter. Erstens können Chats eine Art Geist sein. Die erste ist ein persönliches Gespräch zwischen zwei Personen. Der zweite Typ ist ein Gruppenchat. Warum wurde das gemacht? Dies war notwendig, um die Suche nach dem gewünschten Chat zu vereinfachen, wenn dem Benutzer von seiner persönlichen Seite aus eine Nachricht gesendet wird. Leider gibt es im Moment nur eine Möglichkeit, die erste Nachricht an den Benutzer zu senden. Das heißt, gehen Sie zu seiner Seite und klicken Sie auf die Schaltfläche, um eine Nachricht zu schreiben. Zukünftig wird es möglich sein, den Benutzer über die Dialogseite auf Ihrer persönlichen Seite zu finden. Das sind noch Zeitlimits innerhalb der ersten User Story , die ich selbst geschrieben habe.

Jeder Chat hat einen Many-to-Many -Link, der für die Liste der Teilnehmer innerhalb des Chats verantwortlich ist. Dank dessen können Sie das Anzeigen von Chats sowie die Möglichkeit, Chats zu schreiben, einschränken, wenn der Benutzer nicht zu diesem Chat eingeladen wurde.

urls.py

Welche Ansichten brauchen wir und welche Routen können wir machen? Um diese Funktionalität in der einfachsten Form zu implementieren, musste ich drei Ansichten und dementsprechend drei Routen in urls.py. schreiben.

url(r'^dialogs/$', login_required(views.DialogsView.as_view()), name='dialogs'),
url(r'^dialogs/create/(?P<user_id>\d+)/$', login_required(views.CreateDialogView.as_view()), name='create_dialog'),
url(r'^dialogs/(?P<chat_id>\d+)/$', login_required(views.MessagesView.as_view()), name='messages'),

Wir werden nicht darauf eingehen, für welche Anwendung wir diese Routen verwenden werden - das ist nicht wichtig.

Tatsächlich gibt es hier drei Ansichten, eine für die Dialogliste des Benutzers, eine weitere zum Erstellen eines Dialogs von der Seite eines anderen Benutzers und eine dritte für Nachrichten im Dialog.

Nachrichtenformular

In diesem Fall können Sie das Formular für das Muster verwenden. Wir geben das Modell an, sowie welche Felder angezeigt werden sollen, wenn das Label vom Eingabefeld entfernt wird.

Standardmäßig haben Sie einen normalen Textbereich. Ich verwende meinen eigenen benutzerdefinierten WYSIWYG -Editor.

class MessageForm(ModelForm):
    class Meta:
        model = Message
        fields = ['message']
        labels = {'message': ""}

Ansichten und Vorlagen

Zeigt eine Liste von Dialogen an

Um eine Liste aller Dialoge zu erhalten, an denen der Benutzer beteiligt ist, ist es notwendig, alle Chats nach Teilnehmern zu filtern, also nach dem Many-to-Many-Feld Mitglieder .

class DialogsView(View):
    def get(self, request):
        chats = Chat.objects.filter(members__in=[request.user.id])
        return render(request, 'users/dialogs.html', {'user_profile': request.user, 'chats': chats})

In diesem Fall habe ich eine Vorlage für Dialoge, in der ich einen autorisierten Benutzer und eine Liste von Chats übergebe. Ein aktiver Benutzer wird benötigt, um gelesene und ungelesene Nachrichten in der Dialogliste korrekt anzuzeigen.

Von größtem Interesse ist die Liste der Dialoge und deren Layout, daher wird hier nur der Teil des Templates vorgestellt, der für das Layout verantwortlich ist.

<div class="panel">
        {% load tz %}
        {% if chats.count == 0 %}
            <div class="panel panel-body">{% trans "Нет ни одного начатого диалога" %}</div>
        {% endif %}
        {% for chat in chats %}
            {% if chat.message_set.count != 0 %}
                {% with last_message=chat.message_set.last %}
                    {% get_companion user chat as companion %}
                    <a class="list-group-item {% if companion == last_message.author and not last_message.is_readed %}unreaded{% endif %}" href="{{ chat.get_absolute_url }}">
                        <img class="avatar-messages" src="{{ companion.userprofile.get_avatar }}">
                        <div class="reply-body">
                            <ul class="list-inline">
                                <li class="drop-left-padding">
                                    <strong class="list-group-item-heading">{{ companion.username }}</strong>
                                </li>
                                <li class="pull-right text-muted"><small>{{ last_message.pub_date|utc }}</small></li>
                            </ul>
                            {% if companion != last_message.author %}
                                <div>
                                    <img class="avatar-rounded-sm" src="{{ last_message.author.userprofile.get_avatar }}">
                                    <div class="attached-reply-body {% if not last_message.is_readed %}unreaded{% endif %}">{{ last_message.message|truncatechars_html:"200"|safe|striptags }}</div>
                                </div>
                            {% else %}
                                <div>{{ last_message.message|truncatechars_html:"200"|safe|striptags }}</div>
                            {% endif %}
                        </div>
                    </a>
                {% endwith %}
            {% endif %}
        {% endfor %}
    </div>

Sie können mein benutzerdefiniertes Einbettungs-Tag in der Vorlage sehen. Dieses Tag ist für die Rückgabe des Gesprächspartners im Dialog verantwortlich, um ein angemessenes Layout für die Dialogliste zu bilden und gelesene und ungelesene Nachrichten in der Dialogliste basierend auf den Informationen über den Gesprächspartner anzuzeigen.

@register.simple_tag
def get_companion(user, chat):
    for u in chat.members.all():
        if u != user:
            return u
    return None

Die Liste der Dialoge sieht also so aus:

Aktueller Dialog und Meldungen

Eine komplexere Logik ist bereits erforderlich, um die aktuellen Dialoge und Meldungen anzuzeigen. Tatsache ist, dass hier der Zugriff auf den Chat per ID erfolgt, aber es ist nicht nur erforderlich, zu versuchen, einen Dialog zu erhalten, sondern auch zu prüfen, ob es einen Benutzer in der Teilnehmerliste gibt, der versucht, hineinzukommen dieser Chat. Ist er in der Teilnehmerliste nicht vorhanden, wird ihm der Zugang zu diesem Chat verweigert. Dieselbe Ansicht behandelt unter anderem das Versenden von Nachrichten und das Markieren von Nachrichten als gelesen.

class MessagesView(View):
    def get(self, request, chat_id):
        try:
            chat = Chat.objects.get(id=chat_id)
            if request.user in chat.members.all():
                chat.message_set.filter(is_readed=False).exclude(author=request.user).update(is_readed=True)
            else:
                chat = None
        except Chat.DoesNotExist:
            chat = None

        return render(
            request,
            'users/messages.html',
            {
                'user_profile': request.user,
                'chat': chat,
                'form': MessageForm()
            }
        )

    def post(self, request, chat_id):
        form = MessageForm(data=request.POST)
        if form.is_valid():
            message = form.save(commit=False)
            message.chat_id = chat_id
            message.author = request.user
            message.save()
        return redirect(reverse('users:messages', kwargs={'chat_id': chat_id}))

Vorlage für Beitragsliste

{% if not chat %}
    <div class="panel panel-body">
        {% trans "Невозможно начать беседу. Не найден пользователь или вы не имеете доступа к данной беседе." %}
    </div>
{% else %}
    {% load tz %}
    {% if chat %}
        <div id="messages" class="panel">
            <div id="innerMessages">
                {% for message in chat.message_set.all %}
                        {% include 'users/message.html' with message_item=message %}
                {% endfor %}
            </div>
        </div>
    {% endif %}
    <div id="message_form">
        <form id="message-form" class="panel panel-body" method="post" >
            {% load bootstrap3 %}
            {% csrf_token %}
            {% bootstrap_form form %}
            <button type="submit" class="btn btn-default btn-sm" onclick="return ETextEditor.validateForm('message-form')"><span class="ico ico-comment"></span>{% trans "Отправить" %}</button>
        </form>
    </div>
{% endif %}

Vorlage für die Nachricht selbst

{% url 'users:profile' message_item.author.username as the_user_url%}
{% load tz %}
<div class="list-group-item {% if not message_item.is_readed %}unreaded{% endif %}">
    <a href="{{ the_user_url }}"><img class="avatar-comment" src="{{ message_item.author.userprofile.get_avatar }}"></a>
    <div class="reply-body">
        <ul class="list-inline">
            <li class="drop-left-padding">
                <strong class="list-group-item-heading"><a href="{{ the_user_url }}">{{ message_item.author.username }}</a></strong>
            </li>
            <li class="pull-right text-muted"><small>{{ message_item.pub_date|utc }}</small></li>
        </ul>
        <div>{{ message_item.message|safe }}</div>
    </div>
</div>

Ein Beispiel für den resultierenden Dialog

Beginnen Sie ein Gespräch mit einem Benutzer

Im Moment wurde nur eine Methode implementiert, um eine Konversation mit einem Benutzer zu beginnen. Es ist notwendig, auf die Seite des Benutzers zu gehen und auf die Schaltfläche "Nachricht schreiben" zu klicken, dann wird eine Anfrage über den Link gesendet, in dem ein Chat erstellt oder ein bestehender Chat mit diesem Benutzer gefunden wird. Hier wird geprüft, ob es sich bei dem Chat um einen Dialog oder eine Konversation mehrerer Nutzer handelt. Dadurch können Sie die Suche nach dem benötigten Dialog etwas vereinfachen.

class CreateDialogView(View):
    def get(self, request, user_id):
        chats = Chat.objects.filter(members__in=[request.user.id, user_id], type=Chat.DIALOG).annotate(c=Count('members')).filter(c=2)
        if chats.count() == 0:
            chat = Chat.objects.create()
            chat.members.add(request.user)
            chat.members.add(user_id)
        else:
            chat = chats.first()
        return redirect(reverse('users:messages', kwargs={'chat_id': chat.id}))

Achten Sie darauf, die Anzahl der Benutzer zu überprüfen, da es nur zwei Benutzer in einem persönlichen Gespräch geben kann, und wir überprüfen, ob diese Benutzer unsere autorisierten Benutzer sind, sowie der Benutzer, mit dem wir versuchen, ein Gespräch zu beginnen.

Nachdem der Chat erstellt wurde, leiten wir zur Nachrichtenseite weiter.

Für Django empfehle ich Timeweb-Hoster-VDS-Server .

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

M
  • 26. März 2018 07:34

Сделал всё в точности как написано, но возникает ошибка, возможно надо было что то импортировать во views.py(но это точно), мой первый проект на джанго, не кидайтесь тапками ). И так собственно ошибка:

NoReverseMatch at /messages/dialogs/

'users' is not a registered namespace
содержания views.py:(вот здесь думаю нне робит)
from django.shortcuts import render, redirect
from django.views import View
from .models import Chat
from .forms import MessageForm
from django.db.models import Count
from django.urls import reverse
from django.contrib import auth


# Create your views here.

class DialogsView(View):
    def get(self, request):
        chats = Chat.objects.filter(members__in=[request.user.id])
        return render(request, 'users/dialogs.html', {'user': request.user, 'chats': chats})

дальше всё как в статье
Evgenii Legotckoi
  • 26. März 2018 07:44

Скорее ошибка в том, что у вас App не зарегистрирован в проекте.

apps.py

from django.apps import AppConfig
 
 
class UsersConfig(AppConfig):
    name = 'users'
settings.py
INSTALLED_APPS = [
    'users.apps.UsersConfig',
]
M
  • 26. März 2018 08:22

Откуда вы взяли приложение users?
Я создал было для данной статьи отдельное приложение 'Messages', в нём есть файл app.py с содержанием:

from django.apps import AppConfig


class MessagesConfig(AppConfig):
    name = 'Messages'
у вас есть отдельно статья по созданию 'user.apps.UsersConfig'?
Evgenii Legotckoi
  • 26. März 2018 08:37

users - это приложение для отображения личного кабинета пользователей. И некоторые страницы отображаются в рамках этого приложения.
Полагаю, что в вашем случае нужно сменить users на Messages в ряде мест в шаблонах, например, эту строку

return redirect(reverse('users:messages', kwargs={'chat_id': chat.id}))
заменить на
return redirect(reverse('Messages:messages', kwargs={'chat_id': chat.id}))
Примерно так всё и поменять, отслеживая, чего не хватает.
Конкретно для личного кабинета пользователей статьи нет. Статья основана конкретно на коде данного сайта и является обобщённым примером основных моментов, что-то может быть опущено. Многие статьи ориентированы на наличие определённого опыта у программиста. Поэтому данные статьи и не являются полным руководством к повторению.
M
  • 26. März 2018 09:13

Ошибка не изменилась.

Evgenii Legotckoi
  • 26. März 2018 09:20

Ну а шаблоны? которые html. Там тоже есть {% url 'users:blablabla' %}

M
  • 26. März 2018 10:20

ну ошибка такая же но только не users а messeages найти не может

Evgenii Legotckoi
  • 26. März 2018 10:26

А вы когда создавали приложение для комментариев, вы его urls подключили в urls.py файле всего проекта?

Например, в этой статье есть два приложения: home и knowledge и их urls подключаются в urls.py файле всего проекта. Вы их подключали?
M
  • 27. März 2018 11:23

да, конечно, и в instaled_apps прописал как Messages.apps.MessagesConfig

M
  • 27. März 2018 11:56

и ещё, эта ссылка работает пока тебе или ты не напишешь сообщения
url(r'^dialogs/$', login_required(views.DialogsView.as_view()), name='dialogs'),

Evgenii Legotckoi
  • 27. März 2018 12:18

Внимательнее проанализируйте код из статьи и вывод ошибок, вы однозначно где-то накосячили. Вполне возможно, что вы не зарегистрировали пользовательский тег get_companion, Вы знаете как регистрировать пользовательские теги ?

y
  • 1. April 2018 14:23

Добрый день, очень интересные статьи у Вас на сайте, касательно Django  и  Python. Когда планируете создать часть 2 Чата. Евгений, это ведь не чат в режиме реального времени? каждый раз приходится обновлять страницу. Не планируете использовать для чата channels ?

Evgenii Legotckoi
  • 1. April 2018 14:58

Добрый день!

Спасибо за отзыв.
Всё планирую, а также планирую использовать channels, но пока не так много времени на внедрение всего функционала на сайте. Поэтому по срокам вообще ничего не могу сказать.
g
  • 14. Mai 2018 10:32

спасибо!

l
  • 10. Januar 2019 12:47

Thank you for the article. Is there the github folder or the source code folder for this chat features, because for the code provided here sometimes I don't really know the exact place to put and make the project work.

Evgenii Legotckoi
  • 11. Januar 2019 02:24

Hi, unfortunately - no. But You can ask your question on the forum of this site . I try answer on questions in all sections of forum.

D
  • 19. November 2019 16:00

Привет! Пытаюсь освоить создание чата по вашей статье, сделал все как написано. Пока что результат такой (см. на прикрепленные скрины). Подскажите, пожалуйста, почему такой внешний вид и вдруг мб я чего не учел? Я так понял того, что вы выложили - недостаточно для полноценного чата и у меня получился тот максимум, котоырй можно выжать из вашего кода. Подскажите, как развить дальше, пожалуйста, ресурс какой-нибудь или из своего поделитесь! Спасибо большое за статью!

Evgenii Legotckoi
  • 20. November 2019 02:52

Добрый день.
Вам нужно самостоятельно написать стили, то есть CSS, это здесь уже на усмотрение пользователей, то есть на ваше личное усмотрение. Изучайте CSS и делайте как хотите вёрстку.

B
  • 16. Februar 2020 13:36

Добрый вечер! Монжно по подробней о теге get_companion? ссылка не работает.

Evgenii Legotckoi
  • 17. Februar 2020 03:22

Добрый день. Это кастомный тег, помещается в файл, который находится в каталоге templatetags

  • myapp/
    • templatetags/
      • myapp.py
N
  • 11. Mai 2020 07:01

Добрый день. Подскажите пожалуйста как вы реализовали определение прочитано сообщение или нет.Спасибо

N
  • 11. Mai 2020 07:07

if request.user in chat.members.all():
chat.message_set.filter(is_readed=False).exclude(author=request.user).update(is_readed=True)
И можно подробнее эту строчку. Спасибо

Evgenii Legotckoi
  • 11. Mai 2020 10:44

А вот эта строчка как раз и отвечает за определение, прочитано сообщение или нет.
Данная строка фильтрует все непрочитанные сообщения, исключая сообщения, автором которых является текущий пользователь сессии. А потом делает все сообщения прочитанными. То есть если человек открывает диалог, то все входящие сообщения обновляются как прочитанные, а исходящие так и остаются непрочитанные до тех пор, пока другой участник диалога их не откроет диалог.

p
  • 5. Februar 2021 01:20
  • (bearbeitet)

Hi! could you please explain to me how to sort messages in the dialog by the pub_date of message not by the creation of the chat?
i mean that when someone sends me a message ,i want it to be on the top of messages in my dialog.

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25. Dezember 2023 10:30
Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken