Saltar a contenido

Módulos y Recursos

Si estás construyendo una API web, rara vez se da el caso de que quieras ponerlo todo en un único archivo.

Callisto proporciona una herramienta práctica para estructurar tu aplicación manteniendo toda la flexibilidad, además de una forma sencilla de "cargar" estos módulos.

En la ruta /app/modules propuesta en el apartado de Estructura de carpetas, una vez se crea un CRUD, se puede observar la siguiente estructura de carpetas:

Nota

El ejemplo a continuación asume que creaste un nuevo módulo para servicios/endpoints llamado roles.

├── roles
│   ├── dto
│   │   ├── __init__.py
│   │   └── roles_dto.py
│   ├── helpers
│   │   └── __init__.py
│   ├── __init__.py
│   ├── models
│   │   ├── __init__.py
│   │   └── roles_model.py
│   ├── schemas
│   │   └── __init__.py
│   ├── urls.py
│   └── views
│       ├── __init__.py
│       └── roles_view.py

dto

DTO (Data Transfer Object) es un patrón de diseño que se utiliza habitualmente en el desarrollo de software para transferir datos entre las distintas capas de una aplicación. La idea principal detrás del patrón DTO es encapsular los datos y proporcionar una forma estandarizada de transferirlos entre las diferentes partes de la aplicación.

La diferencia entre un objeto de transferencia de datos y un objeto de negocio (models) ó un objeto de acceso a datos (data access object, DAO) es que un DTO no tiene más comportamiento que almacenar y entregar sus propios datos.

Los DTO son objetos simples que no deben contener lógica de negocio.

En en el caso de FastAPI, se utiliza Pydantic para la validación de los datos entrantes(request body/query path) y normalmente se les llama a estas clases "modelos/schemas", así que para evitar la confusión de nombres hemos decido llamarle a las clases que validan los datos enviados por el usuario, DTO.

Para tener en cuenta

Callisto proporciona una clase llamada ExtrictBaseModel que se extiende de BaseModel de Pydantic.

Para más información consulta el apartado de tipos comunes.

Así luce un modelo de Pydantic para validación del cuerpo de la petición(request body) de un servicio:

from app.common.types import ExtrictBaseModel
...

# create
class RolesCreateDto(ExtrictBaseModel):
    """Valida el requestBody para crear un nuevo
    rol
    """

    name: StrictStr = Field(min_length=3, max_length=50)
    status: Optional[DefaultStatus]

Nada nuevo bajo el ☀, se ha definido una clase de Pydantic, para ser utilizada en un route:

si se utiliza API Router

1
2
3
4
5
6
7
8
...

@roles_router.post("/create")
async def roles_create(
    request: Request,
    body: RolesCreateDto,
):
    ...

si se utiliza Recursos (Class Resource)

1
2
3
...

async def post(self, request: Request, body: RolesCreateDto):

helpers

Son funciones, clases y cualquier código de utilidad(reutilizable) para el módulo en sí.

models

Las clases que abstraen las tablas/colecciones de la Base de Datos (realmente aquí, no importa cual Base de Datos), se utiliza el paradigma de POO, en otros lenguajes/frameworks se les suele llamar entities.

Para tener en cuenta

Callisto proporciona una clase llamada MongoModel que contiene, métodos sencillos para gestionar a MongoDB, usando pymongo.

Para más información consulta el apartado de Base de Datos y el módulo de mongo_model.

Un ejemplo de clase que abstrae una colección de MongoDB:

...

class Roles(MongoModel):
    """Este es el modelo para la DB"""

    __tablename__: str = "Roles"

    name: StrictStr = Field(min_length=3, max_length=50)
    status: DefaultStatus
    created_at: Optional[str]
    updated_at: Optional[str]

schemas

Un sitio para manejar funciones/variables de los esquemas de JSON Schema, así como para separar estos objetos JSON, de los modelos(DTO) de Pydantic.

1
2
3
create_city_scheme = {
    "$schema": "https://json-schema.org/draft/2020-12/schema"
}

urls.py

Este archivo se encuentra en la raiz del módulo, contiene la definición del endpoint, los middlewares que afectarán a las rutas del servicio, tags etc... Permitiendo tener las operaciones de ruta relacionadas con tu servicio(roles, siguiendo el ejemplo), separadas del resto del código, para mantenerlo organizado.

Nota

Este archivo es quien controla y dirige tus rutas en el módulo, y sigue siendo parte de la misma aplicación FastAPI/web API (es parte del mismo "Paquete Python").

si se utiliza API Router

1
2
3
...

add_router(roles_router, "/roles", tags=["roles"], dependencies=[Depends(validate_auth_session)])

si se utiliza Recursos (Class Resource)

1
2
3
...

add_resource(ViewRoles, "/roles", "/me/{id}", dependencies=[Depends(validate_auth_session)])

views

Los archivos de código Python, donde se encuentran las funciones del route (endpoint), en otros lenguajes/frameworks serían algo como los controllers/services.

si se utiliza API Router

from fastapi import APIRouter, Request
...

roles_router = APIRouter()

@roles_router.post("/create")
async def roles_create(
    request: Request,
    body: RolesCreateDto,
):
    try:
        logger.info("create request: {}", request)
        logger.info("create body: {}", body)

        return response(201, HTTPStatus.OK, "role create successfully!")

    except Exception as e:
        logger.exception("roles_router.post roles_create Exception: {}", e)
        return response(400, HTTPStatus.UNEXPECTED_ERROR, error=str(e))

si se utiliza Recursos (Class Resource)

from app.core.resource_model import ResourceModel
...

class ViewRoles(ResourceModel):
    async def post(self, request: Request, body: LocalesCreateDto):
        try:
            logger.info("create request: {}", request)
            logger.info("create body: {}", body)
            return response(201, HTTPStatus.OK, "role create successfully!")

        except Exception as e:
            logger.exception("ViewRoles POST Exception: {}", e)
            return response(400, HTTPStatus.UNEXPECTED_ERROR, error=str(e))

Un poco de orden

como se comentaba arriba y a modo de resumen:

  • Pydantic maneja clases(modelos/schemas), en adelante DTO.
  • JSON Schema maneja funciones y/o variables, en adelante schemas.
  • El motor de Base de Datos maneja modelos/entities, en adelante models.
  • Los controllers/services, en adelante views.

Nota

Aquí lo importante es que tengas esta estructura de carpetas para tu módulo lo más clara posible, que sepas para que es cada carpeta y separar de forma concisa cada módulo, clase, función de la mejor manera.

Lectura recomendada

Te recomendamos leer el apartado de: