Tutorial Flask para crear un chat con WebSockets y VueJS | Programador Web Valencia

Tutorial Flask para crear un chat con WebSockets y VueJS

3 minutos

Flask

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.

Esta obra está bajo una Licencia Creative Commons Atribución-NoComercial-SinDerivadas 4.0 Internacional.

Atribución/Reconocimiento-NoComercial-SinDerivados 4.0 Internacional

¿Me ayudas?

Comprame un café
Pulsa sobre la imagen

No te sientas obligado a realizar una donación, pero cada aportación mantiene el sitio en activo logrando que continúe existiendo y sea accesible para otras personas. Además me motiva a crear nuevo contenido.

Comentarios

{{ comments.length }} comentarios

Nuevo comentario

Nueva replica  {{ formatEllipsisAuthor(replyComment.author) }}

Acepto la política de Protección de Datos.

Escribe el primer comentario

Tal vez también te interese...