SQLAlchemy 级联删除自动映射的通用数据模型架构
SQLAlchemy cascade delete on automapped Universal Data Model Schema
我有一个来自 OfBiz 安装的预定义 PostgreSQL 数据库。该数据库有许多外键组件。我正在尝试编写一个 python 程序来将数据从生产数据库复制到暂存或开发数据库中。
最后一步是从我的数据集中清除开发人员不应看到的一些私有数据。
我的反射设置如下:
def reflectSourceTables():
global Base
Base = automap_base(metadata = smeta)
global baseNum
baseNum = 0
Base.prepare(name_for_collection_relationship=_name_for_collection_relationship, name_for_scalar_relationship=_name_for_scalar_relationship, generate_relationship=_generate_relationship)
我的反射设置如下
def _name_for_scalar_relationship(base, local_cls, referred_cls, constraint):
global baseNum
if constraint.name:
baseNum += 1
disc = '_'.join(col.name for col in constraint.columns)
return referred_cls.__name__.lower() + '.' + disc + "_scalar_" + str(baseNum)
# if this didn't work, revert to the default behavior
return name_for_scalar_relationship(base, local_cls, referred_cls, constraint)
def _name_for_collection_relationship(base, local_cls, referred_cls, constraint):
global baseNum
if constraint.name:
baseNum += 1
disc = '_'.join(col.name for col in constraint.columns)
return referred_cls.__name__.lower() + '.' + disc + "_collection_" + str(baseNum)
# if this didn't work, revert to the default behavior
return name_for_collection_relationship(base, local_cls, referred_cls, constraint)
def _generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw):
if direction is interfaces.ONETOMANY:
kw['cascade'] = 'all, delete-orphan'
kw['passive_deletes'] = True
return generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw)
我可以使用以下代码查看表之间的关系:
def getTableList(smeta):
tableList = []
if args.tables:
##Validate tables are in database
for table in args.tables:
if smeta.tables[table] in smeta.sorted_tables:
tableList.append(str(smeta.tables[table]))
else:
log('Table {0} does not exist on source'.format(table))
else:
tableList = smeta.sorted_tables
if args.tables:
for table in tableList:
for relationship in getattr(Base.classes,str(table)).__mapper__.relationships:
#print(relationship)
tableName = re.search(r'\.(.*)\.', str(relationship)).group(1)
if tableName and tableName not in tableList:
tableList.append(tableName)
return tableList
但我希望删除代码:
def cleanData():
log("Clean Data")
destSession = sessionmaker()
destSess = destSession(bind=db2)
for partyId in partyIds:
log("Cleaning data for {0}".format(partyId))
voucher = Base.classes.voucher
invoice = Base.classes.invoice
voucherDelete = destSess.query(voucher).filter_by(party_id=partyId)
voucherDelete.delete(synchronize_session=False)
invoiceDelete = destSess.query(invoice).filter_by(party_id_from=partyId)
invoiceDelete.delete(synchronize_session=False)
destSess.commit()
删除会删除凭证和发票,但不会删除子 invoice_item 记录。
我的数据库设置不包括外键的级联删除功能,但我希望我可以让 ORM 提供该功能。
理想情况下,此代码会删除凭证或发票的子项。
编辑
新关系生成如下:
def _generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw):
## Write this to include cascade delete see: https://docs.sqlalchemy.org/en/latest/orm/extensions/automap.html#custom-relationship-arguments
if direction is interfaces.ONETOMANY or direction is interfaces.MANYTOMANY:
kw['cascade'] = 'all, delete, delete-orphan'
kw['passive_deletes'] = False
if direction is interfaces.MANYTOONE:
kw['viewonly'] = True
return generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw)
删除代码改为:
voucher = Base.classes.voucher
invoice = Base.classes.invoice
invoiceDelete = destSess.query(invoice).filter_by(party_id_from=partyId)
rs = invoiceDelete.all()
for result in rs:
destSess.delete(result)
voucherDelete = destSess.query(voucher).filter_by(party_id=partyId)
rs = voucherDelete.all()
for result in rs:
destSess.delete(result)
destSess.commit()
这会导致以下错误:
sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) update or delete on table "invoice_item" violates foreign key constraint "invoice_imat_itm" on table "invoice_item_attribute"
DETAIL: Key (invoice_id, invoice_item_seq_id)=(19439, 00001) is still referenced from table "invoice_item_attribute".
[SQL: 'DELETE FROM invoice_item WHERE invoice_item.invoice_id = %(invoice_id)s AND invoice_item.invoice_item_seq_id = %(invoice_item_seq_id)s'] [parameters: ({'invoice_id': '19439', 'invoice_item_seq_id': '00001'}, {'invoice_id': '33674', 'invoice_item_seq_id': '00001'}, {'invoice_id': '49384', 'invoice_item_seq_id': '00001'}, {'invoice_id': '58135', 'invoice_item_seq_id': '00001'}, {'invoice_id': '83457', 'invoice_item_seq_id': '00001'})] (Background on this error at: http://sqlalche.me/e/gkpj)
以下配置允许我删除数据库中凭证和发票的子记录。
关系自动映射器设置如下:
def _generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw):
## Write this to include cascade delete see: https://docs.sqlalchemy.org/en/latest/orm/extensions/automap.html#custom-relationship-arguments
if direction is interfaces.ONETOMANY:
kw['cascade'] = 'all, delete, delete-orphan'
kw['passive_deletes'] = False
kw['lazy'] = 'immediate'
if direction is interfaces.MANYTOONE or direction is interfaces.MANYTOMANY:
kw['viewonly'] = True
return generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw)
删除代码为:
def cleanData():
for partyId in partyIds:
log("Cleaning data for {0}".format(partyId))
invoice = Base.classes.invoice
invoiceDelete = destSess.query(invoice).filter_by(party_id_from=partyId)
rs = invoiceDelete.all()
for result in rs:
deleteChildren(result, destSess)
destSess.delete(result)
destSess.commit()
voucher = Base.classes.voucher
voucherDelete = destSess.query(voucher).filter_by(party_id=partyId)
rs = voucherDelete.all()
for result in rs:
deleteChildren(result, destSess)
destSess.delete(result)
destSess.commit()
def deleteChildren(result, destSess):
for relationship in result.__mapper__.relationships:
if relationship.direction is interfaces.ONETOMANY:
childs = getattr(result, str(re.search(r'\.(.*)', str(relationship)).group(1)))
for child in childs:
if child.__mapper__.relationships:
deleteChildren(child, destSess)
destSess.commit()
destSess.delete(child)
destSess.commit()
为了解释,我获取了与我要从数据库中删除的派对 ID 相关的记录,并使用递归方法使用预获取从我定义的关系中获取子项。如果该子记录有子记录,我将调用相同的方法。当我从要遵循的一对多关系中 运行 时,我将记录和 return 删除到其父级,同时删除该记录。
我有一个来自 OfBiz 安装的预定义 PostgreSQL 数据库。该数据库有许多外键组件。我正在尝试编写一个 python 程序来将数据从生产数据库复制到暂存或开发数据库中。
最后一步是从我的数据集中清除开发人员不应看到的一些私有数据。
我的反射设置如下:
def reflectSourceTables():
global Base
Base = automap_base(metadata = smeta)
global baseNum
baseNum = 0
Base.prepare(name_for_collection_relationship=_name_for_collection_relationship, name_for_scalar_relationship=_name_for_scalar_relationship, generate_relationship=_generate_relationship)
我的反射设置如下
def _name_for_scalar_relationship(base, local_cls, referred_cls, constraint):
global baseNum
if constraint.name:
baseNum += 1
disc = '_'.join(col.name for col in constraint.columns)
return referred_cls.__name__.lower() + '.' + disc + "_scalar_" + str(baseNum)
# if this didn't work, revert to the default behavior
return name_for_scalar_relationship(base, local_cls, referred_cls, constraint)
def _name_for_collection_relationship(base, local_cls, referred_cls, constraint):
global baseNum
if constraint.name:
baseNum += 1
disc = '_'.join(col.name for col in constraint.columns)
return referred_cls.__name__.lower() + '.' + disc + "_collection_" + str(baseNum)
# if this didn't work, revert to the default behavior
return name_for_collection_relationship(base, local_cls, referred_cls, constraint)
def _generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw):
if direction is interfaces.ONETOMANY:
kw['cascade'] = 'all, delete-orphan'
kw['passive_deletes'] = True
return generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw)
我可以使用以下代码查看表之间的关系:
def getTableList(smeta):
tableList = []
if args.tables:
##Validate tables are in database
for table in args.tables:
if smeta.tables[table] in smeta.sorted_tables:
tableList.append(str(smeta.tables[table]))
else:
log('Table {0} does not exist on source'.format(table))
else:
tableList = smeta.sorted_tables
if args.tables:
for table in tableList:
for relationship in getattr(Base.classes,str(table)).__mapper__.relationships:
#print(relationship)
tableName = re.search(r'\.(.*)\.', str(relationship)).group(1)
if tableName and tableName not in tableList:
tableList.append(tableName)
return tableList
但我希望删除代码:
def cleanData():
log("Clean Data")
destSession = sessionmaker()
destSess = destSession(bind=db2)
for partyId in partyIds:
log("Cleaning data for {0}".format(partyId))
voucher = Base.classes.voucher
invoice = Base.classes.invoice
voucherDelete = destSess.query(voucher).filter_by(party_id=partyId)
voucherDelete.delete(synchronize_session=False)
invoiceDelete = destSess.query(invoice).filter_by(party_id_from=partyId)
invoiceDelete.delete(synchronize_session=False)
destSess.commit()
删除会删除凭证和发票,但不会删除子 invoice_item 记录。
我的数据库设置不包括外键的级联删除功能,但我希望我可以让 ORM 提供该功能。
理想情况下,此代码会删除凭证或发票的子项。
编辑
新关系生成如下:
def _generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw):
## Write this to include cascade delete see: https://docs.sqlalchemy.org/en/latest/orm/extensions/automap.html#custom-relationship-arguments
if direction is interfaces.ONETOMANY or direction is interfaces.MANYTOMANY:
kw['cascade'] = 'all, delete, delete-orphan'
kw['passive_deletes'] = False
if direction is interfaces.MANYTOONE:
kw['viewonly'] = True
return generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw)
删除代码改为:
voucher = Base.classes.voucher
invoice = Base.classes.invoice
invoiceDelete = destSess.query(invoice).filter_by(party_id_from=partyId)
rs = invoiceDelete.all()
for result in rs:
destSess.delete(result)
voucherDelete = destSess.query(voucher).filter_by(party_id=partyId)
rs = voucherDelete.all()
for result in rs:
destSess.delete(result)
destSess.commit()
这会导致以下错误:
sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) update or delete on table "invoice_item" violates foreign key constraint "invoice_imat_itm" on table "invoice_item_attribute"
DETAIL: Key (invoice_id, invoice_item_seq_id)=(19439, 00001) is still referenced from table "invoice_item_attribute".
[SQL: 'DELETE FROM invoice_item WHERE invoice_item.invoice_id = %(invoice_id)s AND invoice_item.invoice_item_seq_id = %(invoice_item_seq_id)s'] [parameters: ({'invoice_id': '19439', 'invoice_item_seq_id': '00001'}, {'invoice_id': '33674', 'invoice_item_seq_id': '00001'}, {'invoice_id': '49384', 'invoice_item_seq_id': '00001'}, {'invoice_id': '58135', 'invoice_item_seq_id': '00001'}, {'invoice_id': '83457', 'invoice_item_seq_id': '00001'})] (Background on this error at: http://sqlalche.me/e/gkpj)
以下配置允许我删除数据库中凭证和发票的子记录。
关系自动映射器设置如下:
def _generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw):
## Write this to include cascade delete see: https://docs.sqlalchemy.org/en/latest/orm/extensions/automap.html#custom-relationship-arguments
if direction is interfaces.ONETOMANY:
kw['cascade'] = 'all, delete, delete-orphan'
kw['passive_deletes'] = False
kw['lazy'] = 'immediate'
if direction is interfaces.MANYTOONE or direction is interfaces.MANYTOMANY:
kw['viewonly'] = True
return generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw)
删除代码为:
def cleanData():
for partyId in partyIds:
log("Cleaning data for {0}".format(partyId))
invoice = Base.classes.invoice
invoiceDelete = destSess.query(invoice).filter_by(party_id_from=partyId)
rs = invoiceDelete.all()
for result in rs:
deleteChildren(result, destSess)
destSess.delete(result)
destSess.commit()
voucher = Base.classes.voucher
voucherDelete = destSess.query(voucher).filter_by(party_id=partyId)
rs = voucherDelete.all()
for result in rs:
deleteChildren(result, destSess)
destSess.delete(result)
destSess.commit()
def deleteChildren(result, destSess):
for relationship in result.__mapper__.relationships:
if relationship.direction is interfaces.ONETOMANY:
childs = getattr(result, str(re.search(r'\.(.*)', str(relationship)).group(1)))
for child in childs:
if child.__mapper__.relationships:
deleteChildren(child, destSess)
destSess.commit()
destSess.delete(child)
destSess.commit()
为了解释,我获取了与我要从数据库中删除的派对 ID 相关的记录,并使用递归方法使用预获取从我定义的关系中获取子项。如果该子记录有子记录,我将调用相同的方法。当我从要遵循的一对多关系中 运行 时,我将记录和 return 删除到其父级,同时删除该记录。