关系的 SQLalchemy post_update 似乎对我不起作用

Sqlalchemy post_update for relationship seems not working for me

我有 2 个模型 (sqlalchemy 0.9),它们相互引用(简化并以整数作为 id)

class Device(SyncableModel):
    __tablename__ = 'devices'
    ...
    id = db.Column(db.Integer, primary_key=True)
    current_program_id = db.Column(db.Integer, db.ForeignKey('programs.id', use_alter=True, name='fk_current_program'))

    current_program = relationship(
        'Program', backref = backref('current_for_device', uselist=False),
        primaryjoin="Device.current_program_id==Program.id",
        post_update=True
    )
    programs = relationship(
        'Program', backref='device', lazy='joined',
         cascade="all, delete-orphan",
         primaryjoin="Device.id==Program.device_id"
    )


class Program(SyncableModel):
    __tablename__ = 'programs'
    ...
    id = db.Column(db.Integer, primary_key=True)
    device_id = db.Column(db.Integer, db.ForeignKey('devices.id', ondelete='CASCADE'))
    name = db.Column(db.String(64))

我需要做这个:

device = Device()
device.id = 1
program = Program()
program.id = 2
program.device_id = device.id
device.current_program_id = program.id
db.session.add_all([device, program,])
db.session.commit()

但我得到:

IntegrityError: (IntegrityError) (1452, 'Cannot add or update a child row: a foreign key constraint fails (`cloud2`.`devices`, CONSTRAINT `fk_current_program` FOREIGN KEY (`current_program_id`) REFERENCES `programs` (`id`))') 'UPDATE devices SET created=%s, updated=%s, name=%s, rain_detected=%s, current_program_id=%s, timezone=%s, synced=%s WHERE devices.id = %s' (datetime.datetime(2015, 8, 11, 19, 48, 23), datetime.datetime(2015, 8, 11, 19, 48, 26), 'nWhosie', 0, '\xeaL\xff\x80\xe5\xffC2\x9a)\xe5\x86\xe4\xad}l', 'America/Chicago', datetime.datetime(2015, 8, 11, 19, 48, 27), '\xd4\xe3\x04;:\xa0M\xce\x95\x9a\xf9U\x05x"\x10')

注意:我使用的不是真正的 int id

我认为 post_update=True 它会按预期工作。我做错了什么?

谢谢!

更新 正如 @gsb-eng 提到的,问题的根源应该在于缺少明确的关系设置。但这是一个问题,因为对象是从字典列表中生成的,很难找出所有关系。

更新 2 最后我能够在检查的帮助下修复关系:

def fix_relationships(objs):
    for obj in objs:
        insp = inspect(type(obj))
        for relation in insp.relationships:
            if relation.post_update:
                # A relation to be set
                relation_key = relation.key
                if getattr(obj, relation_key):
                    # Relation had already been set
                    continue
                foreign_key = relation.primaryjoin.left.key
                target_class_name = relation.argument.arg
                target_key = relation.primaryjoin.right.key
                fk = getattr(obj, foreign_key)
                if not fk:
                    # Nothing to set.
                    continue
                for o in objs:
                    if o.__class__.__name__ == target_class_name \
                            and getattr(o, target_key) == fk:
                        setattr(obj, relation_key, o)
                        setattr(obj, foreign_key, None)
                        break

正如我观察到您的以下错误,它正在尝试为我们的程序 current_program_id='\xeaL\xff\x80\xe5\xffC2\x9a)\xe5\x86\xe4\xad}l' 插入这个值,并且应该是它定义的整数值。

UPDATE devices SET created=%s, updated=%s, name=%s, rain_detected=%s, current_program_id=%s, timezone=%s, synced=%s WHERE devices.id = %s' (datetime.datetime(2015, 8, 11, 19, 48, 23), datetime.datetime(2015, 8, 11, 19, 48, 26), 'nWhosie', 0, '\xeaL\xff\x80\xe5\xffC2\x9a)\xe5\x86\xe4\xad}l', 'America/Chicago', datetime.datetime(2015, 8, 11, 19, 48, 27), '\xd4\xe3\x04;:\xa0M\xce\x95\x9a\xf9U\x05x"\x10')

理想情况下,它不应该触发 UPDATE 查询,因为你没有提到这种关系,device.current_program = program 定义了这两者之间的实际关系 objects.May 你最好尝试 运行 这个....

device = Device()
device.id = 1
program = Program()
program.id = 2
device.current_program = program
device.programs = [program]
db.session.add_all([device, program,])
db.session.commit()