es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Existe alguna forma en SQLAlchemy de crear un proxy de un campo JSON de pares clave/valor para que actúe como columnas individuales?

Mi situación es similar a la de los ejemplos de Mapeo de Atributos Verticales cubiertos en la documentación de SQLAlchemy. Sin embargo, los nombres de las columnas y los valores se almacenan en un campo JSON como un diccionario. Los tipos de cada columna se almacenan en otra tabla con otro campo JSON (clave/valor, la clave es el nombre del campo y el valor es el tipo de campo).

¿Es posible crear un proxy de asociación que trate el diccionario del campo ‘kwargs’ como columnas individuales y consultables?

class MainClass(db.Model):
    __tablename__ = "main_class"
    id = db.Column(db.Integer, primary_key=True)
    ...
    # {"nombre_campo1": "str", "nombre_campo2": "int", "nombre_campo3": "bool", "nombre_campo4": "list"}
    valid_kwargs = db.Column(db.JSON, nullable=False, default=dict())

class ChildClass(db.Model):
    __tablename__ = "child_class"
    id = db.Column(db.Integer, primary_key=True)
    main_class_id = db.Column(db.Integer, db.ForeignKey("main_class.id"))
    ...
    # {"nombre_campo1": "test", "nombre_campo2": 1, "nombre_campo3": False, "nombre_campo4": ["a", "b", "c"]}
    kwargs = db.Column(db.JSON, nullable=False, default=dict())
Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Sí, es posible crear un proxy de asociación que trate el diccionario del campo kwargs como columnas individuales consultables. Usando la extensión GenericAttributes de SQLAlchemy, podemos asignar dinámicamente los campos JSON a columnas en nuestro modelo.

    Aquí hay un ejemplo de implementación:

    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.ext.associationproxy import association_proxy
    from sqlalchemy.ext.mutable import MutableDict
    from sqlalchemy_utils import generic_relationship
    from sqlalchemy.orm.collections import attribute_mapped_collection
    
    Base = declarative_base()
    
    class Attribute(Base):
        __tablename__ = 'attributes'
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        type = db.Column(db.String)
        entity_type = db.Column(db.String)
    
    class MainClass(db.Model):
        __tablename__ = "main_class"
        id = db.Column(db.Integer, primary_key=True)
        valid_kwargs = db.Column(MutableDict.as_mutable(db.JSON), nullable=False, default={})
        attributes = generic_relationship('Attribute')
    
        def __getattr__(self, key):
            if key in self.valid_kwargs:
                return self.valid_kwargs[key]
            return super().__getattr__(key)
    
    class ChildClass(db.Model):
        __tablename__ = "child_class"
        id = db.Column(db.Integer, primary_key=True)
        main_class_id = db.Column(db.Integer, db.ForeignKey("main_class.id"))
        kwargs = db.Column(MutableDict.as_mutable(db.JSON), nullable=False, default={})
        attributes = generic_relationship('Attribute')
        proxy_kwargs = association_proxy('attributes', 'value', creator=lambda k, v: Attribute(name=k, value=v))
    
        def __getattr__(self, key):
            if key in self.kwargs:
                return self.kwargs[key]
            return super().__getattr__(key)
    

    En este ejemplo, estamos utilizando el tipo MutableDict de SQLAlchemy para crear un campo JSON que se puede actualizar directamente en el modelo. También estamos utilizando la extensión GenericAttributes para asignar dinámicamente los campos JSON a columnas en nuestro modelo.

    La clase Attribute asigna los nombres y tipos de columnas a un “entity_type” (ya sea “main_class” o “child_class”), lo que nos permite tener diferentes tipos de columnas para cada tabla.

    Las clases MainClass y ChildClass definen métodos __getattr__ que devuelven el valor adecuado del campo JSON si el atributo no está definido en la clase.

    El método association_proxy crea un proxy que nos permite tratar el diccionario JSON como columnas individuales que se pueden consultar directamente.

    En general, esta implementación nos permite almacenar y consultar datos JSON como si fueran una columna regular en nuestro modelo.

Comments are closed.