Microframework: Flask
Contents
Microframework: Flask#
![Logo Flask](../_images/logo-flask.png)
Documentación básica para trabajar con el Microframework Flask
Índice
Configuraciones#
Creación del proyecto#
Crear un Directorio para el proyecto y acceder al mismo.
Crear un entorno virtual.
Instalar flask:
pip install flask
.Dentro del directorio creamos un directorio llamado templates y un archivo llamado main.py
Archivo principal#
El archivo principal básico:
1# importamos flask y creamos el objeto con flask:
2from flask import Flask
3app = Flask(__name__)
4
5# Con un decorador definimos la ruta raiz y dentro una función que devuelve un mensaje.
6@app.route('/')
7def hello_workd():
8 return 'Hola Mundo!!!'
9
10# creamos la condición para arrancar la aplicación flask:
11if __name__ == "__main__":
12 # le pasamos la ejecución de la app y por parámetros la ruta del host y si esta en modo depuración o no.
13 app.run(host='localhost', debug=True)
Arrancar el servidor:
python main.py
Abrir en navegador: http://localhost:5000
Nota
Al tener habilitado el flag debug en la función run nos dará un pin al arrancar el servidor, dicho pin se usará para abrir el depurador en el navegador cuando tengamos algún error de manera que se pulsará sobre el icono de la consola en cualquier línea y se introduce el pin.
Rutas#
Rutas principales (main.py)#
Las rutas se suelen añadir en el archivo principal del proyecto:
1from flask import Flask
2app = Flask(__name__)
3
4@app.route('/')
5def hello_workd():
6 return 'Hola Mundo!!!'
7
8# despues de la / creamos otra ruta:
9@app.route('/otra_ruta')
10def hola():
11 return 'Acceso a otra ruta!'
12
13if __name__ == "__main__":
14 app.run(host='localhost', debug=True)
En el navegador abrir: http://localhost:5000/otra_ruta
rutas con parámetros#
Las rutas con parámetros:
1from flask import Flask
2app = Flask(__name__)
3
4@app.route('/')
5def hello_workd():
6 return 'Hola Mundo!!!'
7
8# con los símbolos mayor-menor podemos capturar un parámetro por defecto tipo cadena:
9@app.route('/nombre/<persona>')
10def nombre(persona):
11 return 'Te llamas {}'.format(persona)
12
13# Podemos definir que sea un entero o coma flotante:
14@app.route('/edad/<int:edad>')
15def edad(edad):
16 return 'Tienes: {} años'.format(edad)
17
18if __name__ == "__main__":
19 app.run(host='localhost', debug=True)
La ruta que recibe parámetros por ejemplo sería: http://localhost:5000/nombre/Guillermo
Rutas por uno o más métodos#
Es posible estipular uno o varios métodos permitidos en una ruta:
1from flask import Flask
2# importamos la librería request que funciona igual que en django:
3from flask import request
4
5app = Flask(__name__)
6
7@app.route('/')
8def hello_workd():
9 return 'Hola Mundo!!!'
10
11# el decorador recibe una lista de métodos permitidos:
12@app.route('/metodos', methods=['GET', 'POST'])
13def login():
14 # preguntamos si el método es GET que nos abra sesion y si no que nos mande de vuelta al formulario:
15 if request.method == 'GET':
16 return 'Recibido ' + request.method
17 else:
18 print("Es otro método")
19
20if __name__ == "__main__":
21 app.run(host='localhost', debug=True)
Nota
aunque en este ejemplo se usa GET para validar el metodo de entrada, es más común y más cómodo validar el método POST, ya que este último podría presentar más iteracciones en el código que el método GET.
Redirecciones#
1# cargar librerías redirect y url_for:
2from flask import Flask, render_template, redirect, url_for
3
4
5app = Flask(__name__)
6
7@app.route('/')
8def hello_workd():
9 # Redireccionar a otra ruta usando el nombre de su función:
10 return redirect(url_for('saludar'))
11
12
13@app.route('/saludar', methods=['GET'])
14def saludar(nombre=None):
15 return 'Hola desde otra ruta'
16
17
18if __name__ == "__main__":
19 app.run(host='localhost', debug=True)
Error 404#
Procedimiento común para redirección 404:
Crear en templates archivo error.html:
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>ERROR 404</title>
8</head>
9<body>
10 <h1>ERROR 404</h1>
11 <p>La página solicitada no existe</p>
12</body>
13</html>
Crear error 404 cuando introducimos rutas incorrectas:
1from flask import Flask, render_template, redirect, url_for
2
3app = Flask(__name__)
4
5@app.route('/')
6def hello_workd():
7 return redirect(url_for('saludar'))
8
9
10@app.route('/saludar', methods=['GET'])
11def saludar(nombre=None):
12 return 'Hola desde otra ruta'
13
14# cargamos la ruta de error cuando no se encuentre página:
15@app.errorhandler(404)
16def error(error): # este recibe un parámetro
17 return render_template('error.html'), 404
18
19if __name__ == "__main__":
20 app.run(host='localhost', debug=True)
Vistas#
Las vistas en Flask no suelen usarse, en su lugar se visualizan los templates con Jinja
Templates (Jinja2)#
Jinja2 es el motor de plantillas que se utiliza de serie en Flask.
En la carpeta templates se crea un archivo por ejemplo prueba.html:
1<!DOCTYPE html>
2<html lang="es">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Prueba con Flask</title>
7</head>
8<body>
9 <h1>Plantillas Jinja2</h1>
10 <p>primera toma de contacto con Flask</p>
11</body>
12</html>
De vuelta a main.py se renderiza el template:
1# importamos render_template para gestionar plantillas:
2from flask import Flask, render_template
3
4app = Flask(__name__)
5
6@app.route('/')
7def hello_workd():
8 return 'Hola Mundo!!!'
9
10@app.route('/prueba')
11def prueba(nombre=None):
12 # Renderizamos el archivo prueba:
13 return render_template('prueba.html')
14
15if __name__ == "__main__":
16 app.run(host='localhost', debug=True)
Formularios#
En Django podemos crear formularios individuales y reutilizables.
Formularios via POST#
Se crea el archivo por ejemplo form.html:
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>Document</title>
8</head>
9<body>
10 <form method="POST">
11 <input type="text" name="nombre" placeholder="Tu nombre">
12 <input type="text" name="apellidos" placeholder="Tus apellidos">
13 <input type="submit" value="Saludar">
14 </form>
15</body>
16</html>
Lo siguiente es registrar las rutas en Flask:
1from flask import Flask, render_template, redirect, url_for, request
2
3app = Flask(__name__)
4
5@app.route('/')
6def hello_workd():
7 return redirect(url_for('saludar'))
8
9
10@app.route('/saludar', methods=['GET'])
11def saludar():
12 return render_template('form.html')
13
14@app.route('/saludar', methods=['POST'])
15def saludo():
16 nombre = request.form['nombre']
17 apellidos = request.form['apellidos']
18 return 'Te llamas ' + nombre + " " + apellidos
19
20
21if __name__ == "__main__":
22 app.run(host='localhost', debug=True)
Recuperar archivos en Flask#
De este modo se gestionaría la subida de un archivo desde un formulario:
Crear en la raiz un directorio llamado subidas
Crear en templates un archivo llamado subidas.html:
1<!DOCTYPE html>
2<html lang="es">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Prueba con Flask</title>
7</head>
8<body>
9 <form method="POST" action="/subir" enctype="multipart/form-data">
10 <label for="documento">Subir archivo</label>
11 <br>
12 <input type="file" name="documento">
13 <br><br>
14 <input type="submit">
15 </form>
16</body>
17</html>
Por último registrar dos rutas en main.py:
1from flask import Flask, render_template, request
2# añadimos también una utilidad para generar nombres seguros de archivos:
3from werkzeug.utils import secure_filename
4
5app = Flask(__name__)
6
7@app.route('/')
8def hello_workd():
9 return 'Hola Mundo!!!'
10
11# Ruta del form:
12@app.route('/subir', methods=['GET'])
13def hola(nombre=None):
14 return render_template('subidas.html')
15
16# Ruta para procesar la petición:
17@app.route('/subir', methods=['POST'])
18def subir():
19 if request.method == 'POST':
20 # recuperamos el archivo del parametro files:
21 f = request.files['documento']
22 # ahora guardamos el archivo con un nombre seguro:
23 f.save('./subidas/{}'.format(secure_filename(f.filename)))
24 return 'Se ha subido correctamente'
25
26if __name__ == "__main__":
27 app.run(host='localhost', debug=True)
Bases de datos con SqlAlchemy#
Instalar librería#
Para usar esta librería se instala:
pip install Flask-Sqlalchemy
Crear paquete del proyecto#
Se crea un paquete llamado application y dentro un archivo __init__.py para inicializarlo, se mueve aquí el archivo principal de la aplicación.
Configurar base de datos#
En el paquete application del proyecto se crea un archivo config.py:
1import os
2
3secret_key = 'CAB7D2CD6CCDC28113C9A3189DEE647BD945B0903EFEC1E160F2ADB5203CE649'
4PWD = os.path.abspath(os.curdir)
5
6DEBUG = True # Modo depuración lo pasamos aquí
7# Vinculamos el conector a la base de datos, en este caso sqlite:
8SQLALCHEMY_DATABASE_URI = 'sqlite:///{}/dbase.db'.format(PWD)
9SQLALCHEMY_TRACK_MODIFICATIONS = False # por rendimiento definimos el track en false
ahora editamos el archivo principal para añadir los cambios:
1from flask import Flask
2# Se carga el sqlalchemy también aqui:
3from flask_sqlalchemy import SQLAlchemy
4# se carga el archivo de configuración:
5from application import config
6
7app = Flask(__name__)
8
9# se añade una linea para cargar el archivo config en app:
10app.config.from_object(config)
11# y se carga la base de datos:
12db = SQLAlchemy(app)
13
14@app.route('/')
15def inicio():
16 return 'Página principal'
17
18if __name__ == "__main__": # establecemos el debug que tenemos en config:
19 app.run(host='localhost', debug=config.DEBUG)
Crear modelo de datos#
En el paquete application del proyecto se creará un archivo llamado models.py:
1# Se importan los tipos de datos:
2from sqlalchemy import *
3# se importa el relacionador:
4from sqlalchemy.orm import relationship
5# se importa la configuración de la base de datos:
6from application.app import db
7
8
9# creamos un primer modelo que será relacionable con el segundo:
10class Categories(db.Model):
11 """ Categorías """
12 # se define nombre interno de tabla y los campos:
13 __tablename__ = 'categories'
14 id = Column(Integer, primary_key=True)
15 name = Column(String(100))
16
17 # retornar clase y función:
18 def __repr__(self):
19 return (u'<{self.__class__.__name__}: {self.id}>'.format(self=self))
20
21
22# Crear otro modelo relacionado con el primero:
23class Articles(db.Model):
24 """ Artículos """
25 __tablename__ = 'articles'
26 id = Column(Integer, primary_key=True)
27 nombre = Column(String(100),nullable=False)
28 precio = Column(Float,default=0)
29 iva = Column(Integer,default=21)
30 descripcion = Column(String(255))
31 image = Column(String(255))
32 stock = Column(Integer,default=0)
33 CategoriaId=Column(Integer,ForeignKey('categories.id'), nullable=False)
34 categoria = relationship("Categories", backref="Articles")
35
36 def precio_final(self):
37 return self.precio*self.iva/100
38
39 def __repr__(self):
40 return (u'<{self.__class__.__name__}: {self.id}>'.format(self=self))
Crear base de datos#
Para crear la base de datos ejecutamos la línea de comandos python y los siguientes comandos:
importar configuración base de datos:
from application.app import db
importar modelos de datos a crear:
from application.models import Categories, Articles
Ejecutar creación de tablas:
db.create_all()
Migrar datos modificados en el modelo#
Actualmente Flask no incorpora un modelo de migración de datos como en el caso de Django. Si se añade un nuevo dato hay que ejecutar primero db.drop_all()
y a continuación
db.create_all()
CRUD desde consola python#
importar configuración base de datos:
from application.app import db
Crear registro#
asignar valor al modelo que se quiere adherir dato:
cat=Categories(name="RPG")
Cargar en el orm los datos:
db.session.add(cat)
Cargar en el orm varios datos:
db.session.add_all([cat1, cat2])
Comitear los datos en la base de datos:
db.session.commit()
Leer registro#
Cargar una lista completa de registros:
cat=Categories.query.all()
(se tiene que recorrer con un bucle)Cargar primer elemento de la lista:
cat=Categories.query.first()
Cargar un elemento de la lista por dato de referencia:
cat=Categories.query.get(1)
Filtrar registros:
Categories.query.filter_by(name="RPG").all()
Filtrar por múltiples campos:
Categories.query.filter_by(name="RPG").filter_by(subname="rol").all()
Ortdenar registros recuperados:
Categories.query.order_by("name").all()
Nota
Cada registro recuperado es un objeto, por lo que acceder a cada campo es igual al acceder a un atributo de una clase ej: cat.name
Nota
Al igual que se filtran registros y se obtienen todos los resultados posibles, podemos obtener con first() el primer resultado posible.
Editar registro#
Cargar un elemento de la lista por dato de referencia:
cat=Categories.query.get(1)
Modificar un campo del elemento:
cat.name="Rol"
Cargar en el orm los datos:
db.session.add(cat)
Comitear los datos en la base de datos:
db.session.commit()
Eliminar registro#
Cargar un elemento de la lista por dato de referencia:
cat=Categories.query.get(1)
Cargar en el orm los datos con delete:
db.session.delete(cat)
Comitear los datos en la base de datos:
db.session.commit()
Trabajar con relaciones#
A partir de un campo foreign key se pueden obtener los datos de la otra tabla relacionada
como un objeto:
* Si recuperamos un registro de la tabla articles: art1=Articles.query.get(1)
* Al estar relacionada con categories se puede recuperar la información de la categoría: art1.categories.nombre
Uso del ORM en archivos py#
El uso del orm es similar al visto en la consola, ejecutando las operaciones desde scripts python:
1from aplicacion.models import Articles
2@app.route('/')
3def inicio():
4 articulos=Articles.query.all()
5 return render_template("inicio.html",articulos=articulos)
Esto en una plantilla se usaría así:
1<div class="panel-heading">Videojuegos</div>
2 <table class="table">
3 {% for art in articulos %}
4 <tr>
5 <td>{{art.nombre}}</td>
6 </tr>
7 {% endfor %}
8 </table>
9</div>
Sistema de usuarios y sesiones#
Modelo de usuarios#
Antes de nada hay que crear un modelo de usuarios en la base de datos:
1class Usuarios(db.Model):
2"""Usuarios"""
3__tablename__ = 'usuarios'
4id = Column(Integer, primary_key=True)
5username = Column(String(100),nullable=False)
6password_hash = Column(String(128),nullable=False)
7nombre = Column(String(200),nullable=False)
8email = Column(String(200),nullable=False)
9admin = Column(Boolean, default=False)
10
11def __repr__(self):
12 return (u'<{self.__class__.__name__}: {self.id}>'.format(self=self))
13
14@property
15def password(self):
16 raise AttributeError('password is not a readable attribute')
17
18@password.setter
19def password(self, password):
20 self.password_hash = generate_password_hash(password)
21def verify_password(self, password):
22 return check_password_hash(self.password_hash, password)
…. TODOOOO