【发布时间】:2010-11-25 14:04:25
【问题描述】:
我想在我的 sqlite 数据库中加载/保存一个字典,但是在想出一个简单的方法来做这件事时遇到了一些问题。我真的不需要能够根据内容进行过滤等,因此简单的字符串转换就可以了。
第二好的东西是外键。请不要发布指向大型示例的链接,如果我看到这些示例,我的头会爆炸。
【问题讨论】:
标签: python sql sqlite sqlalchemy
我想在我的 sqlite 数据库中加载/保存一个字典,但是在想出一个简单的方法来做这件事时遇到了一些问题。我真的不需要能够根据内容进行过滤等,因此简单的字符串转换就可以了。
第二好的东西是外键。请不要发布指向大型示例的链接,如果我看到这些示例,我的头会爆炸。
【问题讨论】:
标签: python sql sqlite sqlalchemy
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))
【讨论】:
mutable 参数。出于性能原因,这已被删除,而是引入了新的可变性策略。请参阅(在 0.9 版文档中)docs.sqlalchemy.org/en/rel_0_9/orm/extensions/mutable.html
dumps() 与 json 的 dumps() 不兼容,因为前者采用协议参数,而后者没有。在 3.5 和更早的 Python 上,这将导致 skip_keys 的值设置为协议号。
您可以通过将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 作为值传递给 PickleType 的 pickler 参数将无法正常工作,就像 AlexGrönholm points out 在他对另一个答案的评论中一样。
【讨论】:
product_count = db.Column(db.Integer))后,将更改迁移到 mysql 时出现以下错误:NameError: name 'TextPickleType' is not defined
SQLAlchemy 有一个可以使用的内置 JSON 类型:
attributes = Column(JSON)
【讨论】:
如果您需要映射 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)
【讨论】:
你可以简单的 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()
【讨论】: