【问题标题】:How to create a non-persistent Elixir/SQLAlchemy object?如何创建一个非持久的 Elixir/SQLAlchemy 对象?
【发布时间】:2011-02-17 18:34:29
【问题描述】:

由于遗留数据在数据库中不可用,但在一些外部文件中不可用,我想创建一个 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 尝试编写这样的对象?

更新

虽然我没有找到任何东西

using_mapper_options(save_on_init=False)

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

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

phone.purchaseOrderPosition = self.getLegacyOrder(ident) 

导致 phone 对象再次被写入。如果我删除该声明,一切似乎都很好。

【问题讨论】:

    标签: python sqlalchemy python-elixir


    【解决方案1】:

    你需要做的

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

    防止您实例化的Entity 实例自动添加到会话中。理想情况下,这应该尽早在您的代码中完成。您也可以通过using_mapper_options(save_on_init=False) 对每个实体执行此操作 - 有关详细信息,请参阅 Elixir 文档。

    更新:

    请参阅 Elixir 邮件列表中的 this post,表明这是解决方案。

    此外,正如 Ants Aasma 指出的那样,您可以在 Elixir 关系上使用级联选项来设置 SQLAlchemy 中的级联选项。详情请见this page

    【讨论】:

    • 请参阅我的问题中的更新部分,以获得我的 cmets 对您的回答。
    • 除了在对象创建时避免隐式 Session.add 之外,您还需要避免关系上的级联。如果 Elixir 没有记录它,请参阅 SQLAlchemy 文档中的级联文档,Elixir 应该仍然允许设置它。
    【解决方案2】:

    好吧,默认情况下 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) 才能使其进入会话。我对长生不老药不熟悉,所以也许这是一些长生不老药的诡计……也许您可以使用session.expunge()将其从会话中删除。

    【讨论】:

      【解决方案3】:

      旧帖子,但我遇到了类似的问题,在我的 sqlalchemy 中,它是由 backrefs 上的级联引起的:

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

      在你的反向引用上关闭它,这样你就必须明确地向会话中添加东西

      【讨论】:

        猜你喜欢
        • 2015-12-20
        • 2018-08-08
        • 1970-01-01
        • 2011-04-21
        • 1970-01-01
        • 2020-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多