Python 我可以让SQLAlchemy基于当前外键值填充关系吗?

Python 我可以让SQLAlchemy基于当前外键值填充关系吗?,python,sqlalchemy,Python,Sqlalchemy,下面是一些代码: # latest version at https://gist.github.com/nickretallack/11059102 from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, ForeignKey, Integer, String from sqlalchemy import create_engine from sqlalchemy.orm i

下面是一些代码:

# latest version at https://gist.github.com/nickretallack/11059102

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, relationship

Base = declarative_base()

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False, unique=True)

    def __str__(self):
        return self.name

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    parent_id = Column(ForeignKey(Parent.id), nullable=False)
    name = Column(String, nullable=False)

    parent = relationship(Parent)

engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)

def run():
    # Basic Setup
    Base.metadata.create_all(engine)
    session = Session()
    fred = Parent(name="Fred", id=1)
    george = Parent(name="George", id=2)
    session.add(fred, george)
    session.commit()

    # The test
    bob = Child(name="Bob", parent_id=1)
    print bob.parent, ": Out of session. Should be Fred but is None.\n"

    session.add(bob)
    print bob.parent, ": In session.  Should be Fred but is None.\n"

    session.commit()
    print bob.parent, ": Committed.  Is Fred.\n" 

    bob.parent_id = 2
    print bob.parent, ": Dirty.  Should be George but is Fred.\n"

    session.add(bob)
    print bob.parent, ": Added to session.  Should be George but is Fred.\n"

    session.expire(bob,['parent'])
    print bob.parent, ": Expired.  Should be George but is None?  Wtf?\n"

    session.commit()
    print bob.parent, ": Committed again.  Is None.  Ugh.\n"

if __name__ == '__main__':
    run()
这个示例演示了简单地设置关系所依赖的外键字段永远不足以使关系查询正确。无论我做什么,这种情况都会发生

是否可以让sqlalchemy基于当前外键值填充关系,而不首先持久化记录?我可以做些什么让它运行查询吗

这个问题在处理web表单时经常出现。表单post只包含事物的ID,因此处理post的最简单方法是在记录中设置ID字段并尝试提交它,如果引用的项不存在,或者如果存在只有数据库才能真正了解的其他问题而不冒竞争条件的风险,则让事务失败,例如,唯一的约束冲突。一旦事务失败,您可能希望向用户重新显示表单。不幸的是,这些关系都不再正确

这可能是个问题,也可能不是,但对我来说,这是相当令人沮丧的。为了纠正这些关系,我需要复制这些关系中的逻辑,因为我找不到方法告诉他们只执行查询

  • 您的“添加”呼叫错误:

     session.add_all([fred, george])
    
  • 对于一个甚至不在会话中的完全瞬态对象(顺便说一句,我不同意这个用例),使用:

  • 对于挂起对象加载其关系(也是我不同意的用例,请参阅) 使用标志:

  • 如常见问题解答条目所述,当您将“家长id”更改为某个内容时,要重新加载“家长”,请使用expire:

     session.expire(bob, ['parent'])
     bob.parent_id = 2
     print bob.parent, ": Dirty.  Should be George but is Fred.\n"
    
  • 脚本完全工作:

    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, ForeignKey, Integer, String
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker, relationship
    
    Base = declarative_base()
    
    class Parent(Base):
        __tablename__ = 'parent'
        id = Column(Integer, primary_key=True)
        name = Column(String, nullable=False, unique=True)
    
        def __str__(self):
            return self.name
    
    class Child(Base):
        __tablename__ = 'child'
        id = Column(Integer, primary_key=True)
        parent_id = Column(ForeignKey(Parent.id), nullable=False)
        name = Column(String, nullable=False)
    
        parent = relationship(Parent, load_on_pending=True)
    
    engine = create_engine('sqlite:///:memory:', echo=True)
    Session = sessionmaker(bind=engine)
    
    def run():
        # Basic Setup
        Base.metadata.create_all(engine)
        session = Session()
        fred = Parent(name="Fred", id=1)
        george = Parent(name="George", id=2)
        session.add_all([fred, george])
        session.commit()
    
        # The test
        bob = Child(name="Bob", parent_id=1)
        session.enable_relationship_loading(bob)
        print bob.parent, ": Out of session. Should be Fred but is None.\n"
    
        session.add(bob)
        print bob.parent, ": In session.  Should be Fred but is None.\n"
    
        session.commit()
        print bob.parent, ": Committed.  Is Fred.\n"
    
        session.expire(bob, ['parent'])
        bob.parent_id = 2
        print bob.parent, ": Dirty.  Should be George but is Fred.\n"
    
        session.add(bob)
        print bob.parent, ": Added to session.  Should be George but is Fred.\n"
    
        session.expire(bob,['parent'])
        print bob.parent, ": Expired.  Should be George but is None?  Wtf?\n"
    
        session.commit()
        print bob.parent, ": Committed again.  Is None.  Ugh.\n"
    
    if __name__ == '__main__':
        run()
    

    惊人的回答,谢谢!尽管您对我使用sqlalchemy不屑一顾,但能够做到这一点将极大地增加我可以从中获得的代码重用量。我为真正需要它的人添加了这些功能,所以这很好!
     session.expire(bob, ['parent'])
     bob.parent_id = 2
     print bob.parent, ": Dirty.  Should be George but is Fred.\n"
    
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, ForeignKey, Integer, String
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker, relationship
    
    Base = declarative_base()
    
    class Parent(Base):
        __tablename__ = 'parent'
        id = Column(Integer, primary_key=True)
        name = Column(String, nullable=False, unique=True)
    
        def __str__(self):
            return self.name
    
    class Child(Base):
        __tablename__ = 'child'
        id = Column(Integer, primary_key=True)
        parent_id = Column(ForeignKey(Parent.id), nullable=False)
        name = Column(String, nullable=False)
    
        parent = relationship(Parent, load_on_pending=True)
    
    engine = create_engine('sqlite:///:memory:', echo=True)
    Session = sessionmaker(bind=engine)
    
    def run():
        # Basic Setup
        Base.metadata.create_all(engine)
        session = Session()
        fred = Parent(name="Fred", id=1)
        george = Parent(name="George", id=2)
        session.add_all([fred, george])
        session.commit()
    
        # The test
        bob = Child(name="Bob", parent_id=1)
        session.enable_relationship_loading(bob)
        print bob.parent, ": Out of session. Should be Fred but is None.\n"
    
        session.add(bob)
        print bob.parent, ": In session.  Should be Fred but is None.\n"
    
        session.commit()
        print bob.parent, ": Committed.  Is Fred.\n"
    
        session.expire(bob, ['parent'])
        bob.parent_id = 2
        print bob.parent, ": Dirty.  Should be George but is Fred.\n"
    
        session.add(bob)
        print bob.parent, ": Added to session.  Should be George but is Fred.\n"
    
        session.expire(bob,['parent'])
        print bob.parent, ": Expired.  Should be George but is None?  Wtf?\n"
    
        session.commit()
        print bob.parent, ": Committed again.  Is None.  Ugh.\n"
    
    if __name__ == '__main__':
        run()