SQLAlchemy:为什么追加这种多对一关系会失败?

时间:2016-05-26 18:57:30

标签: python sqlalchemy

我有两个班级,ParentChild。父母可以有多个孩子。在我的计划中,我根据需要将孩子贴在父母身上。对于第一个孩子来说,这一直是成功的这是我的关系

class Parent(db.Model):

    __tablename__ = 'parent'
    id = db.Column(db.Integer, primary_key=True)
    children = db.relationship(
        'Child',
        backref=db.backref('parent', order_by=id)
    )

class Child(db.Model):

    __tablename__ = 'child'
    id = db.Column(db.Integer, primary_key=True)
    parent_fk = db.Column(
        db.Integer,
        db.ForeignKey('parent.id')
    )

然后是一次添加一个孩子的代码:

print(parent.children)
parent.children.append(Child())
print(parent.children)
database.update_object(parent)  # Uses a scoped session to merge and commit.

这是我的更新代码:

@contextmanager
def session_scope():
    try:
        yield db.session
        db.session.commit()
    except Exception as e:
        print('Rolling back database')
        print(e)
        db.session.rollback()

def update_object(obj):
    with session_scope() as session:
        session.merge(obj)

当我从程序中记录结果时,我得到了这个:

添加第一个孩子:

[]
[<Child None>]

添加第二个孩子:

[<Child 219L>]
[<Child 219L>, <Child None>]
Rolling back database
(OperationalError) (1048, "Column 'parent_fk' cannot be null") 'UPDATE child SET parent_fk=%s WHERE child.id = %s' (None, 218L)

我错过了什么?

FWIW,一致的模式是第二个Child实例的id比第一个低{1}。

1 个答案:

答案 0 :(得分:2)

错误是由@Override public void run() { try { // Code } catch (Exception e) { e.printStackTrace(); } } 的使用造成的。相反,如果您使用session.merge,它应该正常工作,因为session.add执行session.add操作,我相信您的意图。

SO answer说明insert-or-update的使用,我发现它非常有用。链接到它,而不是试图用较差的语言来解释它。

使用代码示例进行编辑,使用session.merge代替add时,问题已解决。

merge

如果我理解正确,那就是使用from sqlalchemy import Integer, Column, ForeignKey, create_engine from sqlalchemy.orm import sessionmaker, relationship, backref from sqlalchemy.ext.declarative import declarative_base engine = create_engine('postgresql://hal:hal@localhost/hal') class Parent(Base): __tablename__ = 'parent' id = Column(Integer, primary_key=True) children = relationship('Child', backref=backref('parent', order_by=id)) class Child(Base): __tablename__ = 'child' id = Column(Integer, primary_key=True) parent_fk = Column(Integer, ForeignKey('parent.id')) Base.metadata.create_all(engine) session_maker = sessionmaker(engine) s = scoped_session(session) parent = Parent() parent.children.append(Child()) s.add(parent) s.commit() engine.execute('select * from child').fetchall() # prints: [(1, 1)] parent2 = Parent() s.add(parent2) s.commit() parent2.children.append(Child()) parent2.children.append(Child()) parent2.children.append(Child()) # ``parent2`` does not need to be ``added`` to the session again, as # user this-vidor pointed out in the comments. # Committing the session will persist the changes, i.e. adding the # related three child rows to the database. s.commit() engine.execute('select * from child').fetchall() # prints: [(1, 1), (2, 2), (3, 2), (4, 2)]

实现的所需行为