如何创建一个非持久的Elixir/SQLAlchemy对象?
因为有一些旧的数据不在数据库里,而是在一些外部文件中,我想创建一个SQLAlchemy对象,这个对象包含从外部文件读取的数据,但如果我执行session.flush()
时,这些数据不应该被写入数据库。
我的代码是这样的:
try:
return session.query(Phone).populate_existing().filter(Phone.mac == ident).one()
except:
return self.createMockPhoneFromLicenseFile(ident)
def createMockPhoneFromLicenseFile(self, ident):
# Some code to read necessary data from file deleted....
phone = Phone()
phone.mac = foo
phone.data = bar
phone.state = "Read from legacy file"
phone.purchaseOrderPosition = self.getLegacyOrder(ident)
# SQLAlchemy magic doesn't seem to work here, probably because we don't insert the created
# phone object into the database. So we set the id fields manually.
phone.order_id = phone.purchaseOrderPosition.order_id
phone.order_position_id = phone.purchaseOrderPosition.order_position_id
return phone
一切运行得很好,除了在应用程序后面执行session.flush()
时,SQLAlchemy试图把创建的Phone对象写入数据库(幸运的是,这个操作没有成功,因为phone.state的长度超过了数据类型的限制),这导致了执行flush的功能出现问题。
有没有办法阻止SQLAlchemy尝试写入这样的对象呢?
更新
虽然我在
using_mapper_options(save_on_init=False)
的Elixir文档中没有找到任何相关信息(也许你能提供一个链接?),但我觉得尝试一下是值得的(我更希望能找到一种方法,只阻止单个实例被写入,而不是整个实体)。
起初我觉得这个语句没有效果,我怀疑我的SQLAlchemy/Elixir版本太旧了,但后来我发现与PurchaseOrderPosition实体的连接(我没有修改过)导致了
phone.purchaseOrderPosition = self.getLegacyOrder(ident)
phone对象再次被写入。如果我去掉这个语句,一切似乎都正常。
3 个回答
这是一篇旧帖子,但我遇到了类似的问题。在我的情况下,这个问题是在使用sqlalchemy时,由于反向引用的级联设置导致的:
http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html#backref-cascade
你可以关闭反向引用的级联功能,这样你就需要手动将内容添加到会话中。
其实,sqlalchemy 默认情况下并不会这样做。
看看下面这个完整的示例代码。
from sqlalchemy import Column, Integer, Unicode, create_engine
from sqlalchemy.orm import create_session
from sqlalchemy.ext.declarative import declarative_base
e = create_engine('sqlite://')
Base = declarative_base(bind=e)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Unicode(50))
# create the empty table and a session
Base.metadata.create_all()
s = create_session(bind=e, autoflush=False, autocommit=False)
# assert the table is empty
assert s.query(User).all() == []
# create a new User instance but don't save it to database:
u = User()
u.name = 'siebert'
# I could run s.add(u) here but I won't
s.flush()
s.commit()
# assert the table is still empty
assert s.query(User).all() == []
所以我不太明白是什么让你的实例自动加入到会话中。通常情况下,你需要手动调用 s.add(u)
才能把它放进会话里。我对 elixir 不太熟悉,也许这其中有什么 elixir 的技巧……也许你可以通过使用 session.expunge()
来把它从会话中移除。
你需要做
import elixir
elixir.options_defaults['mapper_options'] = { 'save_on_init': False }
这样可以防止你创建的Entity
实例自动被加入到会话中。理想情况下,这个操作应该尽早在你的代码中完成。你也可以针对每个实体单独设置,通过using_mapper_options(save_on_init=False)
来实现——想了解更多细节,可以查看Elixir的文档。
更新:
可以查看这个帖子,它在Elixir的邮件列表上说明了这是解决方案。
另外,正如Ants Aasma所指出的,你可以在Elixir关系中使用级联选项,以在SQLAlchemy中设置级联选项。想了解更多信息,可以查看这个页面。