Cree tipos de datos personalizados usando el módulo Pydantic en Python

Muchas veces, encontramos que necesitamos pasar una larga lista de variables a una función, y especificar todo eso en la firma de la función de Python puede ser un poco complicado. Nuevamente, también surgen problemas cuando desea algún tipo de validación de las variables pasadas. Para una larga lista de variables, es realmente difícil seguir validando los datos dentro del cuerpo de la función principal, además, no es una buena práctica. En ese escenario, lo que desea hacer es separar y segregar sus variables en diferentes clases. Aquí, vamos a demostrar cómo puede usar pydantic para crear modelos junto con sus validaciones personalizadas. Primero, analicemos el caso de uso.

Considere, estamos recibiendo algunos datos de una llamada API, y necesitamos hacer algún tipo de análisis al respecto. Por lo general, una respuesta de API enviará la respuesta en forma de JSON, por lo que nos gustaría que nuestros modelos pudieran serializar y deserializar JSON (1) .

Además, asumiríamos tipos de ciertas variables. Por ejemplo, si estamos pasando una dirección, asumiríamos que el código PIN es un valor entero. Esta es la verificación de tipo (2) .

Para realizar el análisis, haría algunas suposiciones sobre los datos, como, por ejemplo, el código PIN debe coincidir con el nombre del distrito proporcionado. Esta es la validación (3) .

También podemos suponer que para ciertos campos como estados, debería estar dentro de una lista de estados, digamos en India, y no cualquier valor arbitrario aleatorio. Esto cae dentro de la limpieza (4) .

Entonces, con estos cuatro requisitos, comencemos a codificar el modo. Asumiría que tiene Python instalado en su sistema. Para instalar pydantic simplemente ejecute,

pip install pydantic

Con ese conjunto, cree un archivo llamado models.py y pegue el siguiente código en él. Hemos agregado comentarios detallados en línea en el propio código para que sea más fácil de entender directamente.

Python3

# import required modules
from enum import Enum
from typing import Optional
from pydantic import BaseModel, PositiveInt, validator, root_validator, constr
 
 
 
# custom class used as choices for state
# pydantic choices using the built-in Enum of python
# which reduces the need for additional packages
class StateTypes(str, Enum):
 
    DELHI = "DLH"
    UTTAR_PRADESH = "UP"
    BENGALURU = "BLR"
    WEST_BENGAL = "WB"
 
     
     
# class to get personal credentials
class PersonalDetails(BaseModel):
    id: int
     
    # constr gives us the ability to specify
    # the min and max length
    name: constr(min_length=2, max_length=15
    phone: PositiveInt
 
    # validation at field level
    @validator("phone")
     
    # get phone number
    def phone_length(cls, v):
         
        # phone number should typically be of length 10
        if len(str(v)) != 10:
            raise ValueError("Phone number must be of ten digits")
        return v
 
 
                  
# class to get address                
class Address(BaseModel):
    id: int
    address_line_1: constr(max_length=50)
     
    # assigning some fields to be optional
    address_line_2: Optional[constr(max_length=50)] = None
    pincode: PositiveInt
    city: constr(max_length=30)
     
    # using choices in python is this simple.
    # Just create a class with Enums as choices
    # and the pass the class as type for the field
    state: StateTypes
 
    @validator("pincode")
    def pincode_length(cls, v):
        if len(str(v)) != 6:
            raise ValueError("Pincode must be of six digits")
        return v
 
                  
 
# using BaseModels as custom datatypes
# in the User class                
class User(BaseModel):
     
    personal_details: PersonalDetails
    address: Address
 
    @root_validator(skip_on_failure=True)
     
    # skip_on_failure=True means it will skip the
    # validation for this class if it's custom
    # fields are not validated
    def check_id(cls, values):
         
        # custom validation ensuring personal_details.id is
        # same as address.id
        personal_details: PersonalDetails = values.get("personal_details")
        address: Address = values.get("address")
        if personal_details.id != address.id:
            raise ValueError(
                "ID field of both personal_details as well as address should match"
            )
        return values
 
                  
 
# Driver Code
if __name__ == "__main__":
 
# testing models                
    validated_data = {
        "personal_details": {
            "id": 1,
            "name": "GeeksforGeeks",
            "phone": 9999999999,
        },
        "address": {
            "id": 1,
            "address_line_1": "Sector- 136",
            "pincode": 201305,
            "city": "Noida",
            "state": "UP",
        },
    }
     
    # this would work without any error as
    # no validation will fail
    user = User(**validated_data)
     
    # would print the standard __str__ value for the model
    print(user)
 
    unvalidated_data = {
        "personal_details": {
            "id": 1,
            "name": "GeeksforGeeks",
            "phone": 9999999999,
        },
        "address": {
            "id": 2,
            "address_line_1": "Sector- 136",
            "pincode": 201305,
            "city": "Noida",
            "state": "UP",
        },
    }
     
    # this would raise a value error since the IDs
    # are different
    user = User(**unvalidated_data)
    print(user)

Producción:

Captura de pantalla de ejecución

Al ejecutar esto, la primera declaración de impresión se ejecutará con éxito, pero en la próxima inicialización del modelo de Usuario , arrojaría un Error de validación de tipo ValueError porque las ID de los detalles personales y la dirección no coinciden.

Publicación traducida automáticamente

Artículo escrito por debdutgoswami y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *