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

Posted

技术标签:

【中文标题】如何在 Django 2 中实现 Django-Private-Chat【英文标题】:how to implement Django-Private-Chat in Django 2 【发布时间】:2019-06-26 03:19:46 【问题描述】:

我正在做一个项目,我需要为用户实现私人聊天,因此一个用户可以与另一个用户私下交流,为此我使用django-private-chat,但我对如何实现感到困惑这在我的应用程序中?

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

1): 安装websockets 2): 安装django-private-chat 3):添加到INSTALLED_APPS 4):然后在main中添加urls.pylike:

来自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):为此添加一个视图:

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 %

【问题讨论】:

【参考方案1】:

你让它太复杂了,你不需要写任何视图或任何东西。在你使用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 页面。如果您的项目中还没有创建模板文件夹,请创建一个。然后在该模板文件夹中创建一个“base.html”文件,base.html 文件的内容如下所示: base.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/

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

【讨论】:

我已按照这些步骤操作,但无法正常工作。我在控制台中看到了这个错误。 File "/Users/raakesh/Python/venvironment/lib/python3.7/site-packages/websockets/compatibility.py", line 8 asyncio_ensure_future = asyncio.async # Python &lt; 3.5 已修复。这是 Python 版本的问题。我使用的是 3.7,因此我不得不将 websockets 库更新为 7.0 而不是 od 3.2,这是该聊天库所必需的 @RaakeshKumar 如果对您有帮助,请为答案投票 为什么要导入:from django_private_chat import urls as django_private_chat_urls?因为你不使用django_private_chat_urls 你是怎么解决这个问题的?我无法让它工作

以上是关于如何在 Django 2 中实现 Django-Private-Chat的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 中实现追随者/追随者

你将如何在 django 中实现聊天?

如何在 Django 模板中实现面包屑?

如何在django模板中实现列表迭代[重复]

你如何在 django rest 框架中实现 CSRF 令牌?

如何在sqlite django ORM中实现have子句