Cuando queremos realizar un chat como toca, se nos debería pasar por la cabeza usar WebSockets. Perminte infinidad de conexiones, es asincrono, utilizas protocolos modernos. E inmediatamente si buscas información pensarás que NodeJS la única alternativa. ¡Grave error!, podemos trabajar en otros lenguajes de programación. Y si usas a diario, o te enamora como a mi, Python… lo puedes realizar en un instante.
Por un lado usando el micro framework favorito de la comunidad Python: Flask. Y por otro VueJS para el frontend. En unos sencillos pasos lo tendremos funcionando.
¿Qué bibliotecas de Python necesitamos?
- Flask: Obviamente.
- Flask-Migrate: Lo utilizaremos para hacer migraciones, al igual que Django.
- Flask-Script: Junto a Flask-Migrate, nos ayudará a realizar migraciones por terminal.
- Flask-SocketIO: Con esta biblioteca conseguiremos realizar nuestro chat con WebSockets.
- Flask-SQLAlchemy: ORM que utilizaremos para guardan en nuestra base de datos el historial y poder cargarlo.
- python-dotenv: Manera sencilla de configurar nuestra aplicación.
Preparando nuestro proyecto.
Creamos un entorno virtual con VirtualEnv, lo activamos y después instalamos las bibliotecas antes mencionadas.
virtualenv venv
source venv/bin/activate
pip install flask flask-migrate flask-script flask-socketio flask-sqlalchemy python-dotenv
Ahora creamos un archivo llamado .env. Irá toda nuestra configuración.
SECRET_KEY=secret
DEBUG=True
DATABASE="sqlite:///database.sqlite"
DOMAIN=127.0.0.1:5000
Base de datos
Nuestros mensajes antiguos se van a guardar y recuperar. El chat tendrá un historial.
Creamos un archivo llamado models.py. Con el siguente contenido.
# -*- coding: utf-8 -*-
# Librerias
from flask import Flask
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from os import environ
from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv())
app = Flask(__name__)
# Settings
app.config['SQLALCHEMY_DATABASE_URI'] = environ.get('DATABASE')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Variables
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
class Chat(db.Model):
'''
Table chat
'''
__tablename__ = 'chat'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(128))
text = db.Column(db.Text)
created_at = db.Column(
db.DateTime, nullable=False, default=datetime.utcnow)
def __repr__(self):
return '<Chat {0}>'.format(self.username)
if __name__ == "__main__":
manager.run()
Después realizamos la migración.
python3 models.py db init
python3 models.py db migrate
python3 models.py db upgrade
Se creará el archivo database.sqlite con la tabla preparada.
Backend con Flask
Creamos un archivo llamado app.py. Dentro pegamos.
from os import environ
from dotenv import load_dotenv, find_dotenv
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
from models import db, Chat
load_dotenv(find_dotenv())
app = Flask(__name__)
# Configuracion
app.config['SECRET_KEY'] = environ.get('SECRET_KEY')
app.config['DEBUG'] = True if environ.get('DEBUG') == 'True' else False
app.config['PORT'] = 80
# Socketio
DOMAIN = environ.get('DOMAIN')
socketio = SocketIO(app)
# Base de datos
app.config['SQLALCHEMY_DATABASE_URI'] = environ.get('DATABASE')
db.init_app(app)
# Cargamos la plantilla HTML con el frontend
@app.route('/<username>/')
def open_chat(username):
my_chat = Chat.query.all()
return render_template(
'chat.html',
domain=DOMAIN,
chat=my_chat,
username=username
)
# Recibirá los nuevos mensajes y los emitirá por socket
@socketio.on('new_message')
def new_message(message):
# Emitimos el mensaje con el alias y el mensaje del usuario
emit('new_message', {
'username': message['username'],
'text': message['text']
}, broadcast=True)
# Salvamos el mensaje en la base de datos
my_new_chat = Chat(
username=message['username'],
text=message['text']
)
db.session.add(my_new_chat)
db.session.commit()
# Iniciamos
if __name__ == '__main__':
socketio.run(app)
Frontend con Vuejs y Plantilla HTML
Creamos la carpeta templates, y dentro el archivo chat.html. Pegamos lo siguiente.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Chat</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>
<body>
<div id="app">
<!-- Renderizará los nuevos mensajes -->
<section>
<div v-for="message in messages">
<p><strong>@[[ message.username ]]</strong></p>
<p>[[ message.text ]]</p>
</div>
</section>
<!-- Formulario para introducir nuevos mensajes -->
<section>
<input v-model="newMessage" @keypress.enter="sendMessage" type="text" placeholder="Escribe un mensaje...">
<button @click="sendMessage">Enviar</button>
</section>
</div>
<!-- Importamos socket.io -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.slim.js"></script>
<!-- Importamos VueJS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<script>
// Conectamos con nuestro dominio
var socket = io.connect('{{ domain }}');
// Instanciamos VueJS
var app = new Vue({
el: "#app",
delimiters: ['[[', ']]'],
data: {
username: '{{ username }}',
// Le damos los mensajes del hitorial
messages: [
{% for message in chat %}
{
username: '{{ message.username }}',
text: '{{ message.text }}'
}{% if not loop.last %},{% endif %}
{% endfor %}
],
newMessage: ''
},
methods: {
sendMessage: () => {
// Enviamos el nuevo mensaje
socket.emit('new_message', {
channel: app.channel,
username: app.username,
text: app.newMessage
});
// Clear text
app.$set(app, 'newMessage', '');
}
}
});
socket.on('connect', function() {
console.log('Connect')
});
socket.on('new_message', function(msg) {
// Recibimos los nuevos mensajes y los añadimos a nuestro array
let my_messages = app.messages;
my_messages.push({
username: msg.username,
text: msg.text
})
app.$set(app, 'messages', my_messages);
});
</script>
</body>
</html>
Ejecutamos
Ya ha llegado el momento de ver los resultados.
python3 app.py
Abrimos en el navegador la ruta http://127.0.0.1:5000/minombre/.
En la ruta se especifica el nombre del usuario que aparecerá en el chat.
Con un poco de Bulma… quedaría de la siguente forma.
El código lo tenéis en Github por si queréis aportar cualquier cosa.
{{ comments.length }} comentarios