2009-09-04 4 views
21

Я хотел бы загрузить/сохранить dict в/из моей базы данных sqlite, но у меня возникли проблемы с выяснением простого способа сделать это. На самом деле мне не нужно фильтровать и т. Д. На основе содержимого, поэтому простое преобразование в/из строки в порядке.Python dicts in sqlalchemy

Следующее, что будет, это внешние ключи. Пожалуйста, не публикуйте ссылки на огромные примеры, моя голова взорвется, если я когда-нибудь посмотрю на них.

ответ

4

Вы можете создать собственный тип, подклассов sqlalchemy.types.TypeDecorator для обработки сериализации и десериализации в текст.

Реализация может выглядеть

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 

Пример использования:

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

Это описано в an example in the SQLAlchemy docs, который также показывает, как отслеживать мутации этого словаря.

Этот подход должен работать для всех версий Python, а просто передавая json в качестве значения к pickler аргумента PickleType не будет работать правильно, так как AlexGrönholm points out в своем комментарии на другой ответ.

+0

Ваш ответ не является полным, поэтому его нельзя принять. В нем также есть ссылки на «принятый ответ», который отклоняет любую попытку и меняет это конкретное состояние на любой ответ. Пожалуйста, начните с правильного ответа, и (если вы должны) включить то, что другие сделали неправильно в конце. –

+0

@ JonasByström справедливо, я удалил ссылку на принятый в настоящее время ответ и немного расширил пример – karlson

+0

Это сработало, когда я его впервые установил. Однако после добавления нового поля в мою модель ('product_count = db.Column (db.Integer)') он дает мне следующую ошибку при переносе изменений в mysql: 'NameError: name 'TextPickleType' не определен' – iamlolz

3

Если необходимо отобразить 1-N соотношение и отобразить его в dict, а затем list, а затем прочитать Dictionary Based Collections

Но если вы имеете в виду поле, то, что вы можете сделать это, чтобы иметь DB поля типа string, которое отображается на ваш объект Python. Но на одном и том же объекте python вы предоставляете свойство, которое будет своеобразным прокси для этого отображаемого строкового поля типа dict(). пример кода (не проверено):

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! Прекрасно! :) –

+1

Обратите внимание, что SQLAlchemy не увидит, что вы модифицируете содержимое dict, поэтому любые изменения, внесенные вами в dict после того, как они были установлены, не будут сохраняться. – Simon

48

SQLAlchemy PickleType предназначен именно для этого.

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

Вы можете изменить механизм сериализации путем изменения вне Pickler с чем-то еще, что имеет dumps() и loads() методы. Механизм, лежащий хранение подклассов PickleType и перекрывая осущ 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)) 
+0

+1: очень приятно действительно – van

+0

Lovely! Я попробую это в первую очередь в понедельник. –

+0

Я, вероятно, что-то серьезно ошибаюсь, когда я пытаюсь self._properties [k] = v? Я получаю объект TypeError: «Column» не поддерживает назначение элемента ». –

Смежные вопросы