Software Testing & QA Services

Estructura y validación de datos en Python con Pydantic, Marshmallow y dataclasses

January 07, 2025

Tags: Tecnologías
python

 

Es común que en cualquier lenguaje de programación esperemos que los datos recibidos y enviados tengan una estructura definida. En Python existen múltiples maneras de lograr esto; como se muestra a continuación.

 

Dataclasses

 

Viniendo de cualquier otro lenguaje de programación, algunas de las opciones que vienen a la mente son usar structs o clases que cumplan este propósito.

 

En el caso de Python, para facilitar esta tarea, se incluyeron las dataclasses. Estas se crean utilizando un decorador incluido en [PEP 557 - Data Classes](https://peps.python.org/pep-0557/) desde Python 3.7.

 

Algunas de las labores que facilitan las dataclasses son la creación de un __init__ automático que por defecto acepta los atributos de nuestra clase (con sus tipos y valores por defecto) y un __repr__ para la clase.

 

Ejemplo

 

```py

from dataclasses import dataclass

 

@dataclass

class Usuario:

    nombre: str

    edad: int

usuario = Usuario(nombre="Esteban", edad=45)

```

 

python

 

Pydantic

 

Hasta este punto, podemos crear estructuras con los valores que esperamos utilizar, pero hace falta algo; la validación. En el caso de dataclasses, aparte de las anotaciones de Python, en realidad no tenemos otra manera de asegurarnos de que los datos sean del tipo que esperamos, o con las restricciones que deseamos.

 

[Pydantic](https://docs.pydantic.dev/2.10/) soluciona esto, integrándose directamente con las anotaciones de Python. Incluyendo también mensajes de error claros en caso de que no se pueda validar correctamente, algunas clases como el PositiveInt para agregar otras restricciones a los tipos convencionales, y funcionalidades como usuario.model_dump() para mostrar campos específicos.

 

Ejemplo

 

```py
from pydantic import BaseModel, PositiveInt, ValidationError


class Usuario(BaseModel):
    nombre: str
    edad: PositiveInt


data = {
    "nombre": "Esteban",
    "edad": -45,
}

try:
    usuario = Usuario(**data)
except ValidationError as e:
    print(e.errors())
```

Resultado

```
[{'type': 'greater_than', 'loc': ('edad',), 'msg': 'Input should be greater than 0', 'input': -45, 'ctx': {'gt': 0}, 'url': 'https://errors.pydantic.dev/2.10/v/greater_than'}]
```

 

Marshmallow

 

Cuando los tipos y validaciones de Pydantic no son suficientes, entra [marshmallow](https://marshmallow.readthedocs.io/en/stable/) a la escena. Marshmallow cambia las anotaciones de tipos por sus propios campos con tipos de datos con validaciones como las de URL, funciones personalizables para validación, restricciones de los valores, mensajes de error, entre otras.

 

Todo esto, siendo también una librería que permite serializar, de otros tipos de datos hacia nuestro schema con todas sus validaciones.

 

Ejemplo

 

```py
from marshmallow import Schema, ValidationError, fields, validate


class UsuarioSchema(Schema):
    nombre = fields.Str(required=True)
    edad = fields.Int(required=True, validate=validate.Range(min=0))


datos = {
    "nombre": "Esteban",
    "edad": -45,
}

try:
    usuario = UsuarioSchema().load(datos)
except ValidationError as e:
    print(e.messages)
```

 

Resultado

 

```
{'edad': ['Must be greater than or equal to 0.']}

```

 

¿Cuál utilizar?

 

Cada uno de los ejemplos cumple con funciones distintas, y cuál utilizar va a depender de la complejidad para validar los datos y la flexibilidad que se busque.

 

  • Dataclasses: Son útiles cuando no importa mucho la validación pero queremos tener una manera de agrupar datos relacionados dentro del sistema de una manera sencilla.
  • Pydantic: Es muy sencillo de usar y se integra muy bien con dataclasses ya existente. Para validaciones simples es perfecto.
  • Marshmallow: Para estructuras más complejas, mayor control sobre las restricciones, anidar schemas, y una organización más controlada de los datos. Además de incluir serialización de JSON directamente.

 

Te recomendamos en video