SQLAlchemy:排除列被腌制
SQLAlchemy: exclude a column from being pickled
是否可以在 SQLAlchemy 中将列标记为“请勿腌制”,以便在取消腌制后按需从数据库加载该列?
这可以通过 deferred
列部分实现,但如果随后加载该列,它也会被 pickled。
(我问的是,由于内置 buffer
的不可腌制性质,Python2.7 中酸洗 Geoalchemy2 几何列似乎仍然存在问题:https://github.com/geoalchemy/geoalchemy/issues/24 )
这是一个天真的 __getstate__
实现,它不起作用,但有望提示正确答案:
Base = declarative_base(metadata=route_metadata)
cols_to_omit = ['my_col_1']
class MyClass(Base):
my_col_0 = Column(Integer)
my_col_1 = Column(Integer)
def __getstate__(self):
return dict((c, getattr(self, c)) for c in \
self.__table__.columns.keys() \
if c not in cols_to_omit)
def __setstate__(self, *args):
for c, v in args[0].items():
setattr(self, c, v)
from pickle import *
pp = dumps(MyClass.query.first())
obj = loads(pp)
这给出 AttributeError: 'MyClass' object has no attribute '_sa_instance_state'
这有点复杂,因为你不能仅仅阻止列的 pickle,因为控制延迟加载的实际上是在 _sa_instance_state
属性中,所以即使你不 pickle 某些属性在您的实例字典中,SQLAlchemy 不知道这意味着它已过期。
一个(有点hacky的)解决方案是在你unpickle时让属性过期:
class Foo(Base):
__tablename__ = "foo"
id = Column(Integer, primary_key=True)
foo = Column(String)
bar = Column(String)
def __getstate__(self):
return {k: v for k, v in self.__dict__.items() if k != "bar"}
def __setstate__(self, d):
for k, v in d.items():
self.__dict__[k] = v
self._sa_instance_state._expire_attributes(self.__dict__, ("bar",))
注意unpickling的结果是一个detached instance,所以你需要注意如何使用它:
foo = session.query(Foo).first()
foo = pickle.loads(pickle.dumps(foo))
print(foo.foo) # fine
# print(foo.bar) # sqlalchemy.orm.exc.DetachedInstanceError
foo = session.merge(foo, load=False)
print(foo.foo) # no query
print(foo.bar) # causes a query
是否可以在 SQLAlchemy 中将列标记为“请勿腌制”,以便在取消腌制后按需从数据库加载该列?
这可以通过 deferred
列部分实现,但如果随后加载该列,它也会被 pickled。
(我问的是,由于内置 buffer
的不可腌制性质,Python2.7 中酸洗 Geoalchemy2 几何列似乎仍然存在问题:https://github.com/geoalchemy/geoalchemy/issues/24 )
这是一个天真的 __getstate__
实现,它不起作用,但有望提示正确答案:
Base = declarative_base(metadata=route_metadata)
cols_to_omit = ['my_col_1']
class MyClass(Base):
my_col_0 = Column(Integer)
my_col_1 = Column(Integer)
def __getstate__(self):
return dict((c, getattr(self, c)) for c in \
self.__table__.columns.keys() \
if c not in cols_to_omit)
def __setstate__(self, *args):
for c, v in args[0].items():
setattr(self, c, v)
from pickle import *
pp = dumps(MyClass.query.first())
obj = loads(pp)
这给出 AttributeError: 'MyClass' object has no attribute '_sa_instance_state'
这有点复杂,因为你不能仅仅阻止列的 pickle,因为控制延迟加载的实际上是在 _sa_instance_state
属性中,所以即使你不 pickle 某些属性在您的实例字典中,SQLAlchemy 不知道这意味着它已过期。
一个(有点hacky的)解决方案是在你unpickle时让属性过期:
class Foo(Base):
__tablename__ = "foo"
id = Column(Integer, primary_key=True)
foo = Column(String)
bar = Column(String)
def __getstate__(self):
return {k: v for k, v in self.__dict__.items() if k != "bar"}
def __setstate__(self, d):
for k, v in d.items():
self.__dict__[k] = v
self._sa_instance_state._expire_attributes(self.__dict__, ("bar",))
注意unpickling的结果是一个detached instance,所以你需要注意如何使用它:
foo = session.query(Foo).first()
foo = pickle.loads(pickle.dumps(foo))
print(foo.foo) # fine
# print(foo.bar) # sqlalchemy.orm.exc.DetachedInstanceError
foo = session.merge(foo, load=False)
print(foo.foo) # no query
print(foo.bar) # causes a query