Capítulo 4. Bases de Datos y Validación

En el capítulo anterior, introdujimos formularios para ayudar a los usuarios a interactuar con nuestra aplicación. En ese capítulo, cuando un usuario se registra en nuestra aplicación, su información no se guarda en una base de datos. Para evitar ese problema, en esta sección aprenderemos a crear bases de datos y a realizar consultas básicas para acceder a la información almacenada en nuestras bases de datos.

En este capítulo, nos centramos en el inicio de sesión y la autenticación de usuarios. Estudiamos el paquete Flask-login y cómo podemos utilizar sus funcionalidades para permitir que el usuario inicie sesión, cierre sesión y restrinja el acceso a algunas rutas si el usuario no está autenticado. También cambiamos el diseño de nuestra aplicación según si el usuario está conectado o desconectado.


Bases de datos, básico

Algunos enlaces útiles

Los enlaces a los videos de YouTube y la cuenta de GitHub para esta sección están a continuación:

Vamos a usar la línea de comandos para interactuar con nuestras bases de datos, pero también accedemos a ellas mediante una Interfaz Gráfica de Usuario. Te sugiero que descargues e instales DB Browser para SQLite o cualquier otra Interfaz Gráfica de Usuario para interactuar con nuestra base de datos. Para descargar DB Browser para SQLite, puedes usar los siguientes enlaces:

Toda la información en esta sección se ha tomado de los siguientes enlaces:

¿Qué es Flask-SQLAlchemy?

Flask-SQLAlchemy es una extensión para Flask que agrega soporte para SQLAlchemy a tu aplicación. Simplifica el uso de SQLAlchemy con Flask al configurar objetos comunes y patrones para usar esos objetos, como una sesión vinculada a cada solicitud web, modelos y motores. Flask-SQLAlchemy no cambia cómo funciona o se usa SQLAlchemy.

¿Qué es SQLAlchemy? SQLAlchemy es el kit de herramientas SQL para Python y Mapper Relacional de Objetos que otorga a los desarrolladores de aplicaciones el poder y la flexibilidad completos de SQL.

Proporciona un conjunto completo de patrones de persistencia a nivel empresarial bien conocidos, diseñados para un acceso a bases de datos eficiente y de alto rendimiento, adaptados a un lenguaje de dominio simple y Pythonico.

¿Qué es SQL? Structured Query Language, abreviado como SQL, es un lenguaje específico de dominio utilizado en programación y diseñado para gestionar datos en un sistema de gestión de bases de datos relacional, o para el procesamiento de flujos en un sistema de gestión de flujos de datos relacional.

Nuestra primera base de datos. Una base de datos con una tabla.

Los enlaces al video de YouTube y a la cuenta de GitHub para esta sección están a continuación:

Comenzamos creando una aplicación simple con una base de datos con una tabla. Procedemos en cinco pasos (el código para la aplicación está en el panel derecho):

  1. Importamos SQLAlchemy:
    
        from flask_sqlalchemy import SQLAlchemy
                                
  2. Asignamos una ubicación para la base de datos. En nuestro caso, la base de datos estará en nuestro ordenador local. Más adelante, en la parte de producción de nuestra aplicación, crearemos una base de datos PostgreSQL en AWS:
    
        application.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///light_talk_app.db'
                                
  3. Definimos nuestra base de datos:
    
        db = SQLAlchemy(application)
                                

Una base de datos con una tabla

    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    
    application = Flask(__name__) 
    
    application.config['SQLALCHEMY_DATABASE_URI'] = 
    'sqlite:///light_talk_app.db'
    db = SQLAlchemy(application)
    
    class User(db.Model):
        __tablename__ = "user_table"
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(30), unique=True, 
        nullable=False)
        
    @application.route('/')
    @application.route('/home')
    def home():
        return "<h1>Bienvenido a casa</h1>"
    
    if __name__=='__main__':
        application.run(debug=True) 
    

  1. Crearemos una tabla para nuestra base de datos.
    
        class User(db.Model):
        __tablename__ = "user_table"
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(30), unique=True, nullable=False)
                            
  2. Creamos nuestra base de datos. Necesitamos proceder en tres pasos.
    1. En el terminal de Visual Studio Code, escribe: python.

    2. En el terminal de Python, escribe: from application import db

    3. En el terminal de Python, escribe: db.create_all()

    4. Cuando termines, escribe: quit()

1. Rellenar nuestra base de datos

Una vez que hayamos creado nuestra base de datos, podemos comenzar a rellenarla con datos. Procedemos en los siguientes pasos:

  1. En el terminal de Visual Studio, escribe: python
  2. En el terminal de Python, escribe una por una las siguientes líneas de código:
    
            from application import User
            user1 = User(username='Bjørk')
            db.session.add(user1)
            user2 = User(username='Fjell')
            db.session.add(user2)
            db.session.commit()
                                

2. Consultas básicas

También podemos realizar algunas consultas básicas.

  1. En el terminal de Visual Studio, escribe: python
  2. En el terminal de Python, escribe una por una las siguientes líneas de código:
    
            User.query.first()
            User.query.filter_by(username='Bjørk').all()
            User.query.filter_by(username='Bjørk').first().username
                                

3. Crear variables

Podemos crear variables utilizando consultas básicas.

  1. En el terminal de Visual Studio, escribe: python
  2. En el terminal de Python, escribe una por una las siguientes líneas de código:
    
            user1 = User.query.get(1)
            user1.id  
            user1.username 
            user2 = User.query.filter_by(username='Bjørk').first()
            user2.id        
            user2.username                                               
                                

Una base de datos con dos tablas

Los enlaces al video de YouTube y a la cuenta de GitHub para esta sección están a continuación:

Para introducir una segunda tabla en nuestra base de datos, procedemos en dos pasos diferentes (el código para la aplicación está en el panel derecho):

  1. Creamos una segunda tabla:
    
            class Sentence(db.Model):
                __tablename__= 'sentence_table'
                id = db.Column(db.Integer, primary_key=True)
                incorrect_sentence = db.Column(db.String)                             
                                
  2. Necesitamos conectar las tablas User y Sentence. Para hacer esto, necesitamos introducir un cambio en la tabla User y otro en la tabla Sentence.
    1. En la tabla User:
      
              sentence = db.relationship('Sentence', backref='author', lazy=True)
                                          
    2. En la tabla Sentence:
      
              user_id = db.Column(db.Integer, db.ForeignKey('user_table.id'), 
              nullable=False)
                                          

Una base de datos con dos tablas

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

application = Flask(__name__) 

application.config['SQLALCHEMY_DATABASE_URI'] = 
'sqlite:///light_talk_app.db'
db = SQLAlchemy(application)

class User(db.Model):
    __tablename__ = "user_table"
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30), 
    unique=True, nullable=False)
    sentence = db.relationship('Sentence', 
    backref='author', lazy=True)

class Sentence(db.Model):
    __tablename__= 'sentence_table'
    id = db.Column(db.Integer, primary_key=True)
    incorrect_sentence = db.Column(db.String) 
    user_id = db.Column(db.Integer, 
    db.ForeignKey('user_table.id'), nullable=False)
    
@application.route('/')
@application.route('/home')
def home():
    return "<h1>Bienvenido a casa</h1>"

if __name__=='__main__':
    application.run(debug=True) 
1. Rellenar nuestra base de datos

Una vez que hayamos creado nuestra base de datos, podemos comenzar a rellenarla con datos. Procedemos en los siguientes pasos:

  1. En el terminal de Visual Studio, escribe: python
  2. En el terminal de Python, escribe una por una las siguientes líneas de código:
    
            from application import User, Sentence
            user1 = User(username='Bjørk')
            db.session.add(user1)
            user2 = User(username='Fjell')
            db.session.add(user2)
            db.session.commit()
            
            sentence1 = Sentence(incorrect_sentence = 
            "Mi perro es bonitos", user_id=user1.id)
            sentence2 = Sentence(incorrect_sentence = 
            "Mi casa es bonito", user_id=user1.id)
            db.session.add(sentence1)
            db.session.add(sentence2)
            sentence3 = Sentence(incorrect_sentence = 
            "Mis perros es grande", user_id=user2.id)
            sentence4 = Sentence(incorrect_sentence = 
            "Mi amiga es alto", user_id=user2.id)
            db.session.add(sentence3)
            db.session.add(sentence4)
            db.session.commit()
                                

2. Consultas básicas

También podemos realizar algunas consultas básicas.

  1. En el terminal de Visual Studio, escribe: python
  2. En el terminal de Python, escribe una por una las siguientes líneas de código:
    
            sentence1 = Sentence.query.first()
            sentence1.user_id
            sentence1.author
            sentence1.author.username
            sentence1.incorrect_sentence
            db.session.add(sentence4)
            db.session.commit()
                                

Dos bases de datos, con una tabla cada una

Los enlaces al video de YouTube y a la cuenta de GitHub para esta sección están a continuación:

En muchos casos, estamos interesados en tener diferentes bases de datos en nuestra aplicación. Si nuestra empresa opera en diferentes países, podría ser útil tener bases de datos separadas para cada país. Si nuestra empresa tiene diferentes fábricas, también podría ser útil tener diferentes bases de datos. Aprendemos cómo crear dos bases de datos con una tabla cada una. Es muy fácil crear más bases de datos y/o tablas en cada base de datos.

Para crear dos bases de datos con dos tablas, procedemos en tres pasos (el código para la aplicación está en el panel derecho):

  1. Necesitamos crear enlaces a cada base de datos adicional.
    
            application.config['SQLALCHEMY_DATABASE_URI'] = 
            'sqlite:///user.db'
            application.config['SQLALCHEMY_BINDS'] = 
            {'sentence': 'sqlite:///sentence.db'}
                                
  2. Necesitamos introducir ese enlace en la(s) tabla(s) asociada(s) con esa base de datos.
    
            class Sentence(db.Model):
                __bind_key__ = 'sentence'
                __tablename__ = 'sentence_table'
                                
  3. Creamos nuestra base de datos. Necesitamos proceder en tres pasos.
    1. En el terminal de Visual Studio Code, escribe: python.

    2. En el terminal de Python, escribe: from application import db

    3. En el terminal de Python, escribe: db.create_all()

    4. Cuando termines, escribe: quit()

Dos bases de datos con una tabla cada una

        from flask import Flask
        from flask_sqlalchemy import SQLAlchemy
        
        application = Flask(__name__) 
        
        application.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///user.db'
        application.config['SQLALCHEMY_BINDS'] = 
        {'sentence': 'sqlite:///sentence.db'}
        db = SQLAlchemy(application)
        
        class User(db.Model):
            __tablename__ = "user_table"
            id = db.Column(db.Integer, primary_key=True)
            username = db.Column(db.String(30), unique=True, nullable=False)
            sentence = db.relationship('Sentence', backref='author', lazy=True)
        
        class Sentence(db.Model):
            __bind_key__ = 'sentence'
            __tablename__ = 'sentence_table'
            id = db.Column(db.Integer, primary_key=True)
            incorrect_sentence = db.Column(db.String)
            user_id = db.Column(db.Integer, 
            db.ForeignKey('user_table.id'), nullable=False)
            
        @application.route('/')
        @application.route('/home')
        def home():
            return "<h1>Bienvenido a casa</h1>"
        
        if __name__=='__main__':
            application.run(debug=True) 
        

Bases de datos, aplicadas

Algunos enlaces útiles

Los enlaces al video de YouTube y la cuenta de GitHub para esta sección están a continuación:

Crear las bases de datos

En esta sección, creamos dos bases de datos en nuestra aplicación, cada una con una tabla. Procedemos en cuatro pasos diferentes.

  1. En el archivo __init__.py:
    1. Importamos SQLAlchemy:
      
          from flask_sqlalchemy import SQLAlchemy
                                          
    2. Definimos una ubicación para nuestra base de datos, creamos los enlaces y definimos la base de datos:
      
          application.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///user.db'
          application.config['SQLALCHEMY_BINDS'] ={'sentence': 'sqlite:///sentence.db'}
          db = SQLAlchemy(application)
                                          
  2. En el archivo models.py, debemos definir nuestras tablas (el código para la aplicación está en el panel derecho):

  3. En las rutas, debemos introducir algunos cambios para crear las tablas cuando creamos las bases de datos. Esto se debe a que estamos trabajando con blueprints, y si no importamos las tablas a nuestros paquetes de blueprint y definimos esas tablas allí, cuando creemos la base de datos, estará vacía, ya que no hemos definido las tablas.
    1. En el paquete home, y en el archivo routes.py, debemos escribir:
      
          from capp.models import User
                                         
    2. En el paquete light_talk_app, y en el archivo routes.py, debemos escribir:
      
          from capp.models import Sentence
                                          
  4. Después de introducir los tres pasos anteriores, creamos nuestra base de datos. Debemos proceder en tres pasos. Consulta la sección anterior, ya que los pasos son exactamente los mismos.

models.py

    from capp import db
    from datetime import datetime
    
    # Base de datos Usuario
    class User(db.Model):
        __tablename__ = "user_table"
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(30), unique=True, nullable=False)
        email = db.Column(db.String(120), unique=True, nullable=False)
        password = db.Column(db.String(60), nullable=False)
        sentence = db.relationship('Sentence', backref='author', //
        lazy=True)
    
    # Base de datos Sentencee
    class Sentence(db.Model):
        __bind_key__ = 'sentence'
        __tablename__= 'sentence_table'
        id = db.Column(db.Integer, primary_key=True)
        incorrect_sentence = db.Column(db.String)
        correct_sentence_one = db.Column(db.String)
        correct_sentence_two = db.Column(db.String)
        correct_sentence_three = db.Column(db.String)
        correct_sentence_four = db.Column(db.String)
        objective = db.Column(db.String)
        source = db.Column(db.String)
        user_id = db.Column(db.Integer, db.ForeignKey('user_table.id'),//
        nullable=False)
    

Agregar usuarios a nuestra base de datos y crear contraseñas seguras

En el paquete user, en el archivo routes.py, debemos introducir tres cambios (el código para la aplicación está en el panel derecho):

  1. Importamos los paquetes que necesitamos:
    
        from capp import db, bcrypt
                                
  2. Aseguramos la contraseña:
    
        user_hashed_password = bcrypt.generate_password_hash//
        (form.password.data).decode('utf-8')
                                
  3. Agregamos el usuario creado en el formulario a nuestra base de datos:
    
        user = User(username=form.username.data, //
        email=form.email.data, password=user_hashed_password)
        db.session.add(user)
        db.session.commit()
                                

routes.py

    from capp import db, bcrypt
    
    @users.route('/register', methods=['GET','POST'])
    def register():
        form = RegistrationForm()
        if form.validate_on_submit():
            user_hashed_password = bcrypt.generate_password_hash//
            (form.password.data).decode('utf-8')
            user = User(username=form.username.data, //
            email=form.email.data, password=user_hashed_password)
            db.session.add(user)
            db.session.commit()
            flash('¡Tu cuenta ha sido creada! //
            Ahora puedes iniciar sesión.', 'success')
            return redirect(url_for('home.home_home'))
        return render_template('register.html', title='Register', //
        form=form)
    

Introducción de consultas en nuestra aplicación

Algunos enlaces útiles sobre consultas en SQLAlchemy se pueden encontrar en los siguientes enlaces:

En el paquete home, en el archivo routes.py, debemos introducir tres cambios.

  1. Importamos las tablas que necesitamos:
    
            from capp.models import User, Sentence
                                
  2. Crearemos las consultas:
    
            user1 = User.query.first().username
            sentence1 = Sentence.query.first().incorrect_sentence
            user3 = User.query.filter_by(username='Bjørk').first().username
            user4 = User.query.filter_by(username='Bjørk').first()
            sentences = Sentence.query.filter_by(user_id=user4.id)
            users = User.query.all()
                                
  3. Pasamos las consultas a los archivos html:
    
            return render_template('home.html', user1=user1, //
            sentence1=sentence1, user3=user3, sentences=sentences, users=users)
                                
En la carpeta de plantillas, en el archivo home.html necesitamos mostrar las consultas (el código para la aplicación está en el panel derecho):

home.html

        <!-- Consultas -->
        <section class="section_developers">
            <div class="container">
            <div class="developers" style="display:block;">
                <h5>Consulta 1</h5>
                {{user1}}
                <h5>Consulta 2</h5>
                {{sentence1}}
                <h5>Consulta 3</h5>
                {{user3}}
                <h5>Consulta 4</h5>
                {% for sentence in sentences %}
                {{sentence.incorrect_sentence}}
                {% endfor %}
                <h5>Consulta 5</h5>
                {% for user in users %}
                {{user.username}}
                {% endfor %}
            </div>
            </div>          
        </section>
        

Personalizar nuestros Validadores de Formularios WTF

Algunos enlaces útiles

Los enlaces a los videos de YouTube y la cuenta de GitHub para esta sección están a continuación:

Toda la información en esta sección ha sido tomada de los siguientes enlaces:

Personalizar nuestros Validadores de Formularios WTF

Personalizamos los mensajes que se muestran al usuario. Si, por ejemplo, intentamos registrarnos usando el mismo nombre de usuario o correo electrónico, recibimos un mensaje no deseado. Para evitar esos mensajes que podrían no ser muy útiles para los usuarios de nuestra aplicación, podemos personalizar nuestros Validadores de Formularios WTF.

En el paquete users, en el archivo forms.py.

  1. Copiar y pegar desde el enlace anterior, y luego personalizar.
    
        class MyForm(Form):
        name = StringField('Nombre', [InputRequired()])
        
        def validate_name(form, field):
            if len(field.data) > 50:
            raise ValidationError('El nombre debe tener menos de 50 caracteres')
                                
  2. Importamos las herramientas que necesitamos:
    
        from capp.models import User
        from wtforms.validators import ValidationError                                
                                
  3. Personalizar errores de nombre de usuario y correo electrónico.
    
        def validate_username(self, username):
            user = User.query.filter_by(username=username.data).first()
            if user:
                raise ValidationError('Ese nombre de usuario ya está en uso.//
                 Por favor, elige uno diferente.')
                                      
        def validate_email(self, email):
            user = User.query.filter_by(email=email.data).first()
            if user:
                raise ValidationError('Ese correo electrónico ya está en uso. //
                Por favor, elige uno diferente.') 
                                

En el paquete users, archivo forms.py

    from flask_wtf import FlaskForm
    from wtforms import StringField, PasswordField, SubmitField, 
        BooleanField
    from wtforms.validators import DataRequired, Length, Email, 
        EqualTo, ValidationError
    from capp.models import User
    
    class RegistrationForm(FlaskForm):
        username = StringField('Nombre de usuario', 
            validators=[DataRequired(), Length(min=2, max=30)])
        email = StringField('Correo electrónico',
            validators=[DataRequired(), Email()])
        password = PasswordField('Contraseña', validators=[DataRequired()])
        confirm_password = PasswordField('Confirmar contraseña',
            validators=[DataRequired(), EqualTo('password')]) 
        submit = SubmitField('Registrarse')
    
        def validate_username(self, username):
        user = User.query.filter_by(username=username.data).first()
        if user:
            raise ValidationError('Ese nombre de usuario ya está en uso. //
            Por favor, elige uno diferente.')
    
        def validate_email(self, email):
        user = User.query.filter_by(email=email.data).first()
        if user:
            raise ValidationError('Ese correo electrónico ya está en uso. //
            Por favor, elige uno diferente.')
    

Paquete Flask-login

Algunos enlaces útiles

Los enlaces al video de YouTube y a la cuenta de GitHub para esta sección están a continuación:

Toda la información en esta sección ha sido tomada de los siguientes enlaces:

¿Qué es Flask-login?

Flask-Login proporciona la gestión de sesiones de usuario para Flask. Maneja las tareas comunes de iniciar sesión, cerrar sesión y recordar las sesiones de tus usuarios durante períodos prolongados.

Este paquete hará lo siguiente:

¿Cómo usar Flask-login?

  1. Instalación

    Instala la extensión con pip: pip install flask-login.

  2. Configurando tu aplicación

    La parte más importante de una aplicación que usa Flask-Login es la clase LoginManager. Deberías crear una para tu aplicación en algún lugar de tu código, así:
    
    from flask_login import LoginManager
    login_manager = LoginManager()
                        
    El login manager contiene el código que permite que tu aplicación y Flask-Login trabajen juntos, como cargar un usuario a partir de un ID, a dónde enviar a los usuarios cuando necesitan iniciar sesión, y similares.

  3. Cómo funciona

    Necesitarás proporcionar un callback user_loader. Este callback se utiliza para recargar el objeto usuario a partir de la ID del usuario almacenada en la sesión. Debe tomar la ID del usuario como cadena de texto y devolver el objeto de usuario correspondiente. Por ejemplo:
    
    @login_manager.user_loader
    def load_user(user_id):
        return User.get(user_id)
                        
    Debe devolver None (no generar una excepción) si la ID no es válida. (En ese caso, la ID se eliminará manualmente de la sesión y se continuará con el procesamiento).

  4. Tu clase de usuario

    La clase que utilizas para representar a los usuarios necesita implementar estas propiedades y métodos:
    1. is_authenticated

      Esta propiedad debe devolver True si el usuario está autenticado, es decir, ha proporcionado credenciales válidas. (Solo los usuarios autenticados cumplirán los criterios de login_required).

    2. Hay otras clases que no vamos a utilizar, pero que se pueden estudiar en el enlace de Flask-login que proporcioné arriba en esta sección.

  5. Documentación de la API

    Esta documentación se genera automáticamente a partir del código fuente de Flask-Login.
    1. flask_login.current_user

      Un proxy para el usuario actual.

    2. flask_login.login_user(user, remember=False, duration=None, force=False, fresh=True)

      Inicia sesión con un usuario. Debes pasar el objeto usuario real a esto. Si la propiedad is_active del usuario es False, no se iniciará sesión a menos que force sea True.

      Esto devolverá True si el intento de inicio de sesión tiene éxito, y False si falla (es decir, porque el usuario está inactivo).

      Parámetros:
      • user (objeto) - El objeto usuario para iniciar sesión.
      • remember (bool) - Si se debe recordar al usuario después de que expire su sesión. Por defecto es False.
      • duration (datetime.timedelta) - La cantidad de tiempo antes de que expire la cookie de recordatorio. Si es None, se usa el valor establecido en la configuración. Por defecto es None.
      • force (bool) - Si el usuario está inactivo, configurar esto como True permitirá iniciar sesión de todos modos. Por defecto es False.
      • fresh (bool) - Configurar esto como False iniciará sesión con una sesión marcada como no “fresca”. Por defecto es True.

    3. flask_login.logout_user()

      Cierra la sesión de un usuario. (No necesitas pasar el usuario real). Esto también limpiará la cookie de recordatorio si existe.

  6. Ayudantes del objeto usuario

    class flask_login.UserMixin

    Esto proporciona implementaciones predeterminadas para los métodos que Flask-Login espera que tengan los objetos usuario.

Flask-login en nuestra aplicación.

En esta sección, utilizamos Flask-login en nuestra aplicación. Introducimos cuatro cambios que nos permiten usar diferentes funcionalidades de Flask-login.

  1. Instalación:
    
            pip install flask-login
                                
  2. Configuración. En el archivo __init__.py:
    1. Importa las herramientas que necesitamos:
      
              from flask_login import LoginManager
              login_manager.login_view = 'users.login'
              login_manager.login_message_category = 'info'
                                          
    2. Configura la aplicación:
      
              login_manager= LoginManager(application)
                                          

  3. Cómo funciona. En el archivo models.py:
    1. Importa las herramientas que necesitamos:
      
              from capp import login_manager
                                          
    2. Importa UserMixin:
      
              from flask_login import UserMixin
                                          
    3. Define la función:
      
              @login_manager.user_loader
              def load_user(user_id):
                  return User.query.get(int(user_id)) 
                                          
    4. Úsalo en las Clases:
      
              class User(db.Model, UserMixin)
                                          

En el archivo __init__.py:

        from flask_login import LoginManager
        
        login_manager= LoginManager(application)
        login_manager.login_view = 'users.login'
        login_manager.login_message_category = 'info'
        
En el archivo models.py:

        from capp import login_manager
        from flask_login import UserMixin
        
        @login_manager.user_loader
        def load_user(user_id):
            return User.query.get(int(user_id))
        
En el paquete users, en el archivo routes.py:

        from capp.users.forms import RegistrationForm
        from capp.models import User
        from capp import db, bcrypt
        from flask_login import login_user
        
        users=Blueprint('users',__name__)
        
        @users.route('/register', methods=['GET','POST'])
        def register():
            form = RegistrationForm()
            if form.validate_on_submit():
                user_hashed_password = //
                bcrypt.generate_password_hash(form.password.data).decode('utf-8')
                user = User(username=form.username.data, email=form.email.data, //
                password=user_hashed_password)
                db.session.add(user)
                db.session.commit()
                flash('¡Tu cuenta ha sido creada! Ahora puedes iniciar sesión.', 'success')
                return redirect(url_for('users.login'))
            return render_template('users/register.html', //
            title='register', form=form)
        

  1. En el paquete users, archivo routes.py, ruta de inicio de sesión:
    1. Importa las herramientas que necesitamos:
      
              from flask_login import login_user
                                      
    2. Modifica la ruta:
      
              user = User.query.filter_by(email=form.email.data).first()
              if user and bcrypt.check_password_hash(user.password, form.password.data):
                  login_user(user, remember=form.remember.data)
                  flash('¡Has iniciado sesión! Ahora puedes comenzar a usar nuestra aplicación.', 'success')
              else:
                  flash('Inicio de sesión fallido. ¡Por favor, verifica el correo electrónico y la contraseña!', 'danger')
                                      
    3. Verifica el campo:
      
              login_user(user, remember=form.remember.data)
                                      

Usuario autenticado, cerrar sesión y redirigir a la última página visitada

En esta sección, nos enfocamos en tres usos diferentes de Flask-login que son útiles para nuestra aplicación.

  1. Si el usuario está autenticado, no queremos que inicie sesión nuevamente en nuestra aplicación, sino que lo redirigimos a la página principal. Para hacer esto, en el paquete users, en el archivo routes.py, procedemos en dos pasos:
    1. Importamos las herramientas que necesitamos:
      
              from flask_login import current_user
                                          
    2. En la ruta de inicio de sesión, necesitamos redirigir al usuario a la página principal:
      
              if current_user.is_authenticated:
                  return redirect(url_for('home.home_home'))
                                          
  2. Queremos permitir que el usuario cierre sesión. Para hacer esto, en el paquete users, en el archivo routes.py, procedemos en dos pasos:
    1. Importamos las herramientas que necesitamos:
      
              from flask_login import logout_user
                                          
    2. Necesitamos crear una nueva ruta:
      
              @users.route('/logout')
              def logout():
                  logout_user()
                  return redirect(url_for('home.home_home')) 
                                          

  3. Dado que la aplicación light_talk está conectada a una base de datos para registrar las frases, no queremos permitir que el usuario acceda a esa página web hasta que haya iniciado sesión. Para hacer esto, en el paquete light_talk_app, en el archivo routes.py, procedemos en dos pasos:
    1. Importamos las herramientas que necesitamos:
      
              from flask_login import login_required
                                          
    2. Introducimos un nuevo decorador en las rutas que queremos modificar:
      
              @light_talk_app.route('/light_talk_app')
              @login_required
                                          

En el paquete user, archivo routes.py:

        from flask import render_template, Blueprint, redirect, flash,
            url_for, request
        from capp.users.forms import RegistrationForm, LoginForm
        from capp.models import User
        from capp import db, bcrypt
        from flask_login import login_user, current_user, logout_user 
        
        users=Blueprint('users',__name__)
        
        @users.route('/register', methods=['GET','POST'])
        def register():
          ...
        
        @users.route('/login', methods=['GET','POST'])
        def login():
          form = LoginForm()
          if current_user.is_authenticated:
                return redirect(url_for('home.home_home'))
          if form.validate_on_submit():
            user = User.query.filter_by(email=form.email.data).first()
            if user and bcrypt.check_password_hash(user.password, 
                form.password.data):
                login_user(user, remember=form.remember.data)
                next_page = request.args.get('next')
                flash('¡Has iniciado sesión! Ahora puedes comenzar a 
                usar nuestra aplicación Light Talk.', 'success')
                return redirect(next_page) if next_page else 
                  redirect(url_for('home.home_home'))
            else:
                flash('Inicio de sesión fallido. ¡Por favor, 
                verifica el correo electrónico y la contraseña!', 
                  'danger') 
          return render_template('users/login.html', title='login', form=form)
        
        @users.route('/logout')
        def logout():    
            logout_user()
            return redirect(url_for('home.home_home'))
        
En el paquete light_talk_app, archivo routes.py:

        @light_talk_app.route('/light_talk_app')
        @login_required
        
        @light_talk_app.route('/light_talk_app/nueva_frase')
        @login_required
        
        @light_talk_app.route('/light_talk_app/tus_frases')
        @login_required    
        

  1. Si el usuario intenta acceder a las rutas que queremos proteger obligando al usuario a estar autenticado, el usuario será redirigido a la página de inicio de sesión. En ese caso, después de iniciar sesión, queremos que el usuario sea redirigido a la última página a la que intentó acceder. Para hacer esto, en el paquete users, en el archivo routes.py, procedemos en dos pasos:
    1. Importamos las herramientas que necesitamos:
      
              from flask import request
                                      
    2. Modificamos la ruta de inicio de sesión:
      
              if user and bcrypt.check_password_hash(user.password, form.password.data):
                  login_user(user, remember=form.remember.data)
                  next_page = request.args.get('next')
                  flash('¡Has iniciado sesión! Ahora puedes comenzar a usar nuestra aplicación Light Talk.', 'success')
                  return redirect(next_page) if next_page else redirect(url_for('home.home_home'))
              else:
                                      

Cambios en la barra de navegación (archivo layout.html):

        {% if current_user.is_authenticated %}
        <nav>
            <ul>
                <li>
                <a href="{{url_for('users.logout')}}">Cerrar sesión</a>
                </li>
                <li>
                    <a href="{{url_for('home.home_home')}}">Inicio</a>
                </li>
                <li>
                    <a href="{{url_for('business.business_home')}}">
                    Negocio</a>
                </li>
                <li>
                    <a href="{{url_for('light_talk_app.light_talk_app_home')}}">
                    Light-Talk-App</a>
                </li>
            </ul>
        </nav> 
        {% else %}
        <nav>
            <ul>
                <li>
                <a href="{{url_for('users.register')}}">Registrarse</a>
                </li>
                <li>
                <a href="{{url_for('users.login')}}">Iniciar sesión</a>
                </li>
                <li>
                    <a href="{{url_for('home.home_home')}}">Inicio</a>
                </li>
                <li>
                    <a href="{{url_for('business.business_home')}}">
                    Negocio</a>
                </li>
                <li>
                    <a href="{{url_for('light_talk_app.light_talk_app_home')}}">
                    Light-Talk-App</a>
                </li>
            </ul>
        </nav>
        {% endif %}
                
Cambios en la página de inicio (archivo home.html):

        {% if current_user.is_authenticated %}
        {% else %}
        <div class="container_buttons_links_header_home">{
            <div class="btn">
                <a href="{{url_for('users.register')}}" 
                class="btn btn-primary btn-lg mr-2", 
                style="background-color:#007bff;">Registrarse</a>
            </div>
            <div class="btn">
                <a href="{{url_for('users.login')}}" 
                class="btn btn-primary btn-lg mr-2", 
                style="background-color:#007bff;">Iniciar sesión</a>
            </div>
        </div>
        {% endif %}
                

Reto Semanal

En este reto semanal, vamos a crear algunas consultas SQLAlchemy para acceder a nuestras bases de datos, y vamos a utilizar código Python para extraer información de las filas obtenidas mediante las consultas.

Nos vamos a enfocar en cinco consultas diferentes. Sin embargo, te animo a que crees tus propias consultas y experimentes con las bases de datos y el código Python. Las seis consultas que vamos a estudiar en el reto semanal son:

  1. Consulta 1: Frases por usuario.
  2. Consulta 2: Frases incorrectas por usuario.
  3. Consulta 3: Frases correctas por usuario.
  4. Consulta 4: Objetivos por usuario.
  5. Consulta 5: Fuente por usuario.

Sé que este reto semanal puede ser difícil, ya que vas a utilizar consultas SQLAlchemy y Python, y algunos de ustedes no están familiarizados con estos dos lenguajes de programación. Te animo a revisar el código, ejecutar la aplicación y tratar de entender la lógica detrás del código. Vamos a revisar el reto semanal juntos durante las clases.