Uno de los principales atractivos de bases de datos de documentos es la flexibilidad de la estructura o esquema del documento. En la mayoría de los casos, la flexibilidad del esquema del documento es beneficiosa. Sin embargo, puede haber algunas situaciones en las que tener alguna estructura en el documento puede ser útil. Este artículo muestra cómo validar tus documentos JSON contra un esquema especificado usando la popular librería Python pydantic.
¿Cuándo hay que validar los documentos?
Un error común sobre el uso de bases de datos NoSQL es que no se requieren estructuras o esquemas de documentos. En la mayoría de los casos, las aplicaciones suelen tener algunas restricciones para los datos, aunque no las validen específicamente. Por ejemplo, puede haber algunos campos en el documento de los que la aplicación depende para su funcionalidad. Una aplicación podría simplemente no funcionar correctamente si faltan algunos de estos campos JSON pídanticos.
Un ejemplo real de este problema podría ser una aplicación que lee datos de otra aplicación poco fiable que envía periódicamente datos erróneos. Sería prudente resaltar los documentos que podrían romper la aplicación en tales casos. Los desarrolladores pueden hacer esto en la aplicación o a nivel de documento.
En este enfoque, especificamos un esquema de JSON a pydantic para los documentos con el fin de ayudar a identificar aquellos que no coinciden con las especificaciones de la aplicación a nivel de documento.
Generar datos de prueba JSON
Utilizamos la biblioteca de código abierto, Farsantepara generar algunos perfiles de usuario falsos para este tutorial.
Esta es la estructura de un documento único:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "trabajo": "Radiógrafo, diagnóstico", "empresa": "Klein, Dillon y Neal", "residencia": "2232 Jackson Forks - Lake Teresa, CO 46959", "sitio web": [ "http://bishop-torres.net/" ], "nombre de usuario": "aabbott", "nombre": "Madison Mitchell", "dirección": "1782 Moore Hill Apt. 717\nWest Stephaniestad, NM 75293", "mail": "amberrodriguez@hotmail.com", "teléfono": { "home": "677-197-4239x889", "móvil": "001-594-038-9255x99138" } } |
Para simular los documentos rotos, modifico una pequeña parte de los perfiles de usuario eliminando algunos de los campos de teléfono móvil y correo. Nuestro objetivo es identificar estos registros que, en el mundo real, se almacenarían en una base de datos de documentos como Couchbase.
Cargo los datos pydantic generados a partir de JSON en un bucket de nuestro hosted Couchbase Capella clúster utilizando el importar en la interfaz de usuario de la consola web integrada para nuestras pruebas. Especifico el nombre de usuario como clave para identificar unívocamente cada documento.
¿Cómo especificar un esquema para documentos JSON?
En los datos del perfil de usuario, espero que los documentos se ajusten a la siguiente estructura en mis aplicaciones:
- Campos obligatorios:
- nombre de usuario
- nombre
- phone - con elementos JSON para "home" y "mobile".
- correo
- sitio web - una matriz
- Campos opcionales:
- empresa
- residencia
- empleo
- dirección
Pydantic es una de las bibliotecas más populares en Python para la validación de datos. La sintaxis para especificar el esquema es similar a usar sugerencias de tipo para funciones en Python. Los desarrolladores pueden especificar el esquema definiendo un modelo. Pydantic tiene un rico conjunto de características para hacer una variedad de validaciones JSON. Vamos a caminar a través de la representación de algunas especificaciones de documentos de perfil de usuario.
Una cosa a tener en cuenta sobre pydantic es que, por defecto, intenta coaccionar los tipos de datos haciendo conversiones de tipo cuando es posible - por ejemplo, convertir la cadena '1' en un numérico 1. Sin embargo, existe una opción para activar la comprobación estricta de tipos sin realizar conversiones.
En este ejemplo de código, puedes ver una configuración básica para el esquema UserProfile utilizando sintaxis pydantic:
1 2 3 4 5 6 7 8 9 10 11 |
clase Perfil de usuario(ModeloBase): """Esquema para perfiles de usuario""" nombre de usuario: str nombre: str teléfono: Teléfono correo: str empresa: Opcional[str] residencia: Opcional[str] sitio web: Lista[HttpUrl] empleo: Opcional[str] dirección: Opcional[str] |
Cada campo se especifica junto con el tipo de datos previsto. Los campos opcionales se marcan como Opcional. Una matriz se especifica mediante el parámetro Lista seguida del tipo de datos deseado.
En nombre de usuario debe ser una cadena, mientras que el campo empresa es una cadena opcional. Si nos fijamos en las otras líneas del fragmento de código, veremos que el campo sitio web es una lista de tipo HttpUrl - uno de los muchos tipos personalizados proporcionados por pydantic. HttpUrl se utiliza para validar que la URL es válida y no cadenas aleatorias. Del mismo modo, hay otros campos como correos electrónicos que podríamos utilizar para asegurarnos de que los campos de correo electrónico son un formulario válido.
Si nos fijamos en el teléfono se marca como Teléfono que es un tipo personalizado que definiremos en el siguiente fragmento de código:
1 2 3 4 5 6 7 8 9 10 11 |
clase Teléfono(ModeloBase): """Esquema para números de teléfono""" Inicio: str móvil: str @validador("móvil", "home") def does_not_contain_extension(cls, v): """Comprueba si los números de teléfono contienen extensiones"""" si "x" en v: subir Error de extensión(valor_incorrecto=v) devolver v |
Aquí especificamos que el Teléfono se compone de dos campos que son cadenas: Inicio y móvil. Esto se comprobaría dentro del Perfil de usuario modelo e interpretado como el Perfil de usuario que contiene un teléfono que contiene Inicio y móvil campos.
Pydantic también nos permite validar la contenidos de los datos y el tipo y presencia de los mismos. Puede hacerlo definiendo funciones que validen campos específicos. En el ejemplo anterior, validamos el campo móvil y Inicio para comprobar si contienen extensiones. Si contienen una extensión, no la soportamos y lanzamos un error personalizado. Estos errores de esquema se muestran a los usuarios que realizan la validación pydantic.
Puede ver la definición del esquema especificando la opción Modelo.schema_json() como se muestra aquí:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
{ "título: "Perfil de usuario", "descripción": "Esquema para perfiles de usuario", "tipo": "objeto", "propiedades": { "nombre de usuario": { "título: "Nombre de usuario", "tipo": "cadena" }, "nombre": { "título: "Nombre", "tipo": "cadena" }, "teléfono": { "$ref": "#/definiciones/Teléfono" }, "mail": { "título: "Correo", "tipo": "cadena" }, "empresa": { "título: "Empresa", "tipo": "cadena" }, "residencia": { "título: "Residencia", "tipo": "cadena" }, "sitio web": { "título: "Página web", "tipo": "array", "artículos": { "tipo": "cadena", "Longitud mínima: 1, "maxLength": 2083, "formato": "uri" } }, "trabajo": { "título: "Trabajo", "tipo": "cadena" }, "dirección": { "título: "Dirección", "tipo": "cadena" } }, "requerido": [ "nombre de usuario", "nombre", "teléfono", "mail", "sitio web" ], "definiciones": { "Teléfono": { "título: "Teléfono", "descripción": "Esquema para números de teléfono", "tipo": "objeto", "propiedades": { "home": { "título: "Inicio", "tipo": "cadena" }, "móvil": { "título: "Móvil", "tipo": "cadena" } }, "requerido": [ "home", "móvil" ] } } } |
Validación de documentos JSON con respecto a un esquema pydantic de Python
Ahora que hemos definido el esquema, vamos a explorar cómo podemos validar los documentos contra el esquema.
La validación puede realizarse utilizando la pydantic parse_obj del modelo. Se especifica el documento como un diccionario y se comprueba si hay excepciones de validación. En este caso, obtenemos todos los documentos (hasta el límite especificado) utilizando una consulta Couchbase y los comprobamos uno a uno e informamos de cualquier error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
autentificador_contraseña = PasswordAuthenticator(DB_USUARIO, DB_PWD) grupo = Grupo( CONN_STR, ClusterOptions(autentificador_contraseña), ) validation_error_count = 0 consulta = f"select perfil.* from `{BUCKET}`.{SCOPE}.{COLLECTION} perfil LIMIT {DOCUMENT_LIMIT}" pruebe: resultados = grupo.consulta(consulta) para fila en resultados: pruebe: Perfil de usuario.parse_obj(fila) validation_error_count += 1 excepto ValidationError como e: imprimir(f"Error encontrado en el documento: {row['username']}", e.json()) excepto Excepción como e: imprimir(e) imprimir(f"Recuento de errores de validación: {validation_error_count}") |
El esquema destaca algunos de los errores observados en los documentos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Error encontrado en documento: aarias [ { "loc": [ "teléfono", "home" ], "msg": "el valor es una extensión, tiene \"(932)532-4001x319\"", "tipo": "valor_error.extension", "ctx": { "valor_equivocado": "(932)532-4001x319" } }, { "loc": [ "teléfono", "móvil" ], "msg": "campo obligatorio", "tipo": "error_valor.missing" } ] |
El documento con el ID aarias tiene extensiones en el campo de origen, y el teléfono → móvil también falta.
Resumen
Este ejemplo de perfil de usuario muestra cómo podemos crear fácilmente esquemas personalizados para nuestros documentos JSON. Este post también muestra cómo utilizar las capacidades de prueba y validación de Python y el módulo pydantic. Es sólo la punta del iceberg, aunque hay muchos más tipos que podamos validar.
También son posibles otros enfoques; por ejemplo, puedes validar los datos en el momento de escribirlos. Esto se puede hacer muy fácilmente integrando el esquema que hemos definido aquí con la aplicación y verificando los datos antes de insertarlos/upsertarlos en Couchbase.
El código completo de esta demostración se encuentra en Github. Allí también se encuentran las instrucciones para generar los datos y ejecutar los scripts.
Recursos
- Validación del esquema JSON - Repositorio GitHub para el código de este post
- Couchbase Capella – DBaaS NoSQL totalmente alojada
- Módulo pydantic de Python
- Módulo Python Faker