Siguiendo con el punto anterior, se dice que hay dos formas de organizar un
proyecto, mediante una división Estructural o bien mediante una división
Funcional.
División Estructural.
En una división Estructural, el código se agrupa basandonos en lo que hace
dentro del programa. Es decir, se agrupan por ejemplo los archivos de
vistas, de un lado, las plantillas por otro, el contenido estático, los
formularios y así sucesivamente.
La estructura típica de esta división sería la siguiente:
+ directorio_de_trabajo/
|_ app/
|_ __init__.py
|_ static/
|_ templates/
|_ public/
|_ index.html
|_ ...
|_ users/
|_ login.html
|_ sign_up.html
|_ ...
|_ private/
|_ index.html
|_ ...
|_ rutas/
|_ __init__.py
|_ private.py
|_ public.py
|_ users.py
|_ ...
|_ modelos/
|_ users.py
|_ ...
|_ formularios/
|_ users.py
|_ ...
|_ inicio.py
|_ requirements.txt
|_ ...
División Funcional.
Por el contrario en una división funcional, los diferentes componentes del
código se van a agrupar en base a lo que hacen o para lo que se utilizan en
la aplicación. Así todas las vistas, plantillas, modelos, formularios etc de
una parte que tienen una relación común en la aplicación se agrupan dentro
de un mismo paquete.
La estructura típica de esta división sería la siguiente:
+ directorio_de_trabajo/
|_ app/
|_ __init__.py
|_ publico/
|_ __init__.py
|_ routes.py
|_ static/
|_ templates/
|_ models.py
|_ forms.py
|_ ...
|_ privado/
|_ __init__.py
|_ routes.py
|_ static/
|_ templates/
|_ models.py
|_ forms.py
|_ ...
|_ usuarios/
|_ __init__.py
|_ routes.py
|_ static/
|_ templates/
|_ models.py
|_ forms.py
|_ ...
|_ ...
|_ static/
|_ templates/
|_ inicio.py
|_ requirements.txt
|_ ...
Para ver como aplicar esto que hemos visto, vamos a utilizar el código del
capítulo 15 "Login de Usuarios" que puedes encontrar
en esta dirección del repositorio de github. Utilizaremos en él un modelo funcional, aunque realmente el diseño del
proyecto puedes hacerlo tu como quieras, de la forma que se adapte a
tus necesidades.
Al utilizar una división funcional, agruparemos los diferentes elementos en
tres bloques. El bloque principal será el que tiene que ver con las
funciones de autentificación del usuario (login, registro y log-out), otro
bloque contendrá las páginas que son públicas (en el ejemplo solo la página
inicial) y un último bloque con la página a a la que se accede una vez el
usuario se ha logeado (dashboard).
Bloque de páginas públicas
(En nuestro caso solamente la pagina_inicial)
Si abrimos el proyecto del capítulo 15, verás que tiene la siguiente
estructura de directorios:
Empecemos a trabajar creando un nuevo directorio, dentro del directorio de
trabajo y al nivel de la aplicación principal. Este archivo lo puedes llamar
como quieras, yo lo llamaré app y contendrá todo el contenido de
la aplicación.
Vamos a ir paso a paso. Dentro de este directorio, app, crearemos un
archivo vacío llamado __init__.py para que python considere este directorio
como un paquete y podamos importar diferentes recursos de ella.
Lo más sencillo es empezar creando el blueprint para la parte pública de la
aplicación. Recordemos que esta parte pública contendrá, en nuestro caso, la
vista y la plantilla de la pagina_inicial o de bienvenida que es la que
puede ver todo el mundo.
Dentro del directorio app, creamos también el directorio
public donde meteremos todo lo que hemos dicho en el párrafo
anterior. Aquí dentro también creamos un nuevo archivo
__init__.py
Para usar un blueprint
siempre seguiremos los mismos pasos:
"Primero creación e inicialización del blueprint y segundo registro del
mismo en la aplicación principal, en nuestro caso inicio.py".
En este último __init__.py que hemos creado, el que esta dentro del
directorio public, tecleamos:
from flask import Blueprint
public = Blueprint('public', __name__, template_folder='templates')
from . import routes
Esto ya lo habíamos visto lo que significaba en el capítulo anterior.
Lo único nuevo es el "from . import routes". Con esto, lo que le decimos al programa es que de este mismo directorio,
en donde esta el __init__.py, importe el módulo routes, que vamos a crear a
continuación. Pero antes de esto creamos dentro del directorio
public, la carpeta templates y cortamos y pegamos la plantilla
pagina_inicial.html, que está en el directorio templates, pero en el primer
nivel del directorio de trabajo.
Ahora si, creamos el archivo routes.py que contendrá la ruta de la
vista de esta página_inicial.
routes.py
from flask import render_template
from . import public
@public.route('/')
def home():
return render_template('pagina_inicial.html')
Comentémoslo un poco.
En la primera línea importamos el método render_template para poder
renderizar la plantilla página_inicil.html. Luego le decimos que importe el
objeto blueprint public que está en el mismo directorio y que creamos en el
archivo __init__.py. Para finalizar con una función que define la vista de
la manera habitual.
Sin embargo esto aún no funcionaría porque nos falta un último paso, que es
registrar el blueprint en la aplicación principal. Para ello en el archivo
inicio.py antes de declarar las vistas tecleamos:
# importamos y registramos los blueprint
from app.public import public
app.register_blueprint(public)
Le estamos diciendo que importe el blueprint public y lo registre en la
aplicación principal de Flask que es "app".
Ahora cada vez que vayamos a la url 127.0.0.1:5000 se utilizará este
blueprint para ir a la página principal.
IMPORTANTE:
Date cuenta que esta vista ya estaba definida en el archivo inicio.py
con lo que tienes que buscar y borrar su código en este
archivo, en inicio.py. Si no lo haces tendrías una función "/" en inicio.py y otra función "/" en
/app/public/routes.py
Para que quede un poco más claro la estructura de la aplicación hasta
el momento quedará de la siguiente forma:
Si ahora pruebas el programa se renderizará la página principal
correctamente, pero si intentas ir a otras opciones nos dará un error,
puesto que ahora todo lo que hace referencia a el archivo
pagina_inicial.html
no está en el directorio templates principal, sino que se encuentra
ahora en app/public/templates, ha cambiado la dirección a la que
apuntan. Esto lo vamos a ir arreglando sobre la marcha según vayamos
cambiando el resto de las plantillas.
Bloque de páginas privadas (a las que se puede acceder una vez logeados)
Por seguir con cosas fáciles, vamos ahora a preparar la parte a la que se
podía acceder una vez que el usuario se había logeado en la aplicación.
Estrictamente hablando, en esta parte ni siquiera se renderizaba una
plantilla, tan solo la vista devolvía un mensaje por pantalla en el que se
mostraba el siguiente texto "Bienvenido {}, esta es la página de control a
la que se puede acceder si estas registrado"
Creamos un directorio llamado private dentro del directorio
app.
directorio_trabajo/app/private
Seguimos los mismos pasos que en el punto anterior y creamos el archivo
__init__.py en donde crearemos el blueprint "private" y
cargaremos las rutas del mismo.
from flask import Blueprint
private = Blueprint("private",__name__)
# como no tiene de momento plantillas no usamos template_folder
from . import routes
A continuación, al mismo nivel creamos el archivo routes.py
directorio_trabajo/app/private/routes.py
from . import private
from flask_login import login_required, current_user
@private.route('/dashboard/')
# decorador para que no se pueda entrar en la vista si no estas logeado
@login_required
def dashboard():
return '''<h2>Bienvenido {},esta es la pagina de control <br/> a la que se puede acceder
si estas registrado.</h2>
<br />
<a href="/">Volver</a>
'''.format(current_user.nombre_usuario)
Importamos el blueprint "private" y los módulos login_required y
current_user de flask_login.
Hacemos un corta y pega de la vista /dashboard/ del archivo inicio.py en
este archivo routes.py.
En el definíamos la función que va a definir la vista de la página privada
(dashboard). Como es una página simple no se renderiza un archivo html sino
que retornamos directamente un texto en formato html. Al tratarse de un
ejemplo sencillo esta parte del código no tiene más vistas y por tanto es la
única a la que se puede acceder cuando el usuario se haya logeado.
Para finalizar registramos el blueprint en la aplicación principal.
Bloque relacionado con las funciones de autentificación de
usuarios.
Nos pondremos ahora con la parte de la aplicación que cuya función es
registrar a los usuarios, logearlos y posteriormente realizar la
desconexión. El proceso es el mismo que en puntos anteriores pero con
algunas modificaciones que tenemos que realizar en el tema de las
plantillas.
Comenzamos creando el directorio que contendrá todos los archivos y el
código. Yo lo voy a llamar "auth" y estará en el directorio:
Directorio_de_trabajo/app/auth
y dentro creamos el archivo __init__.py donde definiremos el
blueprint.
Directorio_de_trabajo/app/auth/__init__.py
from flask import Blueprint
auth = Blueprint('auth', __name__, template_folder="templates")
from . import routes
El código es igual que los anteriores.
Comenzamos importando el método Blueprint de Flask (auth).
Definimos el objeto blueprint "auth" que tendrá una carpeta al mismo
nivel para almacenar las plantillas de sus vistas que llamaremos
"templates". Esta carpeta es propia de este directorio y solo la van a
usar las plantillas del grupo auth.
Aunque todavía no hemos creado el archivo routes.py, que
contendrá las rutas de las diferentes vistas, lo importamos para
finalizar este archivo.
Creamos la carpeta "templates" en:
Directorio_de_trabajo/app/auth/templates
Para a continuación cortar y pegar todas las plantillas que estaban en:
Directorio_de_trabajo/templates a este nuevo directorio.
(login.html, signup.html y flask.html). Luego tenemos que hacerlas unas pequeñas modificaciones.
Seguimos registrando el blueprint en la aplicación principal
inicio.py:
Ahora, creamos al mismo nivel el archivo routes.py
que contendrá las vistas de este grupo.
Punto primero, importamos el blueprint:
Seguimos trasladando de inicio.py
todo lo relacionado con el inicio de sesión en el sistema de los
usuarios, que era la vista 'login'.
Como esta parte es la más laboriosa, que no complicada, vamos a y ir
explicándola muy despacio.
Nos vamos a inicio.py y cortamos y pegamos todo el código de la vista login
en el archivo routes.py
Date cuenta de que en el código el decorador para la vista login no es
@app.route(...) sino que como está en el blueprint auth tiene que ser
@auth.route(...)
from . import auth
# cortado y pagado de la vista login desde inicio
@auth.route('/login/', methods=['GET', 'POST'])
def login():
form = Formulario_de_Login()
# (2) Si el usuario ya estuviera registrado no tiene sentido que volviera a hacer un login
# asi que lo mandamos de vuelta a la página del dashboard
por lo anterior
...
HASTA
...
# Si el usuario no existe o la contraseña es incorrecta
# return "<h2>Usuario o Contraseña incorrecta</h2>"
flash('El usuario no existe o la contraseña es incorrecta')
return redirect(url_for('login'))
else:
return render_template('login.html', form=form)
Si tal como esta el proyecto ahora mismo lo ejecutamos, nos dará el
siguiente error, nada más cargar la página principal.
Esto se debe a que al intentar renderizar la página principal, los
url_for que hacen referencia a esta vista se están buscando en la aplicación
principal "app". Sin embargo no debe buscarlos ahí pues hemos movido la
definición de esta vista al blueprint "auth". Afortunadamente el mismo
programa nos da la solución, que es tan sencilla como en la plantilla que se
carga al inicio, pagina_inicial.html, indicar en todos url_for los
blueprint a los que corresponden. En esta primera vista es sustituir "login"
por "auth.login"
Para ir dejándolo todo arreglado también modificaremos los enlaces para
signup y logout, como puedes ver en la imagen inferior.
Al haber desplazado archivos se nos siguen produciendo errores. Si
ejecutáramos en el programa la vista de login nos daría este error:
Esto se debe a que la vista esta usando un objeto que aun no hemos
especificado en este archivo y tampoco hemos
movido el método que lo genera. En otras palabras, tenemos que cortar y
pegar el archivo form.py que está en el directorio principal al directorio
"auth", además de importar el objeto en el archivo routes.py.
1 from . import auth
2 from .form import Formulario_de_Login
Lo mismo nos va a ocurrir con unas cuantas cosas que estaban definidas o
cuyo código estaba en inicio.py y que ahora tenemos que trasladar a
routes.py para que funcionen sus vistas. Para no extendernos demasiado voy a
poner el código de todo lo que hay que trasladar:
routes.py
from . import auth
from .form import Formulario_de_Login, Formulario_de_Registro
from flask import redirect, url_for, request, render_template, flash
# Por temas de seguridad en la ruta
from werkzeug.urls import url_parse
# para que la contraseña no se guarde como texto plano
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import current_user, login_user, logout_user, login_required
from inicio import db
from .modelos import Usuario
El último punto de este código hace referencia a la importación del objeto
Usuario que va a estar dentro del archivo que vamos a crear a continuación
en este mismo directorio, modelos.py. Contendrá la parte del código que
gestionaba o mejor dicho definía la estructura del objeto usuario. En el se
gestionaba la estructura de la base de datos donde guardamos el nombre,
correo electrónico y contraseña de los usuarios que se registraban en
nuestra aplicación.
Lo único que tenemos que hacer es crear el archivo modelos.py y
cortar y pegar el código siguiente que hasta ahora se encontraba en nuestra
aplicación principal, inicio.py.
modelos.py
from flask_login import UserMixin
from inicio import db
# --------------------------------- Modelo de bases de datos------------------------
# Hay que modificar la clase usuario para trabajar con la clase UserMixin de Flask-login
class Usuario(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
nombre_usuario = db.Column(db.String(15), unique=True)
correo_electronico = db.Column(db.String(50), unique=True)
contrasena = db.Column(db.String(80))
# ----------------------------------------------------------------------------------
Importante: este objeto Usuario es utilizado por en el cargador de Usuarios
en Flask Login. Al haber desplazado el código de la aplicación principal a
app/auth/modelos.py tenemos que importar el objeto Usuario previamente
añadiendo lo siguiente al código en inicio.py:
Además, como dijimos anteriormente, tenemos que modificar los url_for que
hay en la plantilla login.html para que hagan referencia a donde se
encuentra ahora, después de moverlas, la vista a la que hacen
referencia:
login.html
"Esto es muy importante, hemos de revisar cualquier punto donde hayamos
utilizado URL_FOR ya sea plantilla o archivo de python, como por
ejemplo routes.py para que señale al blueprint donde se encuentra la vista
a la que hace referencia".
Hacemos lo mismo para la vista signup. Cortamos y pegamos el código entero de
la vista desde inicio.py hasta el directorio routes.py justo de bajo de la
vista login que habíamos movido anteriormente.
Igual que antes, al desplazar el archivo desde la app principal al blueprint
auth tenemos que modificar el decorador de la vista. También modificaremos todos los url_for
que haya tanto en el código de la vista, como en la plantilla signup.html para
que hagan referencia a los directorios donde se encuentran actualmente.
routes.py
Una vez realizado todo lo anterior realizamos exactamente los mismos pasos
para la vista logout.
Para finalizar simplemente limpiamos el código en inicio.py que ya no se usa
como por ejemplo métodos que existían al comenzar el capitulo pero que ahora
ya no son necesarios al cortar y pegar el código a otros blueprint.
Al final ya tenemos un código manejable, funcional, que puede ser usado en
otros programas o por varias personas que trabajen el proyecto de forma
independiente.
Al terminal la estructura de directorios quedaría de la siguiente manera:
Archivo con los paquetes necesarios para ejecutar la aplicación.
A estas alturas del curso de Flask, ya hemos instalado bastantes paquetes
dentro de nuestro entorno virtual. Si alguna vez tenemos que volver a
ejecutar este entorno en otro máquina, de memoria, seguro que tendríamos
problemas para recordar que paquetes instalamos, por lo que la práctica
generalmente aceptada para evitar este problema es escribir un archivo
llamado requirements.txt. Este archivo estará en la carpeta raíz de nuestro proyecto, e
indicara todas las dependencias o paquetes que necesita el proyecto para
funcionar, junto con sus versiones.
¿Y esto como se hace te preguntarás? Pues realmente es muy
fácil.
(miEntorno) $ pip freeze > requirements.txt
El comando pip freeze volcará automáticamente toda la información sobre paquetes
instalados y versiones en el archivo requirements.txt. De esta forma, si
queremos recrear este mismo entorno virtual en otra máquina, lo único que
tenemos que hacer es escribir:
(miEntorno) $ pip install -r requirements.txt
Con esto se instalará todo lo necesario para que pueda funcionar nuestro
programa.
Y con esto terminamos el capitulo de los blueprint. Puedes ver el código
completo terminado en el siguiente enlace:
Código del capítulo en github.
Próximo episodio. Desplegar una aplicación en Flask con Gunicorn y Nginx en
una Rapsberry Pi.