如何创建非持久 Elixir/SQLAlchemy 对象?

发布于 2024-09-01 03:54:25 字数 1453 浏览 4 评论 0原文

由于遗留数据在数据库中不可用,但在某些外部文件中不可用,我想创建一个 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比数据类型允许的长度要长),这会破坏发出刷新的函数。

有什么方法可以阻止 SQLAlchemy 尝试写入这样的对象吗?

更新

虽然我在 Elixir 文档中没有找到任何内容

using_mapper_options(save_on_init=False)

(也许你可以提供一个链接?),但在我看来值得一试(我更喜欢一种方法来防止单个实例被写入而不是整个实体)。

起初,该语句似乎没有效果,我怀疑我的 SQLAlchemy/Elixir 版本太旧,但后来我发现与 PurchaseOrderPosition 实体(我没有修改)的连接导致

phone.purchaseOrderPosition = self.getLegacyOrder(ident) 

电话对象重新写一下。如果我删除该声明,一切似乎都很好。

Because of legacy data which is not available in the database but some external files, I want to create a SQLAlchemy object which contains data read from the external files, but isn't written to the database if I execute session.flush()

My code looks like this:

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 

Everything works fine except that on a session.flush() executed later in the application SQLAlchemy tries to write the created Phone object to the database (which fortunately doesn't succeed, because phone.state is longer than the data type allows), which breaks the function which issues the flush.

Is there any way to prevent SQLAlchemy from trying to write such an object?

Update

While I didn't find anything on

using_mapper_options(save_on_init=False)

in the Elixir documentation (maybe you can provide a link?), it seemed to me worth a try (I would have preferred a way to prevent a single instance from being written instead of the whole entity).

At first it seemed that the statement has no effect and I suspected my SQLAlchemy/Elixir versions to be too old, but then I found out that the connection to the PurchaseOrderPosition entity (which I didn't modify) made with

phone.purchaseOrderPosition = self.getLegacyOrder(ident) 

causes the phone object to be written again. If I remove the statement, everything seems to be fine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半步萧音过轻尘 2024-09-08 03:54:25

您需要采取

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 中设置级联选项。有关更多详细信息,请参阅此页面

You need to do

import elixir
elixir.options_defaults['mapper_options'] = { 'save_on_init': False }

to prevent Entity instances which you instantiate being auto-added to the session. Ideally, this should be done as early as possible in your code. You can also do this on a per-entity basis, via using_mapper_options(save_on_init=False) - see the Elixir documentation for more details.

Update:

See this post on the Elixir mailing list indicating that this is the solution.

Also, as Ants Aasma points out, you can use cascade options on the Elixir relationship to set up cascade options in SQLAlchemy. See this page for more details.

§普罗旺斯的薰衣草 2024-09-08 03:54:25

嗯,默认情况下 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()

Well, sqlalchemy doesn't, by default.

Consider the following self-contained example code.

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() == []

So I'm not sure what's implicity adding your instances to the session. Normally you have to manually call s.add(u) to make it go to the session. I'm not familiar with elixir so perhaps this is some elixir trickery... Maybe you could remove it from the session, by using session.expunge().

入画浅相思 2024-09-08 03:54:25

老帖子,但我遇到了类似的问题,就我而言,在 sqlalchemy 中,它是由反向引用级联引起的:

http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html#backref-cascade

在你的 backrefs 上关闭它,这样你就必须显式地将东西添加到会议

Old post but I came across a similar issue, in my case in sqlalchemy it was caused by cascading on backrefs:

http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html#backref-cascade

Turn it off on your backrefs so that you have to explicitly add things to the session

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文