如何在Django中批量更新数据库表数据,解析大XML时不删原有数据
优化实现方案
现有代码可优化点
你当前的实现已经解决了删数据破坏外键的问题,但还有两个明显的性能提升空间:
- 存储已有分类ID的
created_categories用了列表结构,cat_id in created_categories的查找时间复杂度是O(n),分类数量大时会明显拖慢处理速度 - 拆分新增、更新列表后两次访问数据库,可合并为一次upsert操作进一步降低数据库IO开销
最优实现方案(Django 3.2+ 适用)
直接用Django内置的bulk_create的冲突更新特性(upsert),无需自己判断记录是新增还是已存在,一次数据库请求完成所有操作,代码更简洁性能也更高:
categories = {} # 解析XML逻辑保持不变 for category in self.root.findall('.//categories/category'): cat_id = category.get('id') cat = CategoryModel( id=cat_id, name=category.text, picture=category.get('picture') ) categories[cat_id] = cat # 批量插入+冲突更新一步完成 CategoryModel.objects.bulk_create( categories.values(), batch_size=50000, update_conflicts=True, unique_fields=['id'], update_fields=['name', 'picture'] )
低版本Django兼容优化
如果你使用的Django版本低于3.2,不支持update_conflicts参数,只需要修改原有代码的ID存储结构为集合,就能将查找复杂度降到O(1),获得明显性能提升:
# 直接查询ID转为集合,无需查询全量模型实例 created_categories = {str(cat[0]) for cat in CategoryModel.objects.values_list('id')} new_cats = [] update_cats = [] for category in self.root.findall('.//categories/category'): cat_id = category.get('id') cat = CategoryModel( id=cat_id, name=category.text, picture=category.get('picture') ) if cat_id in created_categories: update_cats.append(cat) else: new_cats.append(cat) CategoryModel.objects.bulk_create(new_cats, batch_size=50000) CategoryModel.objects.bulk_update(update_cats, fields=['name', 'picture'], batch_size=1000)
超大型XML额外优化
如果你的XML文件大小超过内存负载,不要用findall全量加载XML到内存,改用迭代解析方式边读边处理,避免内存溢出:
import xml.etree.ElementTree as ET categories = {} # 迭代解析XML,只处理category节点结束事件 for event, elem in ET.iterparse('你的XML文件路径', events=('end',)): if elem.tag == 'category': cat_id = elem.get('id') categories[cat_id] = CategoryModel( id=cat_id, name=elem.text, picture=elem.get('picture') ) # 处理完立即清理节点释放内存 elem.clear()
可选同步删除逻辑
如果业务需要同步删除数据库中存在、但XML中已经不存在的分类,可以在解析完XML后增加以下逻辑,操作前注意确认外键约束配置:
# 收集XML中所有分类ID xml_cat_ids = set(categories.keys()) # 批量删除不在XML中的旧分类,不需要该逻辑可跳过 CategoryModel.objects.exclude(id__in=xml_cat_ids).delete()
内容的提问来源于stack exchange,提问作者dzukp
相关产品推荐
相关产品推荐

