Evgenii Legotckoi
Evgenii LegotckoiЖел. 30, 2017, 10:50 Т.Ж.

Джанго - Сабақ 029. Сайтқа жеке хабарламалар мен чаттар қосу - 1 бөлім

Қалыптасқан дәстүр бойынша мен сізге сайтқа жаңа функцияларды енгізудегі тәжірибем туралы айтып беремін. Қазіргі уақытта бұл функция пайдаланушылар арасындағы жеке хабарламалар болып табылады. Әрине, ол қазір белгілі әлеуметтік желілердегідей жақсы жұмыс істемейді... бірақ соңында ол жұмыс істейді. Ең бастысы форум пікірі өтінемін.

Сонымен. Мен сайтқа жеке хабарламаларды қосқым келді, әсіресе мен бұл туралы алты ай бұрын айтқан болатынмын. Мұны қалай жүзеге асыруға болады деген сұрақ қалады. Интернетте іздеу кезінде мен келесі деректер моделі қалыптасқан кезде опцияға тап болдым.

  • Идентификатор хабарлары
  • пайдаланушыдан - жіберуші
  • пайдаланушыға - алушы
  • жарияланатын_күн - жазбаның күні
  • хабарлама - хабарлама мазмұны

Мен бұл опцияны іске асыруға тырыстым, бірақ жеке хабарламалардан кейін кенеттен чат құрғым келетіні мені тоқтатты? Ендеше неге бірден чаттардың негізін салмасқа?


Чат және хабар үлгілері

Бұл ресурсты одан әрі дамытудың тамаша нұсқасы болар еді. Бірақ бұл жағдайда Чат және Хабар екі үлгісін жасау керек.

# -*- 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

Хабар үлгісінде бәрі түсінікті болуы керек. Хабарламаның мәтіні, күйі, оқылған-оқылмағаны, хабарламаның авторы және ол жіберілген чат, сондай-ақ хабарлама жіберілген күні бар.

Сондықтан Чат үлгісі біршама күрделірек болады. Біріншіден, чаттар әртүрлі рух болуы мүмкін. Біріншісі – екі адамның жеке әңгімесі. Екінші түрі – топтық чат. Бұл не үшін жасалды? Бұл пайдаланушыға жеке парақшасынан хабарлама жіберу кезінде қажетті чатты іздеуді жеңілдету үшін қажет болды. Өкінішке орай, қазіргі уақытта пайдаланушыға бірінші хабарламаны жіберудің тек осындай жолы бар. Яғни, оның парақшасына өтіп, хабарлама жазу үшін түймені басыңыз. Болашақта пайдаланушыны жеке парақшаңыздағы диалогтық беттен табуға болады. Бұл мен өзім жазған бірінші Пайдаланушы тарихы ішіндегі әлі де уақыт шектеулері.

Әрбір чатта көптен көпке сілтемесі бар, ол чаттағы қатысушылар тізіміне жауап береді. Осының арқасында сіз чаттарды қарауды, сондай-ақ пайдаланушы осы чатқа шақырылмаған жағдайда чаттарға жазу мүмкіндігін шектей аласыз.

urls.py

Бізге қандай көріністер қажет және қандай бағыттарды жасай аламыз? Бұл функцияны қарапайым түрде енгізу үшін маған urls.py. ішінде үш көріністі және сәйкесінше үш маршрутты жазу керек болды.

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'),

Біз бұл маршруттарды қандай қолданбаны қолданатынымызды қарастырмаймыз - бұл маңызды емес.

Шындығында, мұнда үш көрініс бар, біреуі пайдаланушының диалогтар тізіміне арналған, екіншісі басқа пайдаланушының бетінен диалог құру үшін және диалогтағы хабарламалар үшін үшінші.

Хабарлама пішіні

Бұл жағдайда үлгіге арналған пішінді пайдалануға болады. Біз үлгіні, сондай-ақ енгізу өрісінен жапсырма жойылған өрістерді көрсету керек екенін көрсетеміз.

Әдепкі бойынша сізде тұрақты textarea болады. Мен өзімнің WYSIWYG өңдегішін қолданамын.

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

Көріністер мен үлгілер

Диалогтар тізімін көрсету

Пайдаланушы қатысатын барлық диалогтардың тізімін алу үшін барлық чаттарды қатысушылар бойынша сүзгілеу қажет, яғни «Көптен көпке» өрісі мүшелер. .

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})

Бұл жағдайда менде авторизацияланған пайдаланушы мен чаттар тізімін беретін диалогтар үлгісі бар. Диалог тізімінде оқылған және оқылмаған хабарламаларды дұрыс көрсету үшін белсенді пайдаланушы қажет болады.

Диалогтардың тізімі және оның орналасуы үлкен қызығушылық тудырады, сондықтан мұнда үлгінің орналасуға жауап беретін бөлігі ғана көрсетіледі.

<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>

Үлгіде менің реттелетін ендіру тегімді көре аласыз. Бұл тег диалогтар тізіміне барабар орналасуды қалыптастыру және әңгімелесуші туралы ақпарат негізінде диалогтар тізімінде оқылған және оқылмаған хабарламаларды көрсету үшін сұхбаттасушыны диалогқа қайтаруға жауап береді.

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

Сонымен, диалогтар тізімі келесідей болады:

Ағымдағы диалог және хабарлар

Ағымдағы диалог пен хабарларды көрсету үшін күрделірек логика қажет. Бұл жерде чатқа кіру ID арқылы жүзеге асырылады, бірақ диалогты алуға әрекет жасау ғана емес, сонымен қатар қатысушылар тізімінде кіруге тырысатын пайдаланушының бар-жоғын тексеру қажет. бұл чат. Егер ол қатысушылар тізімінде болмаса, оған осы чатқа кіруге тыйым салынады. Басқа нәрселермен қатар, сол көрініс хабарламаларды жіберуді және хабарларды оқылған деп белгілеуді өңдейді.

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}))

Хабарламалар тізімі үлгісі

{% 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 %}

Хабарламаның өзіне арналған үлгі

{% 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>

Қорытынды диалогтың мысалы

Пайдаланушымен сөйлесуді бастаңыз

Қазіргі уақытта пайдаланушымен сөйлесуді бастаудың бір ғана әдісі енгізілген. Сіз пайдаланушының бетіне өтіп, «Хабар жазу» түймесін басуыңыз керек, содан кейін чат құрылатын сілтеме арқылы сұрау жіберіледі немесе осы пайдаланушымен бар чат табылады. Бұл жерде чат диалог немесе бірнеше пайдаланушының әңгімесі екенін тексеру үшін қолданылады. Бұл қажетті диалогты іздеуді біршама жеңілдетуге мүмкіндік береді.

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}))

Пайдаланушылар санын тексеріңіз, өйткені жеке сөйлесуде тек екі пайдаланушы болуы мүмкін және біз бұл пайдаланушылар біздің авторизацияланған пайдаланушыларымыз екенін, сондай-ақ біз сөйлесуді бастағалы отырған пайдаланушы екенін тексереміз.

Чат жасалғаннан кейін біз хабарлар бетіне қайта бағыттаймыз.

Django үшін Timeweb хостының VDS-сервері ұсынамын.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

M
  • Наурыз 26, 2018, 7: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, 2018, 7:44 Т.Ж.

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

apps.py

from django.apps import AppConfig
 
 
class UsersConfig(AppConfig):
    name = 'users'
settings.py
INSTALLED_APPS = [
    'users.apps.UsersConfig',
]
M
  • Наурыз 26, 2018, 8:22 Т.Ж.

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

from django.apps import AppConfig


class MessagesConfig(AppConfig):
    name = 'Messages'
у вас есть отдельно статья по созданию 'user.apps.UsersConfig'?
Evgenii Legotckoi
  • Наурыз 26, 2018, 8: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, 2018, 9:13 Т.Ж.

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

Evgenii Legotckoi
  • Наурыз 26, 2018, 9:20 Т.Ж.

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

M
  • Наурыз 26, 2018, 10:20 Т.Ж.

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

Evgenii Legotckoi
  • Наурыз 26, 2018, 10:26 Т.Ж.

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

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

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

M
  • Наурыз 27, 2018, 11:56 Т.Ж.

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

Evgenii Legotckoi
  • Наурыз 27, 2018, 12:18 Т.Қ.

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

y
  • Сәуір 1, 2018, 2:23 Т.Қ.

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

Evgenii Legotckoi
  • Сәуір 1, 2018, 2:58 Т.Қ.

Добрый день!

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

спасибо!

l
  • Қаң. 10, 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, 2019, 2: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, 2019, 4 Т.Қ.

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

Evgenii Legotckoi
  • Қар. 20, 2019, 2:52 Т.Ж.

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

B
  • Ақп. 16, 2020, 1:36 Т.Қ.

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

Evgenii Legotckoi
  • Ақп. 17, 2020, 3:22 Т.Ж.

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

  • myapp/
    • templatetags/
      • myapp.py
N
  • Мамыр 11, 2020, 7:01 Т.Ж.

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

N
  • Мамыр 11, 2020, 7: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, 2020, 10:44 Т.Ж.

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

p
  • Ақп. 5, 2021, 1:20 Т.Ж.
  • (өңделген)

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.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз