zoukankan      html  css  js  c++  java
  • web聊天室

    开发一个web聊天室

    功能需求:

    1、用户可以与好友一对一聊天

    2、群聊

    所需知识

    1、Django

    2、bootstrap

    3、CSS

    4、ajax

    涉及到的新的知识点

    1、如果设计表结构的时候,一张表中有一个以上的字段关联另外一张相同的表(外键),那么直接关联会出错,合适的方法是使用related_name指定一个名字就可以解决,如下members和admins.

    class QQgroup(models.Model):
        name =models.CharField(max_length=64,unique=True)
        members=models.ManyToManyField(UserProfile,blank=True)  #null =True无效
        admins=models.ManyToManyField(UserProfile,related_name='group_admins')   #在model中存在同时存在两个字段关联一张表,这样的会出错,需要使用related_name将名字修改一下来解决
        max_member_nums=models.IntegerField(default=200)

    2、在views或者js代码中,如果需要调用不同的函数,指向函数的url可以通过name名直接调用函数,而无需写url的真实路径,多个name可以同时指向同一个url,如下new_msg,另外,url建议使用名词,养成良好的编码习惯。

    urlpatterns = patterns('',
    
        url(r'dashboard/$', views.dashboard,name='web_chat'),
        url(r'contacts/$', views.contacts,name='load_contact_list'),    #url全部使用名词,符合规范
        url(r'msg/$', views.new_msg,name='send_msg'),    #url全部使用名词,符合规范
        url(r'msg/$', views.new_msg,name='get_new_msgs'),
                           )

    3、使用外键关联表自己的时候,不管是ForeignKey还是ManyToManyField,都需要related_name,如下friends字段:

    class UserProfile(models.Model):
        '''账户信息表'''
        user=models.OneToOneField(User)   #继承自带的User表,但是原生的user表中的字段较少,可以继承之后可以扩展字段;只能使用onetoone,否则就会使得多个用户同时关联一个账户onetoone是在代码层面进行限制的,其实就是讲两张表进行拼接了
        name = models.CharField(max_length=32)
        groups=models.ManyToManyField('UserGroup')
        friends=models.ManyToManyField('self',related_name='my_friends')
        def __unicode__(self):
            return self.name
    contacts=request.user.userprofile.friends.select_related().values('id','name')     #通过一个字段查看多对多,select_related查看所有的朋友,values表示需要查看的字段,为列表形式,元素为字典

    4、如果开启了csrf验证功能,那么在Django form提交的时候,在模本中增加{% csrf_token %}即可,如果是ajax提交的数据,有两种方式让提交的数据添加csrf token;

    a、在html模板中添加{% csrf_token %},将其埋在页面中,默认是hide的,通过页面元素审查,找到对应的input下的name为csrfmiddlewaretoken,将其value值通过函数在POST提交的时候,添加到提交的数据中,但是这种方式,每次POST数据都需要获取token并添加提交,不方便;

        function GetCsrfToken(){
            return $("input[name='csrfmiddlewaretoken']").val();   //获取csrftoken的值
            //每次post提交数据都需要将csrf token的值获取出来进行post提交,有点low
        }
    
    
     function SendMsg(msg_text){   //通过ajax 将数据进行提交
            var contact_id = $(".chat-header span").attr("contact_id");
            var contact_type = $(".chat-header span").attr("contact_type");
            var msg_dic={
                'contact_type':contact_type,
                'to':contact_id,
                'from':"{{ user.userprofile.id }}",
                'from_name':"{{ user.userprofile.name }}",
                'msg':msg_text
            };
            console.log("{{ user.userprofile.id }}");
            console.log("{{ user.userprofile.name }}");
            //$.post("{% url 'send_msg' %}",{'data':JSON.stringify(msg_dic),'csrfmiddlewaretoken':GetCsrfToken()},function(callback){    //csrfmiddlewaretoken提交的时候讲csrf的值一起提交
            $.post("{% url 'send_msg' %}",{'data':JSON.stringify(msg_dic)},function(callback){    //使用插件,csrfmiddlewaretoken提交的时候讲csrf的值一起提交
                 console.log(callback);     //函数的返回值为callback,采用回调函数
            });    //end post  JSON.stringify(msg_dic)将字典转换为json格式
        }

    b、使用插件提交

       //csrf ref
        //获取csrf token并添加到每一次post的数据中
        function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    var csrftoken = getCookie('csrftoken');
    
    
        function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });
        // end csrf ref

    5、正向或者反向获取数据库外键关联表的字段值

        contacts=request.user.userprofile.friends.select_related().values('id','name')     #通过一个字段查看多对多,select_related(外键关联的所有实体)查看所有的朋友,values表示需要查看的字段,为列表形式,元素为字典
        print contacts
        contact_dic['contact_list']=list(contacts)    #显示为列表,其实还是django的对象,需要强制转换为列表
        groups=request.user.userprofile.qqgroup_set.select_related().values('id','name','max_member_nums')    #1、获取群组,qqgroup_set这种方法适用于自己没有和别的表关联,但是别的表和自己关联了
                                                                        #2、如果不使用上述方法,就需要从QQgroup表的member中过滤出包含自己名字的组名,添加
                                                                        #a=models.QQgroup.object.all(); for i in a:print i.members.select_related()

    聊天室几种实现方式:

    http请求是短链接、无状态

    客户端cookie中保存的是session id,每次,对于同义词session请求,客户端会携带session id与服务器进行交互,这样对于无状态的http请求,服务端就会知道这次请求与上次请求的关系;

    客户端连接上服务器之后,会阻塞,如果客户端有新消息发送过来的时候,就会唤醒,属于一种长轮询方式;

    另外一种解决方式是WebSocket(长连接),通过浏览器(支持h5的浏览器)和服务器(支持长连接)端建立socket来快速实现;Django不支持长连接;

    表结构设计:

    class UserProfile(models.Model):
        '''账户信息表'''
        user=models.OneToOneField(User)   #继承自带的User表,但是原生的user表中的字段较少,可以继承之后可以扩展字段;只能使用onetoone,否则就会使得多个用户同时关联一个账户onetoone是在代码层面进行限制的,其实就是讲两张表进行拼接了
        name = models.CharField(max_length=32)
        groups=models.ManyToManyField('UserGroup')
        friends=models.ManyToManyField('self',related_name='my_friends')  #每一个用户都有自己的多个好友,好友也可以有多个好友;
        def __unicode__(self):
            return self.name

    webchatmodel.py

    from django.db import models
    from web.models import UserProfile
    # Create your models here.
    
    class QQgroup(models.Model):
        name =models.CharField(max_length=64,unique=True)
        members=models.ManyToManyField(UserProfile,blank=True)  #null =True无效
        admins=models.ManyToManyField(UserProfile,related_name='group_admins')   #在model中存在同时存在两个字段关联一张表,这样的会出错,需要使用related_name将名字修改一下来解决
        max_member_nums=models.IntegerField(default=200)

    表结构创建完成之后,利用admin进行创建数据 

    WEB聊天室页面布局

    将聊天的url分发到自己的应用当中

    from django.conf.urls import patterns, include, url
    from django.contrib import admin
    from web import views
    from webchat import urls as chat_urls
    
    urlpatterns = patterns('',
        # Examples:
        # url(r'^$', 'js.views.home', name='home'),
        # url(r'^blog/', include('blog.urls')),
    
        url(r'^admin/', include(admin.site.urls)),
        url(r'^chat/', include(chat_urls)),

    webchaturls.py

    from django.conf.urls import patterns, include, url
    from django.contrib import admin
    import views
    
    urlpatterns = patterns('',
    
        url(r'dashboard/', views.dashboard,name='web_chat'),
                           )

    webchatviews.py

    from django.shortcuts import render
    def dashboard(request):
        return render(request,'web_chat/dashboard.html')

    dashboard.html:整个包含在一个大的div中,分为左右两个小的div(加row属性),占3/12,右边占9/12,右边的div分为3部分,顶部chat-header,中部chat-content,和底部chat-msg-sendbox(分为左右两部分);

    {% extends 'index.html' %}
    
    {% block page-container %}
    <h1>撩妹专区.....</h1>
    <div class="chat-container row">
        <div class="contact-list col-md-3">
            contact list
        </div>
        <div class="chat-box col-md-9">
            chat box
            <div class="chat-header"> talking with ...now</div>
            <div class="chat-content">content</div>
            <div class="chat-msg-sendbox row">
                <div class="msg-box col-md-10">
                    <textarea></textarea>
                </div>
                <div class="msg-box-tn col-md-2">
                    <button type="button" class="btn btn-success">发送</button>
                </div>
            </div>
        </div>
    
    </div>
    {% endblock %}
    {% block bottom-js %}
        <script>
            $(document).delegate("textarea","keydown", function (e) {
                if(e.which==13){
                    var msg_text=$("textarea").val();
                    if($.trim(msg_text).length>0){
                        //SendMsg(msg_text);
                        console.log(msg_text);
                        //AddSendMsgIntoBox(msg_text);
                        //$("textarea").val('');
    
                    }
                }
            })
        </script>
    {% endblock %}
  • 相关阅读:
    handsontable-developer guide-data binding,data sources
    Android版Web服务器实现(三)HTTP响应
    Android版Web服务器实现(二)使用服务来监听HTTP请求
    HTTP协议详解(很详细)
    Android版Web服务器实现(一)HTTP协议请求头解析
    HTTP协议中Content-Length的详细解读。
    Android基于SwiFTP开源库的FTP实现(FTP匿名登录)
    Android之查看Wifi密码
    道德经与抽象、接口及框架
    JAVA中利用JNI与VS2012实现C/C++的DLL调用
  • 原文地址:https://www.cnblogs.com/cqq-20151202/p/5689808.html
Copyright © 2011-2022 走看看