Python dicts in sqlalchemy

Question:

I would like to load/save a dict to/from my sqlite DB, but am having some problems figuring out a simple way to do it. I don’t really need to be able to filter, etc., based on the contents so a simple conversion to/from string is fine.

The next-best thing would be foreign keys. Please don’t post links to huge examples, my head would explode if I ever set eyes on any those.

Asked By: Jonas Byström

||

Answers:

If you need to map a 1-N relation and map it as dict rather then list, then read Custom Dictionary-Based Collections

But if you mean a field, then what you can do it to have a DB field of type string, which is mapped to your Python object. But on the same python object you provide a property which will be kind-of proxy for this mapped string field of type dict().
Code example (not tested):

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)
Answered By: van

The SQLAlchemy PickleType is meant exactly for this.

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()

You can change the serialization mechanism by changing out the pickler with something else that has dumps() and loads() methods. The underlying storage mechanism by subclassing PickleType and overriding the impl attritbute:

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))
Answered By: Ants Aasma

You can create a custom type by subclassing sqlalchemy.types.TypeDecorator to handle serialization and deserialization to Text.

An implementation might look like

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

Example usage:

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

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

This is outlined in an example in the SQLAlchemy docs, which also shows how to track mutations of that dictionary.

This approach should work for all versions of Python, whereas simply passing json as the value to the pickler argument of PickleType will not work correctly, as AlexGrönholm points out in his comment on another answer.

Answered By: karlson

SQLAlchemy has a built-in JSON type that you can use:

attributes = Column(JSON)
Answered By: f.ashouri

You can simply save() method to save dicts in sqlalchemy

For example

class SomeModel(Base):
    __tablename__ = 'the_table'
    id = Column(Integer, primary_key=True)
    baked = Column(String, nullable=True)
    spam = Column(String, nullable=True)

s = {'baked': 'beans', 'spam': 'ham'})
SomeModel(**s).save()
Answered By: sagnik80
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.