Crear un proyecto en Flask, con flask_restx parte 1

Kalim Al Razif
10 goto 10
Published in
5 min readJul 21, 2020
Imagen tomada de link.

Si eres impaciente o sencillamente no deseas leer la aburrida historia puedes saltar directamente al proyecto.

En ocasiones cuando un framework es muy versátil cuesta poder empezar a trabajar pues al buscar información sobre como crear un proyecto muchas formas de hacerlo.

Esto me paso con flask, al comenzar a leer la documentación y varios tutoriales me costo llegar a un esqueleto de proyecto que fuese mas o menos razonable para mi.

Los primeros ejemplos que vi en la documentación eran muy sencillos y al querer extrapolar a funcionalidades mas avanzadas, bases de datos, flask_restx, etcétera; me tropecé con muchos features automágicos y con agujeros en mi conocimiento de python en general que deberían llenarse con horas de practica y lectura.

¿El problema?
Debo aprenderlo “¡Para YA!” Los proyectos no esperan 😅

Como debo trabajar en flask con restx el patrón básico que vi en los tutoriales de flask solo no me era muy útil, y al tratar de ver tutoriales de flask_restx había un salto gigante en la conformación del proyecto, ni hablar de agregar una base de datos. Después de mucho dar vueltas y buscar información di con esta estructura de proyecto espero que les sea útil.

El proyecto

Ante que nada vamos a instalar una herramienta que nos permita usar una versión de python especifica y hacernos independientes de la versión de python instalada en el sistema operativo que puede cambiar a lo largo del tiempo con las actualizaciones del sistema operativo.

Para instalarlo vamos a seguir las instrucciones de el siguiente enlace.

Una vez instalado pyenv hacemos:

pyenv install x.y.z

Esto instalara la versión x.y.z de python en nuestro directorio de usuario.

Lo primero seria crear el directorio para el proyecto:

mkdir prueba_flaskcd prueba_flask

Ahora definimos la versión de python que vamos a usar dentro de este proyecto con:

pyenv local x.y.z

Y creamos un virtual environment con:

python3 -m venv nombre_venv

Luego vamos a crear algunos directorios, al final el proyecto debería lucir así, el directorio del final (test_flask) es el nombre de mi ambiente virtual.:

test_flask
├── app
│ ├── main
│ │ ├── Controller
│ │ ├── Dto
│ │ ├── Service
│ │ ├── Templates
│ │ └── Util
│ └── test
├── instance
└── test_flask

Ahora empezaremos a instalar los paquetes, pero para eso activaremos primero el ambiente virtual, nos ubicamos en el directorio de la app y hacemos:

source ./test_flask/bin/activate

Esto activa el ambiente virtual y luego podemos hacer, esto instalara los paquetes que necesitamos para comenzar.:

pip install flask flask_restx flask_script

Crearemos ahora el archivo ./app/main/__init__.py:

from flask_restx import Api
from flask import Blueprint

# Luego, aqui van los imports de los controladores ejemplo:
# from .main.Controller.user_controller import api as user_ns

blueprint = Blueprint('api', __name__)
api = Api(blueprint,
title='Titulo API',
version='1.0.0',
description='Descripcion de la api',
prefix='/api'
)

# Aqui se agregan los espacios de nombres que importamos arriba a la app
# api.add_namespace(user_ns, path='/user')

En este archivo estamos definiendo la aplicación y sus componentes (blueprints). Como estamos usando restx en acá definimos también el titulo de la aplicación, la versión, la descripción y el prefijo donde comenzaran los endpoints. El principal beneficio de usar restx es que si definimos todo correctamente la misma aplicación generara el swagger de nuestro código, evitando hacer “doble trabajo”.

En el directorio app vamos a crear lo que se llama denomina el application factory. Generamos el archivo app/__init__.py.

import logging
from flask import Flask
from logging.config import dictConfig


# Aca importamos módulos para nuestra aplicación, por ejemplo:
# from flask_pymongo import PyMongo
# from flask_bcrypt import Bcrypt


# Importamos la configuración
from instance.config import config_by_name, logging_config

# Acá integramos los módulos, por ejemplo:
# db = PyMongo()
# flask_bcrypt = Bcrypt()


def create_app(config_name):
dictConfig(logging_config)
app = Flask(__name__)
app.config.from_object(config_by_name[config_name])
# Acá se inicializan los módulos, por ejemplo:
# db.init_app(app)
# flask_bcrypt.init_app(app)
return app

En esta application factory vamos a crear una instancia de la aplicacion y a devolverla para que corra. Esto permite tener diferentes configuraciones ejecutar el mismo código con solo definir que configuración usar. En la documentación de flask esta explicada con mas detalle.

Ahora vamos a crear el archivo de configuración, dentro del directorio ./instance vamos a poner el archivo coinfig.py:

import os


dir = os.path.dirname(__file__)


class Config:
# Clave secreta del flas
SECRET_KEY = os.getenv('SECRET_KEY', 'Set_a_very_dificult_key')
DEBUG = False
BUNDLE_ERRORS = True
SERVER_NAME = "127.0.0.1:5001"


class DevConfig(Config):
ENV = 'development'
DEBUG = True


class TestConfig(Config):
ENV = 'testing'
DEBUG = True
TESTING = True
PRESERVE_CONTEXT_ON_EXCEPTION = False


class ProdConfig(Config):
DEBUG = False
ENV = 'production'


config_by_name = dict(
dev=DevConfig,
test=TestConfig,
prod=ProdConfig
)

key = Config.SECRET_KEY

logging_config = {
'version': 1,
'formatters': {'default': {
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
}},
'handlers': {
'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default'
},
'custom_handler': {
'class' : 'logging.FileHandler',
'formatter': 'default',
'filename': '{}/logs/app.log'.format(dir)
}
},
'root': {
'level': 'INFO',
'handlers': ['wsgi', 'custom_handler']
}
}

En este archivo creamos una clase llamada Config que sera nuestra clase base para la aplicacion. En ella vamos a definir los parámetros básicos que todas las versiones de la configuración van a tener en común. Luego vamos creando configuraciones para los distintos ambientes que heredan todas de la clase base. Acá vamos a definir por ejemplo credenciales de bases de datos y servicios. También es posible sobrescribir nuevos valores en parámetros definidos en la clase base. En este archivo también podemos definir las configuraciones de las bitácoras de la aplicacion.

Por ultimo en el directorio de la aplicacion generamos un archivo llamado app_ctl.py con el siguiente contenido:

#! python
import os
import unittest
from flask_script import Manager

from app.main import blueprint
from app import create_app

app = create_app(os.getenv('BOILERPLATE_ENV') or 'dev')
app.register_blueprint(blueprint)
app.app_context().push()

manager = Manager(app)


@manager.command
def run():
app.run()


@manager.command
def test():
"""Runs the unit tests."""
tests = unittest.TestLoader().discover('app/test', pattern='test*.py')
result = unittest.TextTestRunner(verbosity=2).run(tests)
if result.wasSuccessful():
return 0
return 1


if __name__ == '__main__':
manager.run()

Este archivo hace uso de flask_script para generar un script externo que podemos usar para ejecutar comandos de nuestra aplicacion, por ejemplo: ejecutar la aplicacion, hacer deploy de la base de datos, crear un usuario etcetera. En la documentacion de flask script puede leerse más.

Por el momento esto es lo minimo que necesitamos para ejecutar la aplicacion en flask.

La corremos con el siguiente comando:

python app_ctl.py run * Serving Flask app "app" (lazy loading)
* Environment: development
* Debug mode: on
[2020-07-21 13:16:05,647] INFO in _internal: * Running on http://127.0.0.1:5001/ (Press CTRL+C to quit)
[2020-07-21 13:16:05,648] INFO in _internal: * Restarting with stat
[2020-07-21 13:16:05,825] WARNING in _internal: * Debugger is active!
[2020-07-21 13:16:05,825] INFO in _internal: * Debugger PIN: 272-214-306

Y podemos ver el resultado en el navegador.

Interfaz de la app

Notas

  • Flask script parece estar obsoleta, en su pagina indican el uso de la herramienta cli de flask.

Enlaces para saber mas

--

--

Kalim Al Razif
10 goto 10

Nací, crecí y aquí sigo. Curioso de nacimiento. Ávido lector. Animeadicto. Cinéfilo o cinefilico XD. SysAdmin por vocación.