Sqlalchemy post_update for relationship似乎不适合我

时间:2015-08-11 20:18:06

标签: python sqlalchemy flask-sqlalchemy

我有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

1 个答案:

答案 0 :(得分:0)

当我观察到你的下面的错误时,它试图为我们的程序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定义了这两个对象之间的实际关系。您最好尝试运行它。 ...

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()