基于Python实现从头搭建一个在线聊天室框架
作者:萝卜大杂烩
今天从头开始做一个在线聊天网站,网上各种各样的聊天工具已经很多了,为啥还要做这么一个聊天工具呢,无他,兴趣耳!
今天先完成第一部分,搭建起聊天网站的整体框架。
整体技术栈
- flask 框架
- flask_login 的使用
- jquery 简单应用
搭建权限框架
还是使用 Flask 来搭建后台应用,使用 flask-login 扩展来处理用户登陆鉴权逻辑。
首先定义登陆表单
class LoginForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), ]) password = PasswordField('Password', validators=[DataRequired()]) remember_me = BooleanField('Keep me logged in') submit = SubmitField('Log in')
一个简单的登陆表单,不做过多解释
接下来定义数据库结构
class User(UserMixin, db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True, index=True) password = db.Column(db.String(64))
当前,我们只需要一个 user 用户表,只包含三个字段的简单表。用户密码也只是简单的保存了明文,后面再处理用户密码的 hash 问题。
下面就可以定义用户登陆表单
from flask_login import LoginManager login_manager = LoginManager() login_manager.session_protection = 'strong' login_manager.login_view = 'login' app = Flask(__name__) login_manager.init_app(app) app.config['SECRET_KEY'] = 'hardtohard' @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) @app.route('/login', methods=['GET', 'POST']) def login(): form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(username=form.username.data).first() if user: login_user(user) return redirect(url_for('chat')) return render_template('login.html', form=form)
这里定义了,只检查用户名是否存在,如果存在,就执行 login_user() 函数,登陆。用户密码的使用,也留到后面再做处理。
其中 load_user,是回调函数,将获取到的 user 对象存储到浏览器的 session 中,然后在调用 login_user 函数时,就会调用 load_user 来把真正需要登陆的用户设置到 session 中。当登陆成功后,就会跳转到 chat 函数所对应的页面。
chat 函数比较简单,只是展示一个网页
@app.route('/chat', methods=['GET', 'POST']) @login_required def chat(): return render_template('chat.html')
使用 login_required 装饰器,保证该函数只允许登陆的用户访问。
增加些初始化函数
@app.route('/adddb', methods=['GET', 'POST']) def addbd(): db.create_all() return "OK" @app.route('/deldb', methods=['GET', 'POST']) def delbd(): db.drop_all() return "OK" @app.route('/adduser/<user>', methods=['GET', 'POST']) def adduser(user): user = User(username=user, password='admin') db.session.add(user) db.session.commit() return "OK"
增加了初始化数据库和新增用户的函数。
构建前端页面
首先处理登陆页面,在 login.html 中添加
{% extends "bootstrap/base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Flasky{% endblock %} {% block navbar %} <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/" rel="external nofollow" rel="external nofollow" >Flasky</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="/" rel="external nofollow" rel="external nofollow" >Home</a></li> </ul> <ul class="nav navbar-nav navbar-right"> {% if current_user.is_authenticated %} <li><a href="{{ url_for('logout') }}" rel="external nofollow" rel="external nofollow" >Logout</a></li> {% else %} <li><a href="{{ url_for('login') }}" rel="external nofollow" rel="external nofollow" >Login</a></li> {% endif %} </ul> </div> </div> </div> {% endblock %} {% block content %} <div class="container"> <div class="page-header"> <h1>Hello, Welcome!</h1> </div> {{ wtf.quick_form(form) }} </div> {% endblock %}
使用扩展库 flask_bootstrap 来快速构建页面。
下面重点来看看 chat 页面,主要使用了 Ajax 来处理文字交互。
首先来看看主体页面,在 chat.html 中填入代码
{% extends 'bootstrap/base.html' %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Kung Fu Realm{%endblock %} {% block head %} <head> <meta charset="utf-8"> <title>Hi Hi 聊天室</title> <link rel="shortcut icon" href="{{ url_for('static',filename='chat/images/hihi.jpg')}}" rel="external nofollow" rel="external nofollow" > <link rel="icon" href="{{ url_for('static',filename='chat/images/hihi.jpg')}}" rel="external nofollow" rel="external nofollow" type="image/x-icon"> <link type="text/css" rel="stylesheet" href="/static/chat/css/style.css" rel="external nofollow" > <script type="text/javascript" src="{{ url_for('static', filename='chat/js/jquery.min.js') }}"></script> </head> {% endblock %} {% block content %} <body> <div class="chatbox"> <div class="chat_top fn-clear"> <div class="uinfo fn-clear" style="float: left;"><div class="uface"><h1 style="color: #7777">ROOM: 聊天室123哈哈哈</h1></div></div> <div class="uinfo fn-clear"> {% if current_user.is_authenticated %} <div class="uface"><img src="{{ url_for('static', filename='chat/images/hi.jpg') }}" width="40" height="40" alt=""/></div> {% else %} <div class="uface"><img src="{{ url_for('static', filename='chat/images/hi.jpg')}}" width="40" height="40" alt=""/></div> {% endif %} <div class="uname"> 小HI<i class="fontico down"></i> <ul class="managerbox"> {% if current_user.is_authenticated %} <li><a href="{{ url_for('login') }}" rel="external nofollow" rel="external nofollow" ><i class="fontico lock"></i>退出登陆</a></li> {% else %} <li><a href="{{ url_for('logout') }}" rel="external nofollow" rel="external nofollow" ><i class="fontico logout"></i>登录</a></li> {% endif %} </ul> </div> </div> </div> <div class="chat_message fn-clear"> <div class="chat_left"> <div class="message_box" id="message_box"> <div class="msg_item fn-clear"> <div class="uface"><img src="{{ url_for('static', filename='chat/images/duck.jpg')}}" width="40" height="40" alt=""/></div> <div class="item_right"> <div class="msg own"><img src="{{ url_for('static', filename='chat/images/hihi.jpg')}}" width="400" height="400" alt=""/></div> <div class="name_time">小黄鸭 </div> </div> </div> {% if current_user.is_authenticated %} <div class="msg_item fn-clear"> <div class="uface"><img src="{{ url_for('static', filename='chat/images/duck.jpg')}}" width="40" height="40" alt=""/></div> <div class="item_right"> <div class="msg">Welcome to Hihi Chat Room. 欢迎来到 Hihi 聊天室。 </div> <div class="name_time">小黄鸭 </div> </div> </div> {% else %} <div class="msg_item fn-clear"> <div class="uface"><img src="{{ url_for('static', filename='chat/images/duck.jpg')}}" width="40" height="40" alt=""/></div> <div class="item_right"> <div class="msg">您还没有登陆,先和小黄鸭聊聊吧。 </div> <div class="name_time">小黄鸭 </div> </div> </div> {% endif %} </div> <div class="write_box"> {% if current_user.is_authenticated %} <textarea id="message" name="message" class="write_area" placeholder="说点啥吧..."></textarea> {% else %} <textarea id="message_not" name="message" class="write_area" placeholder="说点啥吧..."></textarea> {% endif %} <input type="hidden" name="fromname" id="fromname" value="你" /> <input type="hidden" name="to_uid" id="to_uid" value="0"> <div class="facebox fn-clear"> <div class="expression"></div> <div class="chat_type" id="chat_type">群聊</div> {% if current_user.is_authenticated %} <button name="login" class="sub_but" id="sub_but_login">提 交</button> {% else %} <button name="logout" class="sub_but" id="sub_but">提 交</button> {% endif %} </div> </div> </div> </div> </div>
整体效果如下,是不是挺少女系的。
当用户在点击“提交”按钮后,调用 JS 函数
/*用户登陆的用户点击提交按钮发送消息按钮*/ $('#sub_but_login').click(function(event){ sendMessageLogin(event, fromname, to_uid, to_uname); });
为了后面便于扩展,将未登录的用户特别区分开来,后面也许同样允许未登陆用户访问该页面,但是只能同机器人小黄鸭聊天
/*用户未登陆的用户点击提交按钮发送消息按钮*/ $('#sub_but').click(function(event){ sendMessage(event, fromname, to_uid, to_uname); });
再来看函数 sendMessageLogin
function sendMessageLogin(event, from_name, to_uid, to_uname){ var msg = $("#message").val(); var myDate = new Date(); var myTime = myDate.toLocaleTimeString(); var itTime = myDate.toLocaleString(); //var iTime = myDate.toDateString(); var htmlData = '<div class="msg_item fn-clear">' + ' <div class="uface">{% if current_user.is_authenticated %}<img src="{{ url_for('static', filename='chat/images/hi.jpg') }}" width="40" height="40" alt=""/>{% endif %}</div>' + ' <div class="item_right">' + ' <div class="msg own">' + msg + '</div>' + ' <div class="name_time">' + from_name + ' · ' + itTime +'</div>' + ' </div>' + '</div>'; $("#message_box").append(htmlData); $('#message_box').scrollTop($("#message_box")[0].scrollHeight + 20); $("#message").val(''); setTimeout(function(){sendToServer(from_name, msg)}, 1000); //延时调用 }
接收几个参数,然后将当前会话消息追加到 HTML 页面中,并且调用真正的后台 API 函数 sendToServer
function sendToServer(name, msg){ var xmlhttp = new XMLHttpRequest(); var myDate = new Date(); //var myTime = myDate.toLocaleTimeString(); var myTime = myDate.toLocaleString(); xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { myObj = xmlhttp.responseText; var htmlData2 = '<div class="msg_item fn-clear">' + ' <div class="uface"><img src="{{ url_for('static', filename='chat/images/duck.jpg')}}" width="40" height="40" alt=""/></div>' + ' <div class="item_right">' + ' <div class="msg">' + myObj + '</div>' + ' <div class="name_time">' + '小黄鸭' + ' · ' + myTime +'</div>' + ' </div>' + '</div>'; $("#message_box").append(htmlData2); $('#message_box').scrollTop($("#message_box")[0].scrollHeight + 20); } } xmlhttp.open("GET", "/api/sendchat/" + msg, true); xmlhttp.send(); };
sendToServer 函数调用后台 API,并把接收到的消息回写到 HTML 页面中。
而目前的后台 API 也比较简单,直接返回用户输入的消息
@app.route('/api/sendchat/<info>', methods=['GET', 'POST']) @login_required def send_chat(info): return info
这样,一个整体的聊天室架子就搭建好了,后面我们再接入 redis 和自己训练的聊天机器人,来实现真正的在线聊天室。
到此这篇关于基于Python实现从头搭建一个在线聊天室的文章就介绍到这了,更多相关Python在线聊天室内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!