基于Python實(shí)現(xiàn)從頭搭建一個(gè)在線(xiàn)聊天室框架
今天從頭開(kāi)始做一個(gè)在線(xiàn)聊天網(wǎng)站,網(wǎng)上各種各樣的聊天工具已經(jīng)很多了,為啥還要做這么一個(gè)聊天工具呢,無(wú)他,興趣耳!
今天先完成第一部分,搭建起聊天網(wǎng)站的整體框架。
整體技術(shù)棧
- flask 框架
- flask_login 的使用
- jquery 簡(jiǎn)單應(yīng)用
搭建權(quán)限框架
還是使用 Flask 來(lái)搭建后臺(tái)應(yīng)用,使用 flask-login 擴(kuò)展來(lái)處理用戶(hù)登陸鑒權(quán)邏輯。
首先定義登陸表單
class?LoginForm(FlaskForm):
????username?=?StringField('Username',?validators=[DataRequired(),?])
????password?=?PasswordField('Password',?validators=[DataRequired()])
????remember_me?=?BooleanField('Keep?me?logged?in')
????submit?=?SubmitField('Log?in')
一個(gè)簡(jiǎn)單的登陸表單,不做過(guò)多解釋
接下來(lái)定義數(shù)據(jù)庫(kù)結(jié)構(gòu)
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))
當(dāng)前,我們只需要一個(gè) user 用戶(hù)表,只包含三個(gè)字段的簡(jiǎn)單表。用戶(hù)密碼也只是簡(jiǎn)單的保存了明文,后面再處理用戶(hù)密碼的 hash 問(wèn)題。
下面就可以定義用戶(hù)登陸表單
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)
這里定義了,只檢查用戶(hù)名是否存在,如果存在,就執(zhí)行 login_user() 函數(shù),登陸。用戶(hù)密碼的使用,也留到后面再做處理。
其中 load_user,是回調(diào)函數(shù),將獲取到的 user 對(duì)象存儲(chǔ)到瀏覽器的 session 中,然后在調(diào)用 login_user 函數(shù)時(shí),就會(huì)調(diào)用 load_user 來(lái)把真正需要登陸的用戶(hù)設(shè)置到 session 中。當(dāng)?shù)顷懗晒?,就?huì)跳轉(zhuǎn)到 chat 函數(shù)所對(duì)應(yīng)的頁(yè)面。
chat 函數(shù)比較簡(jiǎn)單,只是展示一個(gè)網(wǎng)頁(yè)
@app.route('/chat',?methods=['GET',?'POST'])
@login_required
def?chat():
????return?render_template('chat.html')
使用 login_required 裝飾器,保證該函數(shù)只允許登陸的用戶(hù)訪(fǎng)問(wèn)。
增加些初始化函數(shù)
@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"
增加了初始化數(shù)據(jù)庫(kù)和新增用戶(hù)的函數(shù)。
構(gòu)建前端頁(yè)面
首先處理登陸頁(yè)面,在 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?%}
使用擴(kuò)展庫(kù) flask_bootstrap 來(lái)快速構(gòu)建頁(yè)面。
下面重點(diǎn)來(lái)看看 chat 頁(yè)面,主要使用了 Ajax 來(lái)處理文字交互。
首先來(lái)看看主體頁(yè)面,在 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. 歡迎來(lái)到 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">您還沒(méi)有登陸,先和小黃鴨聊聊吧。?</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="說(shuō)點(diǎn)啥吧..."></textarea>
??????{%?else?%}
??????<textarea?id="message_not"?name="message"?class="write_area"?placeholder="說(shuō)點(diǎn)啥吧..."></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>
整體效果如下,是不是挺少女系的。

當(dāng)用戶(hù)在點(diǎn)擊“提交”按鈕后,調(diào)用 JS 函數(shù)
????/*用戶(hù)登陸的用戶(hù)點(diǎn)擊提交按鈕發(fā)送消息按鈕*/
????$('#sub_but_login').click(function(event){
????????sendMessageLogin(event,?fromname,?to_uid,?to_uname);
????});
為了后面便于擴(kuò)展,將未登錄的用戶(hù)特別區(qū)分開(kāi)來(lái),后面也許同樣允許未登陸用戶(hù)訪(fǎng)問(wèn)該頁(yè)面,但是只能同機(jī)器人小黃鴨聊天
????/*用戶(hù)未登陸的用戶(hù)點(diǎn)擊提交按鈕發(fā)送消息按鈕*/
????$('#sub_but').click(function(event){
????????sendMessage(event,?fromname,?to_uid,?to_uname);
????});
再來(lái)看函數(shù) 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);?//延時(shí)調(diào)用
}
接收幾個(gè)參數(shù),然后將當(dāng)前會(huì)話(huà)消息追加到 HTML 頁(yè)面中,并且調(diào)用真正的后臺(tái) API 函數(shù) 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 函數(shù)調(diào)用后臺(tái) API,并把接收到的消息回寫(xiě)到 HTML 頁(yè)面中。
而目前的后臺(tái) API 也比較簡(jiǎn)單,直接返回用戶(hù)輸入的消息
@app.route('/api/sendchat/<info>',?methods=['GET',?'POST'])
@login_required
def?send_chat(info):
????return?info
這樣,一個(gè)整體的聊天室架子就搭建好了,后面我們?cè)俳尤?redis 和自己訓(xùn)練的聊天機(jī)器人,來(lái)實(shí)現(xiàn)真正的在線(xiàn)聊天室。
到此這篇關(guān)于基于Python實(shí)現(xiàn)從頭搭建一個(gè)在線(xiàn)聊天室的文章就介紹到這了,更多相關(guān)Python在線(xiàn)聊天室內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python開(kāi)發(fā)的單詞頻率統(tǒng)計(jì)工具wordsworth使用方法
wordsworth是字母,單詞和n元組頻率分析,用來(lái)分析文件中的單詞出現(xiàn)頻率的工具。2014-06-06
Python異常對(duì)代碼運(yùn)行性能的影響實(shí)例解析
這篇文章主要介紹了Python異常對(duì)代碼運(yùn)行性能的影響實(shí)例解析,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02
親測(cè)解決tensorflow和keras版本不匹配的問(wèn)題
這篇文章主要介紹了親測(cè)解決tensorflow和keras版本不匹配問(wèn)題,完美解決:ImportError: No module named 'tensorflow.python.eager'問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
python獲得文件創(chuàng)建時(shí)間和修改時(shí)間的方法
這篇文章主要介紹了python獲得文件創(chuàng)建時(shí)間和修改時(shí)間的方法,涉及Python針對(duì)文件屬性的相關(guān)操作技巧,需要的朋友可以參考下2015-06-06
關(guān)于pandas-profiling的降級(jí)之旅
這篇文章主要介紹了關(guān)于pandas-profiling的降級(jí)之旅,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
Python K-means實(shí)現(xiàn)簡(jiǎn)單圖像聚類(lèi)的示例代碼
本文主要介紹了Python K-means實(shí)現(xiàn)簡(jiǎn)單圖像聚類(lèi)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10

