【问题标题】:Python dicts in sqlalchemysqlalchemy 中的 Python 字典
【发布时间】:2010-11-25 14:04:25
【问题描述】:

我想在我的 sqlite 数据库中加载/保存一个字典,但是在想出一个简单的方法来做这件事时遇到了一些问题。我真的不需要能够根据内容进行过滤等,因此简单的字符串转换就可以了。

第二好的东西是外键。请不要发布指向大型示例的链接,如果我看到这些示例,我的头会爆炸。

【问题讨论】:

    标签: python sql sqlite sqlalchemy


    【解决方案1】:

    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 并覆盖 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))
    

    【讨论】:

    • 可爱!我会在星期一的第一件事上试一试。
    • 当我尝试 self._properties[k] = v?我得到“TypeError:'Column'对象不支持项目分配”。
    • 我的示例是使用声明性语法一次性定义表和映射器。您遇到的错误听起来像是您没有为您的类使用声明性基类。为此,请在类定义之前添加 Base = sqlalchemy.ext.declarative.declarative_base()。
    • 从 SQLAlchemy 0.7 版开始,PickleType 上不再有 mutable 参数。出于性能原因,这已被删除,而是引入了新的可变性策略。请参阅(在 0.9 版文档中)docs.sqlalchemy.org/en/rel_0_9/orm/extensions/mutable.html
    • 在 Python 3.6 上工作,并且不应该在早期版本上工作。 Pickle 的 dumps() 与 json 的 dumps() 不兼容,因为前者采用协议参数,而后者没有。在 3.5 和更早的 Python 上,这将导致 skip_keys 的值设置为协议号。
    【解决方案2】:

    您可以通过将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 = SomeModel(json_field={'baked': 'beans', 'spam': 'ham'})
    session.add(s)
    session.commit()
    

    an example in the SQLAlchemy docs 中对此进行了概述,其中还显示了如何跟踪该字典的突变。

    这种方法应该适用于所有版本的 Python,而简单地将 json 作为值传递给 PickleTypepickler 参数将无法正常工作,就像 AlexGrönholm points out 在他对另一个答案的评论中一样。

    【讨论】:

    • 您的回答不完整,无法接受。它还提到了“接受的答案”,它会改变任何尝试并改变任何答案的特定状态。请以正确的答案开头,并且(如果必须)在最后包括其他人做错的事情。
    • @JonasByström 很公平,我已经删除了对当前接受的答案的引用并稍微扩展了示例
    • 这在我第一次设置时有效。然而,在我的模型中添加一个新字段(product_count = db.Column(db.Integer))后,将更改迁移到 mysql 时出现以下错误:NameError: name 'TextPickleType' is not defined
    • 假设您正在使用 alembic 进行迁移,您可能必须在迁移脚本中导入该类型。很难说。也许问一个新问题?
    【解决方案3】:

    SQLAlchemy 有一个可以使用的内置 JSON 类型:

    attributes = Column(JSON)
    

    【讨论】:

      【解决方案4】:

      如果您需要映射 1-N 关系并将其映射为 dict 而不是 list,请阅读 Custom Dictionary-Based Collections

      但是如果你的意思是一个字段,那么你可以做什么来拥有一个字符串类型的数据库字段,它被映射到你的 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)
      

      【讨论】:

      • 请注意,SQLAlchemy 不会看到您修改 dict 的内容,因此您对 dict 设置后所做的任何更改都不会保留。
      • 您的第一个链接不再有效。
      【解决方案5】:

      你可以简单的 save() 方法在 sqlalchemy 中保存字典

      例如

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

      【讨论】:

        猜你喜欢
        • 2010-10-21
        • 1970-01-01
        • 2017-12-09
        • 2021-04-26
        • 2012-01-22
        • 1970-01-01
        • 2018-09-28
        • 1970-01-01
        • 2010-12-29
        相关资源
        最近更新 更多