DEV Community

loading...
Cover image for Tu primer Web App con Flask: Parte 3

Tu primer Web App con Flask: Parte 3

Leonel G.
Developer | Argentina
Updated on ・4 min read

Tal como lo prometí, hoy estaremos viendo cosas interesantes: manejo de formularios y persistencia de datos en una BD.

Manejando formularios

Una pregunta que te habras hecho es, ¿cual es el chiste de manejar formularios con otra cosa que no sea HTML?

Bueno, eso se debe a que hacerlo de forma tradicional, requiere que protejamos de forma "casera" los input de nuestros formularios (te recomiendo leer sobre CSRF).

WTForms es una integracion simple de Flask que incluye entre sus caracteristicas:

  • Proteccion contra CSRF
  • Subida de archivos
  • soporte para reCaptcha
  • Internacionalizacion
  • y mas!!

Para instalarlo:

pip install Flask-WTF
Enter fullscreen mode Exit fullscreen mode

Ahora es tiempo de modelarlos!

Vas a crear un directorio llamado 'forms' y un archivo 'FormularioEstudiantes.py' donde definiremos la estructura del formulario:

from flask_wtf import FlaskForm
from wtforms.validators import DataRequired
from wtforms import StringField, SubmitField
from wtforms.fields.html5 import IntegerField, EmailField
from wtforms.widgets.html5 import NumberInput 

class FormularioEstudiantes(FlaskForm):
    nombre = StringField('Nombre', validators=[DataRequired()])
    calificacion = IntegerField('Calificacion', validators=[DataRequired()], widget=NumberInput(min=0, max=10, step=1))
    email = EmailField('Direccion de correo electronico', validators=[DataRequired()])
    submit = SubmitField('Registrar')
Enter fullscreen mode Exit fullscreen mode

Como podemos ver, con WTForms podemos controlar el tipo de dato, las validaciones, etc. que recibimos desde el cliente.

Ahora debemos de importarlo en nuestra aplicacion:

from forms.FormularioEstudiantes import FormularioEstudiantes
Enter fullscreen mode Exit fullscreen mode

Y haremos unas modificaciones a lo que ya tenemos.

Creamos una secret_key, necesaria para el token de CSRF:

app.secret_key = 'Coloca aca tu clave secreta'
Enter fullscreen mode Exit fullscreen mode

La ruta de registro ahora se verá asi:

@app.route('/registro/', methods=['GET', 'POST'])
def registro():
    if request.method == 'POST':

        formulario = FormularioEstudiantes(request.form)

        alumnos.append({
            "nombre": formulario.nombre,
            "calificacion": formulario.calificacion,
            "email": formulario.email,
        })

        return redirect(url_for('index'))
    else:
        formulario = FormularioEstudiantes()
    return render_template('registro_alumno.html', formulario=formulario)
Enter fullscreen mode Exit fullscreen mode

Es necesario que le pasemos el formulario a nuestra vista para que esta pueda mostrarlo.

Si la peticion viene por POST, quiere decir que desde el cliente se presiono el boton "Registrar", por lo que deberia venir con datos.
Es por ello que inicializamos el mismo con la data contenida en request.form.

Si la peticion viene por GET, significa que el cliente esta intentando acceder a la ruta para poder registrar un alumno. Por lo que el formulario iria vacio (en este caso no le pasamos ningun parametro en la instancia)

Ha llegado el turno de modificar tambien el HTML, que es donde debemos de "dibujar" nuestro formulario:

Una de las formas de hacerlo, es esta:

<form action="" method="POST">
    {{formulario.csrf_token}}

    {{ formulario.nombre.label() }} {{formulario.nombre()}}
    <br>
    {{ formulario.calificacion.label() }} {{formulario.calificacion()}}
    <br>
    {{ formulario.email.label() }} {{formulario.email()}}
    <br>
    {{ formulario.submit()}}
</form>
Enter fullscreen mode Exit fullscreen mode

La segunda forma es:

<form action="" method="POST">
    {{formulario.csrf_token}}

    {% for campo in formulario %}
        {{campo.label() }}{{campo()}}<br/>
    {% endfor %}
</form>
Enter fullscreen mode Exit fullscreen mode

La eleccion es tuya.

WTForms nos provee tambien de validaciones con sus correspondientes informes de "errores". Para mostrarlos solo debemos colocar lo siguiente:

{% for campo, errores in form.errors.items() %}
    <div style="color: red;">
        {{ form[campo].label }}: {{ ', '.join(errores) }}
    </div>
{% endfor %}
Enter fullscreen mode Exit fullscreen mode

Al ejecutar nuestra app y verificar el codigo HTML, deberiamos ver algo asi:

App y codigo HTML

Como podemos ver, WTForms se encargo de crear los inputs que le indicamos y ademas le aplicó las validaciones necesarias ademas de las indicadas.

Hay un campo que no mencione pero si está en el codigo y es necesario (ademas de ayudarnos a mitigar un ataque especifico). Se trata del csrf_token.
Si queres aprender mas de esto, te dejo dos enlaces para que visites:

CSRF Glosario
Cross-Site Request Forgery Prevention Cheat Sheet

Genial! Nustro formulario ya se encuentra funcionando.
Ahora solo debemos almacenar los datos de forma persistente.

Persistencia de datos

Es momento darle seriedad a la aplicacion, y guardar los datos en una BD como debe ser.

Pero cual motor debo elegir?
El que vos quieras.
En mi caso, para aplicaciones pequeñas (como esta) utilizo SQLite3, pero tenes mas opciones como PostgreSQL, MariaDB/MySQL, MSSQLServer, etc. O, si preferis NoSQL, hay opciones como MongoDB.

A diferencias de otros frameworks (como Django), Flask no posee un ORM propio, por lo que seremos nosotros los que decidamos que usar.

El mapeo objeto-relacional (o por sus siglas en ingles Object-Relational Mapping) es una técnica de programación para convertir datos entre el sistema de tipos utilizado en un lenguaje de programación orientado a objetos y la utilización de una base de datos relacional como motor de persistencia.

Basicamente, un ORM nos ayuda a trabajar con la BD sin necesidad de escribir consultas SQL o NoSQL a mano (aumentando la posibilidad de vulnerabilidades como SQL Injection).

Yo voy a estar usando SQLAlchemy.

Instalacion

pip install flask-sqlalchemy
Enter fullscreen mode Exit fullscreen mode

Por un problema en versiones recientes de SQLAlchemy, recomiendo instalar su version 1.3.23:

pip install sqlalchemy==1.3.23
Enter fullscreen mode Exit fullscreen mode

Ahora haremos una serie de modificaciones en nuestra aplicacion:

from flask import Flask, render_template, request, redirect, url_for
from forms.FormularioEstudiantes import FormularioEstudiantes
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.secret_key = 'Coloca aca tu clave de seguridad'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///alumnos.sqlite3'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

# Modelo de datos: Alumno

class Alumno(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    nombre = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    calificacion = db.Column(db.Integer)

    def __repr__(self):
        return '<Alumno %r>' % self.nombre

@app.route('/')
def index():
    alumnos = Alumno.query.all()
    return render_template('index.html', alumnos=alumnos)

@app.route('/registro/', methods=['GET', 'POST'])
def registro():
    if request.method == 'POST':

        formulario = FormularioEstudiantes(request.form)

        alumno = Alumno(
            nombre = formulario.nombre.data,
            calificacion=formulario.calificacion.data,
            email=formulario.email.data,
        )

        db.session.add(alumno)
        db.session.commit()

        return redirect(url_for('index'))
    else:
        formulario = FormularioEstudiantes()
    return render_template('registro_alumno.html', formulario=formulario)


if __name__ == '__main__':
    db.create_all()
    app.run(port=3000, debug=True)
Enter fullscreen mode Exit fullscreen mode

Ahora corremos nuestra aplicacion, y comenzamos a cargar alumnos:

Alt Text

Ya tenemos nuestra base de datos funcionando!!

Por hoy finalizamos. En la proxima entrega vamos a finzalizar con el CRUD de alumnos, y reestructuraremos un poco la app.


Si queres darle una mirada al codigo, te dejo el link acá

Discussion (0)