Django ORM实现Item模型批量增改删逻辑且保留原有price数据
解决方案
前置准备
首先从待同步的新Item列表中提取所有唯一标识对,缩小后续操作的查询范围,避免全表扫描:
# 假设待同步的新Item列表为 new_items # 提取所有(article, owner_id)唯一标识对,直接用外键id性能更高 sync_identifiers = {(item.article, item.owner_id) for item in new_items} # 提取本次操作涉及的所有owner id,避免误删其他owner的数据 related_owner_ids = {item.owner_id for item in new_items}
步骤1+2:批量插入/更新,保留原有price字段
你已有的bulk_create_or_update能力直接指定更新时排除price字段即可,新增记录因为没有传入price会自动设为Null:
# 以Django ORM为例的参数配置 Item.objects.bulk_create_or_update( new_items, unique_fields=['article', 'owner'], # 按要求用article+owner做唯一判定 update_fields=['stock'], # 仅更新需要同步的字段,自动跳过price,原有值完全保留 )
没有内置批量upsert能力的框架也可以直接用数据库原生语法实现,比如MySQL的
INSERT ... ON DUPLICATE KEY UPDATE、PostgreSQL的INSERT ... ON CONFLICT ... DO UPDATE,只要指定更新时跳过price字段即可,全程是批量操作没有单条查询的性能损耗。
步骤3:批量删除未被更新的记录
直接用前置步骤提取的唯一标识集合做过滤即可,不会影响已保留的price数据:
from django.db.models import Q # 拼接排除条件:所有在待同步列表里的记录都不删 exclude_conditions = Q() for article, owner_id in sync_identifiers: exclude_conditions |= Q(article=article, owner_id=owner_id) # 仅删除本次涉及的owner范围内、不在待同步列表里的记录 Item.objects.filter(owner_id__in=related_owner_ids).exclude(exclude_conditions).delete()
如果待同步数据量超过1000条,可以用临时表或者子查询优化,避免拼接过多OR条件:
from django.db.models import Exists, OuterRef keep_query = Item.objects.filter( owner_id=OuterRef('owner_id'), article=OuterRef('article'), # upsert完成后已有id的记录就是需要保留的 id__in=[item.id for item in new_items if item.id] ) Item.objects.filter( owner_id__in=related_owner_ids, ~Exists(keep_query) ).delete()
注意事项
- 提前给
article和owner字段加联合唯一索引,既能保证upsert逻辑正确,也能大幅提升所有操作的性能 - 所有操作放在同一个事务中执行,避免中途出错导致数据不一致:
from django.db import transaction with transaction.atomic(): # 上述所有操作放在这个代码块内 - 如果原有Item模型的
price字段没有设置null=True,需要先执行数据库迁移,改为IntegerField(null=True, blank=True),否则新增记录存Null会报错。
内容的提问来源于stack exchange,提问作者Rey
相关产品推荐
相关产品推荐

