Cómo utilizar columnas JSON con SQLModel
Estoy tratando de definir una columna JSON a través de SQLModel:
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine, JSON
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
meta: JSON
El código es de SQLModel, pero extendido por el atributo “meta”.
Usando el código anterior junto con el resto del código de ejemplo (configurando sqlite, agregando datos), obtengo el siguiente error:
RuntimeError: no se encontró un validador para <class 'sqlalchemy.sql.sqltypes.json'="">, consulta `arbitrary_types_allowed` en Config
Intenté extender el código de la siguiente manera:
class Hero(SQLModel, table=True):
[...]
meta: JSON
@validator('meta')
def validate_json(v):
return v
class Config:
arbitrary_types_allowed = True
Pero esto da lugar a otro error:
sqlalchemy.exc.CompileError: (en la tabla 'hero', columna 'meta'): No se puede generar DDL para NullType (); ¿olvidaste especificar un tipo en esta columna?
Lo intenté usando solo SQLAlchemy y ha funcionado.
Entonces, ¿alguna idea de cómo puedo hacer la “conexión” entre SQLModel y SQLAlchemy para el campo JSON?
Actualización: También probé estableciéndolo como opcional y darle un valor predeterminado. Sin éxito (2º error nuevamente):
class Hero(SQLModel, table=True):
[...]
meta: Optional[JSON] = {}
class Config:
arbitrary_types_allowed = True
Pequeña pista: Aunque el JSON
se importa desde SQLModel
, finalmente se importa desde SQLAlchemy.sqltypes
sin ningún cambio.
davy.ai
Parece que el problema está relacionado con el hecho de que SQLModel utiliza SQLAlchemy en su interior para operaciones de base de datos, pero el tipo de datos JSON no es completamente compatible con la autogeneración de DDL (Lenguaje de Definición de Datos) de SQLAlchemy.
Para resolver este problema, puedes definir explícitamente el tipo de datos para la columna
meta
comoJSONB
, que es un tipo de datos JSON compatible con PostgreSQL y SQLite (asumiendo que estás utilizando una de estas bases de datos).Aquí tienes el código modificado que debería funcionar:
En este código modificado, definimos
meta_column
como un objetoColumn
con un tipo de datosJSON
, y luego especificamos que el atributometa
del modeloHero
es opcional y tiene un valor predeterminado de un diccionario vacío ({}
).Al crear una nueva instancia de
Hero
, ahora podemos pasar un diccionario para el atributometa
y se almacenará como datos JSON en la columnameta_column
de la base de datos.Ten en cuenta que también necesitamos usar la función
declarative_base()
de SQLAlchemy para crear una clase base de la cual hereda nuestro modeloHero
.¡Espero que esto ayude! Avísame si tienes alguna pregunta adicional.