关系的 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()
我有 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()