2009-09-04 11 views
21

Me gustaría cargar/guardar un dict a/desde mi base de datos sqlite, pero estoy teniendo algunos problemas para encontrar una forma sencilla de hacerlo. Realmente no necesito poder filtrar, etc., en función de los contenidos, por lo que una conversión simple a/desde cadena está bien.Discos de Python en sqlalchemy

La siguiente mejor opción serían las claves externas. Por favor, no publique enlaces a grandes ejemplos, mi cabeza explotaría si alguna vez los puse en esos.

Respuesta

4

Usted puede crear un tipo personalizado subclasificando sqlalchemy.types.TypeDecorator para manejar la serialización y deserialización a texto.

Una implementación podría ser similar

import json 
import sqlalchemy 
from sqlalchemy.types import TypeDecorator 

SIZE = 256 

class TextPickleType(TypeDecorator): 

    impl = sqlalchemy.Text(SIZE) 

    def process_bind_param(self, value, dialect): 
     if value is not None: 
      value = json.dumps(value) 

     return value 

    def process_result_value(self, value, dialect): 
     if value is not None: 
      value = json.loads(value) 
     return value 

Ejemplo de uso:

class SomeModel(Base): 
    __tablename__ = 'the_table' 
    id = Column(Integer, primary_key=True) 
    json_field = Column(TextPickleType()) 

s = SomeEntity(json_field={'baked': 'beans', 'spam': 'ham'}) 
session.add(s) 
session.commit() 

Esto se describe en an example in the SQLAlchemy docs, que también muestra cómo realizar un seguimiento mutaciones de ese diccionario.

Este enfoque debería funcionar para todas las versiones de Python, mientras que simplemente pasando json como el valor para el argumento de picklerPickleType no funcionará correctamente, como AlexGrönholm points out en su comentario sobre otra respuesta.

+0

Su respuesta no está completa, por lo tanto, no puedo aceptarla. También tiene referencias a "la respuesta aceptada" que desvía cualquier intento y cambia ese estado particular en cualquier respuesta. Por favor, comience con una respuesta correcta y (si debe) incluya lo que otros han hecho mal al final. –

+0

@ JonasByström, eliminé la referencia a la respuesta actualmente aceptada y extendí el ejemplo un poco – karlson

+0

Esto funcionó cuando lo configuré por primera vez. Sin embargo, después de agregar un nuevo campo a mi modelo ('product_count = db.Column (db.Integer)') me da el siguiente error al migrar los cambios a mysql: 'NameError: name 'TextPickleType' no está definido' – iamlolz

3

Si necesita asignar una 1-N relación y asignarla como dict en lugar de list, a continuación, lea Dictionary Based Collections

Pero si se refiere a un campo, entonces, ¿qué se puede hacer para tener una El campo DB de tipo cadena, que se asigna a su objeto Python. Pero en el mismo objeto python usted proporciona una propiedad que será tipo de proxy para este campo de cadena mapeado de tipo dict(). ejemplo de código (no probado):

class MyObject(object): 
    # fields (mapped automatically by sqlalchemy using mapper(...) 
    MyFieldAsString = None 

    def _get_MyFieldAsDict(self): 
     if self.MyFieldAsString: 
      return eval(self.MyFieldAsString) 
     else: 
      return {} # be careful with None and empty dict 

    def _set_MyFieldAsDict(self, value): 
     if value: 
      self.MyFieldAsString = str(value) 
     else: 
      self.MyFieldAsString = None 

    MyFieldAsDict = property(_get_MyFieldAsDict, _set_MyFieldAsDict) 
+0

¡Ahhh! ¡Espléndido! :) –

+1

Tenga en cuenta que SQLAlchemy no lo verá modificando el contenido del dict, por lo que cualquier cambio que realice en un dict una vez que se haya establecido no se mantendrá. – Simon

48

El SQLAlchemy PickleType se entiende exactamente por esto.

class SomeEntity(Base): 
    __tablename__ = 'some_entity' 
    id = Column(Integer, primary_key=True) 
    attributes = Column(PickleType) 

# Just set the attribute to save it 
s = SomeEntity(attributes={'baked': 'beans', 'spam': 'ham'}) 
session.add(s) 
session.commit() 

# If mutable=True on PickleType (the default) SQLAlchemy automatically 
# notices modifications. 
s.attributes['parrot'] = 'dead' 
session.commit() 

Puede cambiar el mecanismo de serialización cambiando el pickler con otra cosa que tiene dumps() y loads() métodos. El mecanismo de almacenamiento subyacente subclasificando PickleType y anulando el attritbute impl:

class TextPickleType(PickleType): 
    impl = Text 

import json 
class SomeOtherEntity(Base): 
    __tablename__ = 'some_other_entity' 
    id = Column(Integer, primary_key=True) 
    attributes = Column(TextPickleType(pickler=json)) 
+0

+1: muy agradable de hecho – van

+0

¡Encantador! Voy a probarlo a primera hora del lunes. –

+0

Probablemente estoy haciendo algo muy mal cuando intento self._properties [k] = v? Obtengo el objeto "TypeError: 'Column' no admite la asignación de elementos". –