Python 当外键约束是复合主键的一部分时,依赖关系规则试图在SQLAlchemy中清空主键

Python 当外键约束是复合主键的一部分时,依赖关系规则试图在SQLAlchemy中清空主键,python,sqlalchemy,Python,Sqlalchemy,我有以下模型定义 class Foo(Base): __tablename__ = 'foo' id = Column(Integer, primary_key=True) name = Column(String(200)) class FooCycle(Base): __tablename__ = 'foocycle' foo_id = Column( String(50), ForeignKey('foo.id

我有以下模型定义

class Foo(Base):
    __tablename__ = 'foo'

    id = Column(Integer, primary_key=True)
    name = Column(String(200))


class FooCycle(Base):
    __tablename__ = 'foocycle'

    foo_id = Column(
        String(50),
        ForeignKey('foo.id'),
        primary_key=True
    )
    some_number = Column(
        Integer,
        primary_key=True,
    )

    foo = relationship("Foo", backref="cycles")
以及下面的测试用例

class HierarchicModelTest(unittest.TestCase):
    def test_create_data_via_orm_save_twice(self):
        # get_session is a convenience wrapper to access a scoped session object
        s = get_session()

        def create_foo():
            foo = Foo(id="12345", name="fancy foo")
            foo.cycles = [FooCycle(some_number=1)]

            return foo

        # initially create foo
        foo = create_foo()
        s.add(foo)
        s.flush()

        # recreating foo, using merge to update into database
        foo = create_foo()
        s.merge(foo)

        # raises Exception: Dependency rule tried to blank-out primary key
        # column 'foocycle.foo_id' on instance '<FooCycle at 0x32e6b10>'
        s.flush()
但是,由于简洁、建筑方面的考虑以及无可否认的个人自豪感,我不想这样做。有没有一种简单的方法可以让SQLAlchemy解决这个问题。我还没有完全理解依赖规则的目的。关于这个问题有什么建议/信息吗

堆栈跟踪:

# Test 1 of 7:
# test_core.HierarchicModelTest.test_create_data_via_orm_save_twice
===============
HierarchicModelTest: test_create_data_via_orm_save_twice (tests.test_core.HierarchicModelTest)
Failed test "test_create_data_via_orm_save_twice (tests.test_core.HierarchicModelTest)"! Reason: Dependency rule tried to blank-out primary key column 'foocycle.foo_id' on instance '<FooCycle at 0x39cda10>'
Traceback (most recent call last):
  File "/home/xxx/xxx/xxx/backend/tests/test_core.py", line 115, in test_create_data_via_orm_save_twice
    s.flush()
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 149, in do
    return getattr(self.registry(), name)(*args, **kwargs)
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1879, in flush
    self._flush(objects)
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1997, in _flush
    transaction.rollback(_capture_exception=True)
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 57, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1961, in _flush
    flush_context.execute()
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 370, in execute
    rec.execute(self)
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 479, in execute
    self.dependency_processor.process_saves(uow, states)
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/dependency.py", line 552, in process_saves
    uowcommit, False)
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/dependency.py", line 569, in _synchronize
    sync.clear(dest, self.mapper, self.prop.synchronize_pairs)
  File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/sync.py", line 53, in clear
    (r, orm_util.state_str(dest))
AssertionError: Dependency rule tried to blank-out primary key column 'foocycle.foo_id' on instance '<FooCycle at 0x39cda10>'
#测试1/7:
#test_core.hierarchycmodeltest.test_通过\u orm\u save\u创建\u数据\u两次
===============
HierarchysModelTest:test\u通过\u orm\u save\u创建\u数据\u两次(tests.test\u core.HierarchysModelTest)
失败的测试“test\u create\u data\u via\u orm\u save\u两次(tests.test\u core.hierarchycmodeltest)”!原因:依赖项规则试图清空实例“”上的主键列“foocycle.foo_id”
回溯(最近一次呼叫最后一次):
文件“/home/xxx/xxx/xxx/backend/tests/test\u core.py”,第115行,通过orm\u save\u创建数据
s、 刷新()
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/scoping.py”,do中第149行
返回getattr(self.registry(),name)(*args,**kwargs)
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/session.py”,第1879行,刷新
自冲洗(对象)
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/session.py”,第1997行,在
事务.rollback(\u capture\u exception=True)
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py”,第57行,在退出时__
兼容性(exc_类型、exc_值、exc_tb)
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/session.py”,第1961行,在
flush_context.execute()
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/unitofwork.py”,第370行,在execute中
rec.execute(self)
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/unitofwork.py”,第479行,在execute中
自相关性\u处理器。进程\u保存(uow,状态)
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/dependency.py”,第552行,进程中保存
uowcommit,False)
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/dependency.py”,第569行,在
sync.clear(dest、self.mapper、self.prop.synchronize\u对)
文件“/home/xxx/xxx/xxx/.env/lib/python2.7/site packages/sqlalchemy/orm/sync.py”,第53行,清晰显示
(r,orm_util.state_str(目的地))
AssertionError:依赖项规则试图清空实例“”上的主键列“foocycle.foo_id”

根据van的评论,我找到了一个解决方案。默认关系级联是
“保存更新,合并”
。我必须将其设置为
“保存更新、合并、删除、删除孤立文件”

添加
delete
本身并没有改变行为,
delete orphan
是必需的

仅添加
delete-orphan
使删除测试用例失败,出现问题中提到的“依赖规则”断言错误:

class HierarchicModelTest(unittest.TestCase):
    def test_delete_parent_object(self):
        foo = Foo(**foo_data).save()
        self.assertEqual(Foo.query.count(), 1)
        self.assertEqual(FooCycle.query.count(), 1)

        s = get_session()
        s.delete(foo)
        s.flush()

        self.assertEqual(Foo.query.count(), 0)
        self.assertEqual(FooCycle.query.count(), 0)
--


我不认为这是SA无法处理
foo\u id
的问题。事实上,SA确实能够做到这一点,这是一个伟大的点。我认为问题在于在
合并过程中
SA删除
FooCycle
的第一个实例并插入新实例。删除第一行是个问题,因为它不会删除其行,而只会尝试删除关系。因此,您需要为您的关系配置适当的规则。感谢您的评论,这为我指明了正确的方向。@van您能告诉我们在这种情况下适当的级联规则是什么吗(最好是作为一个答案,因为我想这可能太多,无法放入评论中)?您说它不会删除它的行,而只是尝试删除关系;但如果行被删除,那么每次我们都会得到一个新的
FooCycle
实例。有没有办法让SA更新
FooCycle
的现有行/实例?@piotrdorgost:SA不会自动更新现有行/实例。您应该先编写代码来搜索关系,并将新实例中的数据合并到现有实例中。我在SQLAlchemy新闻组上问了类似的问题——SQLAlchemy的作者Mike Bayer对此进行了有趣的深入研究。但是,使用
delete orphan
会导致
FooCycle
的现有实例被删除而不是更新。如果
FooCycle
的标识很重要(例如,当我们想通过保存所有历史值的历史记录来对其进行版本设置时),则不能使用此解决方案。@room2web:有没有理由不简单地将
cascade=“all,delete orphan”
改为
cascade=“save update,merge,delete,delete orphan”
class HierarchicModelTest(unittest.TestCase):
    def test_delete_parent_object(self):
        foo = Foo(**foo_data).save()
        self.assertEqual(Foo.query.count(), 1)
        self.assertEqual(FooCycle.query.count(), 1)

        s = get_session()
        s.delete(foo)
        s.flush()

        self.assertEqual(Foo.query.count(), 0)
        self.assertEqual(FooCycle.query.count(), 0)
  File "/home/xxx/xxx/xxx/backend/tests/test_core.py", line 128, in test_delete_parent_object
     s.flush()
  [...]
  AssertionError: Dependency rule tried to blank-out primary key column 'foocycle.foo_id' on instance '<FooCycle at 0x37a1710>'
class FooCycle(Base):
    __tablename__ = 'foocycle'

    foo_id = Column(
        String(50),
        ForeignKey('foo.id'),
        primary_key=True
    )
    some_number = Column(
        Integer,
        primary_key=True,
    )

    foo = relationship("Foo",
                       backref=backref("cycles",
                                        cascade="save-update, merge, "
                                                "delete, delete-orphan"))