python - 如何在 Django 2 中实现 Django-Private-Chat

标签 python django python-3.x websocket django-2.0

我正在开发一个项目,其中我需要为用户实现私有(private)聊天,以便一个用户可以与另一个用户私下通信,为此我正在使用 django-private-chat但我对如何在我的应用程序中实现这一点感到困惑?

这是我到目前为止所做的:

1): 安装websockets 2):安装django-private-chat 3): 在INSTALLED_APPS中添加 4): 然后在 main urls.py 中添加如下内容:

来自project.urls.py:

path('', include('jobexpertapp.urls')),
url(r'', include('django_private_chat.urls')),

来自myapp.urls.py:

re_path(r'^users/$', views.UserListView.as_view(), name='user_list'),

5):为此添加一个 View :

class UserListView(LoginRequiredMixin, generic.ListView):
    model = get_user_model()
    # These next two lines tell the view to index lookups by username
    slug_field = 'username'
    slug_url_kwarg = 'username'
    template_name = 'jobexpertapp/users.html'
    login_url = '/login'

6): 然后添加两个模板: (a): users.html (b): dialogs.html

来自users.html:

{% extends "base.html" %}
{% block content %}
    <h3>Here you can see users avaliable for chat, excluding yourself (chat with yourself is possible thought):</h3>
    <div class="container">
        {% for user in object_list %}
            {% if user != request.user %}
                <p>{{ user.get_full_name }}</p>
                <a href="{% url 'dialogs_detail' user.username %}" class="btn btn-primary">
                    Chat with {{ user.username }}
                </a>
            {% endif %}
        {% endfor %}
    </div>

{% endblock %}

来自dialogs.html:

{# This template is here just to demonstrate how to customize the default one. #}
{# It has none to very few changes #}
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block css %}
{% endblock css %}

{% block content %}
    <input id="owner_username" type="hidden" value="{{ request.user.username }}">
    <div class="container">
        <div class="col-md-3">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">{% trans "Chat list" %}</h3>
                </div>
                <div class="panel-body">
                    <div class="user-list-div">
                        <ul style="list-style-type: none;">
                            {% for dialog in object_list %}
                                <li>
                                    {% if dialog.owner == request.user %}
                                        {% with dialog.opponent.username as username %}
                                            <a href="{% url 'dialogs_detail' username %}" id="user-{{ username }}"
                                               class="btn btn-danger">{% trans "Chat with" %} {{ username }}</a>
                                        {% endwith %}
                                    {% else %}
                                        {% with dialog.owner.username as username %}
                                            <a href="{% url 'dialogs_detail' username %}" id="user-{{ username }}"
                                               class="btn btn-danger">{% trans "Chat with" %} {{ username }}</a>
                                        {% endwith %}
                                    {% endif %}
                                </li>
                            {% endfor %}
                        </ul>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-9">
            <div class="well well-lg">
                <div class="row">
                    <div class="col-md-3 col-md-offset-9">
                    <span class="pull-right" hidden id="typing-text">
                        <strong>{{ opponent_username }} {% trans "is typing..." %}</strong>
                    </span>
                    </div>
                    <p>
                        {{ opponent_username }}
                    </p>
                    <p class="text-success" id="online-status" style="display: none">{% trans "Online" %}</p>
                    <p class="text-danger" id="offline-status" style="display: none">{% trans "Offline" %}</p>

                    <div class="messages-container">
                        <div id="messages" class="messages">
                            {% for msg in active_dialog.messages.all %}
                                <div class="row  {% if msg.read %}msg-read{% else %}msg-unread{% endif %} {% if msg.sender != request.user %}opponent{% endif %}"
                                     data-id="{{ msg.id }}">
                                    <p class="{% if msg.sender == request.user %}pull-left{% else %}pull-right{% endif %}">
                                        <span class="username">{{ msg.sender.username }}:</span>
                                        {{ msg.text }}
                                        <span class="timestamp">&ndash; <span
                                                data-livestamp="{{ msg.get_formatted_create_datetime }}">{{ msg.get_formatted_create_datetime }}</span></span>
                                    </p>
                                </div>
                            {% endfor %}
                        </div>
                    </div>
                </div>

                <div class="row">
                    <div class="add-message">
                        <div class="form-group">
                        <textarea id="chat-message" class="form-control message"
                                  placeholder="{% trans 'Write a message' %}"></textarea>
                        </div>

                        <div class="form-group clearfix">
                            <input id="btn-send-message" type="submit" class="btn btn-primary pull-right send-message"
                                   style="margin-left: 10px;" value="{% trans 'Send' %}"/>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

{% block extra_js %}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/scrollmonitor/1.2.0/scrollMonitor.js"
            integrity="sha256-BseZlDlA+yL4qu+Voi82iFa5aaifralQEXIjOjaXgeo=" crossorigin="anonymous"></script>
    <script>
        var base_ws_server_path = "{{ ws_server_path }}";
        $(document).ready(function () {
            var websocket = null;
            var monitor = null;
            function initReadMessageHandler(containerMonitor, elem) {
                var id = $(elem).data('id');
                var elementWatcher = containerMonitor.create(elem);
                elementWatcher.enterViewport(function () {
                    var opponent_username = getOpponnentUsername();
                    var packet = JSON.stringify({
                        type: 'read_message',
                        session_key: '{{ request.session.session_key }}',
                        username: opponent_username,
                        message_id: id
                    });
                    $(elem).removeClass('msg-unread').addClass('msg-read');
                    websocket.send(packet);
                });
            }
            function initScrollMonitor() {
                var containerElement = $("#messages");
                var containerMonitor = scrollMonitor.createContainer(containerElement);
                $('.msg-unread').each(function (i, elem) {
                    if ($(elem).hasClass('opponent')){
                        initReadMessageHandler(containerMonitor, elem);
                    }
                });
                return containerMonitor
            }
            function getOpponnentUsername() {
                return "{{ opponent_username }}";
            }
            // TODO: Use for adding new dialog
            function addNewUser(packet) {
                $('#user-list').html('');
                packet.value.forEach(function (userInfo) {
                    if (userInfo.username == getUsername()) return;
                    var tmpl = Handlebars.compile($('#user-list-item-template').html());
                    $('#user-list').append(tmpl(userInfo))
                });
            }
            function addNewMessage(packet) {
                var msg_class = "";
                if (packet['sender_name'] == $("#owner_username").val()) {
                    msg_class = "pull-left";
                } else {
                    msg_class = "pull-right";
                }
                var msgElem =
                    $('<div class="row msg-unread" data-id="' + packet.message_id + '">' +
                        '<p class="' + msg_class + '">' +
                        '<span class="username">' + packet['sender_name'] + ': </span>' +
                        packet['message'] +
                        ' <span class="timestamp">&ndash; <span data-livestamp="' + packet['created'] + '"> ' + packet['created'] + '</span></span> ' +
                        '</p> ' +
                        '</div>');
                $('#messages').append(msgElem);
                scrollToLastMessage()
            }
            function scrollToLastMessage() {
                var $msgs = $('#messages');
                $msgs.animate({"scrollTop": $msgs.prop('scrollHeight')})
            }
            function generateMessage(context) {
                var tmpl = Handlebars.compile($('#chat-message-template').html());
                return tmpl({msg: context})
            }
            function setUserOnlineOffline(username, online) {
                var elem = $("#user-" + username);
                if (online) {
                    elem.attr("class", "btn btn-success");
                } else {
                    elem.attr("class", "btn btn-danger");
                }
            }
            function gone_online() {
                $("#offline-status").hide();
                $("#online-status").show();
            }
            function gone_offline() {
                $("#online-status").hide();
                $("#offline-status").show();
            }
            function flash_user_button(username) {
                var btn = $("#user-" + username);
                btn.fadeTo(700, 0.1, function () {
                    $(this).fadeTo(800, 1.0);
                });
            }
            function setupChatWebSocket() {
                var opponent_username = getOpponnentUsername();
                websocket = new WebSocket(base_ws_server_path + '{{ request.session.session_key }}/' + opponent_username);
                websocket.onopen = function (event) {
                    var opponent_username = getOpponnentUsername();
                    var onOnlineCheckPacket = JSON.stringify({
                        type: "check-online",
                        session_key: '{{ request.session.session_key }}',
                        username: opponent_username
                        {#                      Sending username because the user needs to know if his opponent is online #}
                    });
                    var onConnectPacket = JSON.stringify({
                        type: "online",
                        session_key: '{{ request.session.session_key }}'
                    });
                    console.log('connected, sending:', onConnectPacket);
                    websocket.send(onConnectPacket);
                    console.log('checking online opponents with:', onOnlineCheckPacket);
                    websocket.send(onOnlineCheckPacket);
                    monitor = initScrollMonitor();
                };
                window.onbeforeunload = function () {
                    var onClosePacket = JSON.stringify({
                        type: "offline",
                        session_key: '{{ request.session.session_key }}',
                        username: opponent_username,
                        {# Sending username because to let opponnent know that the user went offline #}
                    });
                    console.log('unloading, sending:', onClosePacket);
                    websocket.send(onClosePacket);
                    websocket.close();
                };
                websocket.onmessage = function (event) {
                    var packet;
                    try {
                        packet = JSON.parse(event.data);
                        console.log(packet)
                    } catch (e) {
                        console.log(e);
                    }
                    switch (packet.type) {
                        case "new-dialog":
                            // TODO: add new dialog to dialog_list
                            break;
                        case "user-not-found":
                            // TODO: dispay some kind of an error that the user is not found
                            break;
                        case "gone-online":
                            if (packet.usernames.indexOf(opponent_username) != -1) {
                                gone_online();
                            } else {
                                gone_offline();
                            }
                            for (var i = 0; i < packet.usernames.length; ++i) {
                                setUserOnlineOffline(packet.usernames[i], true);
                            }
                            break;
                        case "gone-offline":
                            if (packet.username == opponent_username) {
                                gone_offline();
                            }
                            setUserOnlineOffline(packet.username, false);
                            break;
                        case "new-message":
                            var username = packet['sender_name'];
                           if (username == opponent_username || username == $("#owner_username").val()){
                                addNewMessage(packet);
                                if (username == opponent_username) {
                                    initReadMessageHandler(monitor, $("div[data-id='" + packet['message_id'] + "']"));
                                }
                            } else {
                                if ($("#user-"+username).length == 0){
                                    var new_button = $(''+
                                        '<a href="/'+ username + '"' +
                                        'id="user-'+username+'" class="btn btn-danger">{% trans "Chat with" %} '+username+'</a>');
                                    $("#user-list-div").find("ul").append()
                                }
                                flash_user_button(username);
                            }
                            break;
                        case "opponent-typing":
                            var typing_elem = $('#typing-text');
                            if (!typing_elem.is(":visible")) {
                                typing_elem.fadeIn(500);
                            } else {
                                typing_elem.stop(true);
                                typing_elem.fadeIn(0);
                            }
                            typing_elem.fadeOut(3000);
                            break;
                        case "opponent-read-message":
                            if (packet['username'] == opponent_username) {
                                $("div[data-id='" + packet['message_id'] + "']").removeClass('msg-unread').addClass('msg-read');
                            }
                            break;
                        default:
                            console.log('error: ', event)
                    }
                }
            }
            function sendMessage(message) {
                var opponent_username = getOpponnentUsername();
                var newMessagePacket = JSON.stringify({
                    type: 'new-message',
                    session_key: '{{ request.session.session_key }}',
                    username: opponent_username,
                    message: message
                });
                websocket.send(newMessagePacket)
            }
            $('#chat-message').keypress(function (e) {
                if (e.which == 13 && this.value) {
                    sendMessage(this.value);
                    this.value = "";
                    return false
                } else {
                    var opponent_username = getOpponnentUsername();
                    var packet = JSON.stringify({
                        type: 'is-typing',
                        session_key: '{{ request.session.session_key }}',
                        username: opponent_username,
                        typing: true
                    });
                    websocket.send(packet);
                }
            });
            $('#btn-send-message').click(function (e) {
                var $chatInput = $('#chat-message');
                var msg = $chatInput.val();
                if (!msg) return;
                sendMessage($chatInput.val());
                $chatInput.val('')
            });
            setupChatWebSocket();
            scrollToLastMessage();
        });
    </script>
{% endblock %}

最佳答案

你把它弄得太复杂了,你不需要编写任何 View 或任何东西。在使用pip install django-private-chat<安装django-private-chat之后 并将其添加到已安装的应用程序中。 将以下行添加到您的设置 .py 文件

CHAT_WS_SERVER_HOST = 'localhost'
CHAT_WS_SERVER_PORT = 5002
CHAT_WS_SERVER_PROTOCOL = 'ws'

接下来您需要做的是将其所有 url 指向您的主项目的 urls.py。 它应该看起来像这样,URLS.py

from django_private_chat import urls as django_private_chat_urls

urlpatterns = [
#other urls of ur project
path('', include('django_private_chat.urls')),
]

下一步是前端部分,希望你有一个项目的静态目录,如果没有,请在与项目的manage.py文件相同的路径中创建一个静态文件夹。 现在从环境中复制 django-private-chat 静态文件夹的内容。它的路径为 your_venv_name/lib/pythonX.X/site-packages/django_private_chat/static。复制此静态文件夹的所有内容到您项目的静态文件夹。它应该看起来像这样 your_project/static/django_private_chat/static

接下来是您的 dailog.html 页面。如果您的项目中还没有创建 templates 文件夹,请创建一个。然后在该 templates 文件夹中创建一个“base.html”文件,base.html 文件的内容将如下所示: 基本.html

 <html><head></head><body>
    {% block content %}

    {% endblock %}
    {%block js%}
    {%endblock%}
    {% block extra_js %}{% endblock extra_js %}

</body>

接下来在模板文件夹中,创建 dailogs.html 文件,复制您在问题中在 dailogs.html 中编写的内容,只需将其复制到 {%block css%}

{{ block.super }}
<link href="{% static 'django_private_chat/css/django_private_chat.css' %}" rel="stylesheet" type="text/css" media="all">

现在在一个终端窗口 runserver 和另一个终端窗口中运行运行服务器的同一目录。执行

python manage.py run_chat_server

转到浏览器并登录到您的 django 项目。然后转到 http://127.0.0.1:8000/dialogs/

您将找到与之聊天的用户。

关于python - 如何在 Django 2 中实现 Django-Private-Chat,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54481329/

相关文章:

python - windows7 安装psycopg2 报错: Unable to find vcvarsall. bat

python/django - html 选择列表中的 bidi 括号问题

javascript - Django/jQuery 在管理中级联选择框

python - pandas向量化运算获取字符串长度

python - Python 的 stdlib 测试在哪里?

python-3.x - 相对路径在 Jupyter 笔记本中的 Pandas python 中不起作用

python - 定义一个函数,该函数接受项目列表并返回以逗号分隔的项目名称列表

python-3.x - 检查列表中的项目是否在列表类型的列中可用

python - 如何使用 Django 模型创建组合框?

python - MYSQL 语句中的动态表名 (Google Cloud SQL)