jueves, 10 de septiembre de 2020

Flask 18. Usar Flask Blueprint para estructurar las aplicaciones. Parte 1ª




Uso de Flask Blueprint para estructurar aplicaciones. Parte 1ª.


En este capítulo veremos como optimizar la estructura de nuestras aplicaciones. Esto será especialmente útil cuando el proyecto alcanza un cierto tamaño y además es gestionado por diferentes personas. O también cuando queramos reutilizar parte del código en otros proyectos.

Para mejorar la estructura del programa vamos a utilizar planos o módulos (blueprint

Hasta ahora todas las aplicaciones que hemos creado seguían la misma sencilla estructura, un archivo principal, unas pocas vistas, algunas plantillas etc.


+ nuestra_aplicación
|_ inicio.py
|_ static/
|_ templates/


Y todo su código, ya fuera más sencillo o más complejo seguía este mismo esquema. ¿Pero que pasa cuando la aplicación crece de tamaño? Pues si cojes, por ejemplo el código del capítulo 15, verás que el archivo inicio.py es mucho mas largo y complejo, que además hay varias plantillas dentro de templates y varios archivos.py junto al principal, conteniendo partes del proyecto. Vamos, que si no has seguido el proyecto desde el principio te parecerá un jaleo.

¿Y si esto lo juntamos con que el proyecto no sólo lo estas haciendo tú, sino que lo están elaborando varias personas? Pues que las probabilidades de que al ser el archivo principal tan grande, alguien de los que están trabajando en el modifique algo y estropee el trabajo del resto, son muy grandes.

Por ello lo ideal, es que te imagines el trabajo de cada uno de los participantes en el proyecto como unos módulos o compartimentos estancos en los que cada uno trabaja en su parte, sin afectar al resto y que al final se unen y esto es lo que hace que la aplicación este completa. Es como si la aplicación principal la dividiéramos en mini-aplicaciones. Eso es lo que van a hacer los Blueprints.

Para entender como se crean y como funcionan vamos a empezar con una aplicación muy sencilla en la que vamos a usar un único blueprint para ver como se crea y funciona, para luego coger el código del capitulo 15 y trasformarlo usando Blueprints.


Creamos una estructura básica típica de un directorio de Flask:

directorio_de_trabajo
|__ principal.py
|
|__ /templates
|__ /static

En principal.py escribimos el el código básico de una aplicación de Flask:

from flask import Flask

app=Flask(__name__)

@app.route('/')
def test():
    return "<h1>Prueba</h1>"

if __name__=="__main__":
    app.run(debug=True)

Bien, ahora crearemos una segunda vista (segunda.py), al mismo nivel que principal.py, que va a ser nuestro Blueprint de ejemplo. Si te fijas su construcción es muy parecida a como hemos creado la aplicación principal de Flask. Voy a escribir el código y comentamos el contenido.

segunda.py

from flask import Blueprint, render_template

# recomendacion: poner el mismo nombre a la variable que el archivo o directorio que contenga
# el Blueprint
segunda = Blueprint("segunda", __name__, static_folder="static", template_folder="templates")
# static _folder y template_folder son opcionales

@segunda.route('/')
@segunda.route('/home/')
def home():
    return render_template('home.html')

Comenzamos importando el módulo Blueprint de Flask (al igual que en la aplicación principal hicimos con el modulo Flask)

Creamos una variable, que se suele llamar igual que el archivo o directorio que la contiene, en este caso, segunda. Esta variable será un objeto de Blueprint (al igual que en la aplicación principal "app" es un objeto de Flask -> app=Flask(__name__) )

El primer parámetro del Blueprint es su nombre. Se suele recomendar ponerle el mismo nombre que la variable y por tanto del archivo o directorio que lo contiene. 

El segundo parámetro del Blueprint es la variable de importación que es una variable especial de python llamada __name__ (igual que en el proyecto principal)

El tercer parámetro static_folder y el cuarto template_folder son opcionales. Hacen referencia a si este Blueprint va a usar los directorios por defecto para plantillas y contenido estático de la aplicación principal (si los dejamos en blanco) o si por el contrario va a usar los suyos propios. Como ya dijimos el Blueprint es como una mini-aplicación y por tanto puede tener su propio lugar para guardar sus plantillas y su contenido estático independientemente de donde lo hagan las vistas del programa principal.

Por último usando el decorador, creamos dos vistas "home" e "/". Fíjate que aquí no usamos app.route sino que utilizamos el nombre del objeto de Blueprint, en este caso concreto "segunda.route".

Por último creamos la función que se aplicará a esas dos vistas, que será renderizar el archivo "home.html" que es una plantilla que nos da la bienvenida mostrando un pingüino de linux. (los archivos puedes encontrarlos en el código de la aplicación al final del capítulo)

Una vez que hemos creado el Blueprint hay que ir al programa principal y siempre es el mismo procedimiento. Importarlo y registrarlo en la aplicación principal.

Importarlo. Ten presente como es la estructura de la aplicación:

directorio_de_trabajo
|__ principal.py
|__ segunda.py
|__ templates
|      |__ base.html
|      |__ home.html
|__ static
Pues bien, para importar el objeto blueprint que hemos creado, y que se llama segunda, dentro de nuestro programa principal, le decimos al programa: " Importa desde el módulo segunda el objeto segunda ":

from segunda import segunda

Y una vez registrada la aplicación principal "app" registramos nuestras mini-aplicaciones o blueprints con:

app.register_blueprint(segunda)

Quedando el código de principal.py de la siguiente forma:

from flask import Flask
# importamos el blueprint
from segunda import segunda

app=Flask(__name__)
# Lo registramos una vez creada la aplicación principal
app.register_blueprint(segunda)

@app.route('/')
def test():
    return "<h1>Prueba</h1>"

if __name__=="__main__":
    app.run(debug=True)


Pues ya tenemos creado el blueprint.


Date cuenta de que en nuestra segunda vista, hay dos rutas que llevan a que se renderice la plantilla "home.html". Una es ...route('/home/) y la otra es ...route('/'). Pero es que en la aplicación principal, también hay una vista ...route('/'), con el mismo nombre. ¿Que pasará cuando la invoquemos?

Vamos a comenzar con lo más obvio, invocando la vista home:


Hasta aquí todo normal. ¿Pero que pasa si llamamos a la vista ('/')? ¿Se rendizará la palabra "prueba" que es lo que dice la vista de principal.py o por el contrario saldrá de nuevo este bonito pingüino como esta codificado para la vista ('/') en segunda.py?

Si ejecutas el programa, nada más cargar el navegador en la vista principal obtendrás la respuesta:



Esto se produce al entrar en conflicto las dos aplicaciones ya que ambas tienen el mismo nombre de vista ...route('/'). Para evitar esto y hacer que cada aplicación pueda tener vistas independientes vamos a usar el parámetro url_prefix al definir el blueprint. Simplemente añade esto al código:

from flask import Flask
# importamos el blueprint
from segunda import segunda

app=Flask(__name__)
# Lo registramos una vez creada la aplicación principal
app.register_blueprint(segunda, url_prefix="/admin")

@app.route('/')
def test():
    return "<h1>Test</h1>"

if __name__=="__main__":
    app.run(debug=True)


Con esto conseguiremos que nuestro blueprint empiece a funcionar cuando se acceda a la vista con prefijo /admin. Más concretamente a 127.0.0.1:5000/admin. En este caso @segunda.route("/") hace que se renderice el archivo home.html cuando vayamos a la vista inicial del blueprint que es la que ya comentamos, 127.0.0.1:5000/admin.  Y lo mismo ocurre si acedemos a la url 127.0.0.1:5000/admin/home


Vista para 127.0.0.1:5000/admin e 127.0.0.1:5000/admin/home





Sin embargo si ahora tecleas 127.0.0.1:5000/ si que veras el texto "prueba"





Hay otros argumentos opcionales que puedes proporcionar para modificar el comportamiento del Blueprint. Repasemos los que ya conocemos y veamos algunos nuevos:

static_folder: la carpeta donde se pueden encontrar los archivos estáticos del Blueprint

static_url_path: la URL desde la cual servir archivos estáticos.

template_folder: la carpeta que contiene las plantillas del Blueprint

url_prefix: la ruta para anteponer a todas las URL del Blueprint

subdomain: el subdominio con el que las rutas de este Blueprint coincidirán de forma predeterminada

url_defaults: un diccionario de valores predeterminados que recibirán las vistas de este Blueprint

root_path: la ruta del directorio raíz del Blueprint, cuyo valor predeterminado se obtiene del nombre de importación del Blueprint

Ten en cuenta que todas las rutas, excepto root_path, son relativas al directorio del Blueprint.

Puedes encontar más información aquí.


Estructura de un proyecto con Blueprint


Ahora ha llegado el momento de ver realmente el potencial de los blueprint y como se puede utilizar para ordenar la aplicación y poder reutilizar el código. 

Imaginemos, que no tenemos solo un segundo archivo con una sola vista sino que tenemos 100 archivos.py distintos cada uno con muchas vistas distintas. Además uno de ellos controla una parte de una aplicación de una empresa. Se puede corresponder con, por ejemplo, el segundo con la parte de administración, el tercero con la parte que gestiona el alta de clientes, el cuarto el que gestiona el control de las existencias de almacén, el quinto el que gestiona los productos que se venden online y así hasta que te alcance la imaginación.

Hagamos que cada uno de ellos se comporte como una aplicación propia, de manera que podamos trabajar con ella sin afectar al resto. 

Comencemos creando algunas carpetas y trasladando archivos. 

En el ejemplo anterior copia la siguiente estructura creando los directorios app y admin y moviendo los archivos que se indican. Crea los archivos __init__.py que son archivos vacíos que utilizamos en python para que se consideren los directorios como paquetes y podamos importar los objetos blueprint que necesitamos. 

/app será el directorio padre que contendrá las diferentes partes de nuestra empresa (admin, ventas, gestión de inventarios, clientes etc). En este ejemplo práctico solo he puesto una, admin, pero podrían ser cientos, cada uno con sus propias rutas, plantillas y contenido estático independiente.

Como ves esta estructura de trabajo es más práctica e intuitiva para aplicaciones de gran tamaño.

directorio_de_trabajo
|__ principal.py
|__ /app
      |__ __init__.py
      |__ /admin
	     |__ __init__.py
	     |__ segunda.py
	     |__ /templates
	     |__ /static

En templates acuérdate de mover el archivo base.html y home.html que están dentro.

Si has hecho todo correctamente, para conseguir que funcione únicamente tendremos que cambiar la ruta desde la cual importamos el objeto blueprint, en este caso el código de primera.py que quedará así:


from flask import Flask
# importamos el blueprint
from app.admin.segunda import segunda

app=Flask(__name__)
# Lo registramos una vez creada la aplicación principal
app.register_blueprint(segunda, url_prefix="/admin")

@app.route('/')
def test():
    return "<h1>Test</h1>"

if __name__=="__main__":
    app.run(debug=True)


Ejecuta principal.py y comprueba que puedes acceder igualmente a cada una de las rutas que vimos antes de organizar y mover los archivos.

127.0.0.1:5000/

127.0.0.1:5000/admin/

127.0.0.1:5000/admin/home


Para que todo esto quede más claro, en el próximo capitulo vamos a ver como trasformar todo el código del capitulo 15, la aplicación "login de usuarios", utilizando blueprints.


Código del capítulo.


Próximo Episodio: Flask 19. Usar Flask Blueprint para estructurar las aplicaciones. Parte 2ª


No hay comentarios:

Publicar un comentario