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 .
Сделал всё в точности как написано, но возникает ошибка, возможно надо было что то импортировать во views.py(но это точно), мой первый проект на джанго, не кидайтесь тапками ). И так собственно ошибка:
NoReverseMatch at /messages/dialogs/
содержания views.py:(вот здесь думаю нне робит)Скорее ошибка в том, что у вас App не зарегистрирован в проекте.
Откуда вы взяли приложение users?
Я создал было для данной статьи отдельное приложение 'Messages', в нём есть файл app.py с содержанием:
users - это приложение для отображения личного кабинета пользователей. И некоторые страницы отображаются в рамках этого приложения.
Полагаю, что в вашем случае нужно сменить users на Messages в ряде мест в шаблонах, например, эту строку
Конкретно для личного кабинета пользователей статьи нет. Статья основана конкретно на коде данного сайта и является обобщённым примером основных моментов, что-то может быть опущено. Многие статьи ориентированы на наличие определённого опыта у программиста. Поэтому данные статьи и не являются полным руководством к повторению.
Ошибка не изменилась.
Ну а шаблоны? которые html. Там тоже есть {% url 'users:blablabla' %}
ну ошибка такая же но только не users а messeages найти не может
А вы когда создавали приложение для комментариев, вы его urls подключили в urls.py файле всего проекта?
да, конечно, и в instaled_apps прописал как Messages.apps.MessagesConfig
и ещё, эта ссылка работает пока тебе или ты не напишешь сообщения
url(r'^dialogs/$', login_required(views.DialogsView.as_view()), name='dialogs'),
Внимательнее проанализируйте код из статьи и вывод ошибок, вы однозначно где-то накосячили. Вполне возможно, что вы не зарегистрировали пользовательский тег get_companion, Вы знаете как регистрировать пользовательские теги ?
Добрый день, очень интересные статьи у Вас на сайте, касательно Django и Python. Когда планируете создать часть 2 Чата. Евгений, это ведь не чат в режиме реального времени? каждый раз приходится обновлять страницу. Не планируете использовать для чата channels ?
Добрый день!
Всё планирую, а также планирую использовать channels, но пока не так много времени на внедрение всего функционала на сайте. Поэтому по срокам вообще ничего не могу сказать.
спасибо!
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.
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.
Привет! Пытаюсь освоить создание чата по вашей статье, сделал все как написано. Пока что результат такой (см. на прикрепленные скрины). Подскажите, пожалуйста, почему такой внешний вид и вдруг мб я чего не учел? Я так понял того, что вы выложили - недостаточно для полноценного чата и у меня получился тот максимум, котоырй можно выжать из вашего кода. Подскажите, как развить дальше, пожалуйста, ресурс какой-нибудь или из своего поделитесь! Спасибо большое за статью!
Добрый день.
Вам нужно самостоятельно написать стили, то есть CSS, это здесь уже на усмотрение пользователей, то есть на ваше личное усмотрение. Изучайте CSS и делайте как хотите вёрстку.
Добрый вечер! Монжно по подробней о теге get_companion? ссылка не работает.
Добрый день. Это кастомный тег, помещается в файл, который находится в каталоге templatetags
Добрый день. Подскажите пожалуйста как вы реализовали определение прочитано сообщение или нет.Спасибо
if request.user in chat.members.all():
chat.message_set.filter(is_readed=False).exclude(author=request.user).update(is_readed=True)
И можно подробнее эту строчку. Спасибо
А вот эта строчка как раз и отвечает за определение, прочитано сообщение или нет.
Данная строка фильтрует все непрочитанные сообщения, исключая сообщения, автором которых является текущий пользователь сессии. А потом делает все сообщения прочитанными. То есть если человек открывает диалог, то все входящие сообщения обновляются как прочитанные, а исходящие так и остаются непрочитанные до тех пор, пока другой участник диалога их не откроет диалог.
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.