django bulk_update_or_create 批量创建更新

文章介绍了在Django中为QuerySet扩展`bulk_update_or_create`方法,用于批量创建或更新数据。该方法首先根据通用筛选条件和唯一键进行数据查询,然后分别处理不存在的数据(批量创建)和已存在的数据(批量更新)。同时,文章提供了使用示例,展示了如何在业务场景中应用这个自定义功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

当前django并没有提供能够批量创建或更新的方法,只有bulk_updatebulk_create以及create_or_update,在实际业务中并不能满足我们的需求。因此才会有了bulk_update_or_create


一、代码实现

from utils import time_tools
from django.db import models
from django.db import transaction
# from bulk_update_or_create import BulkUpdateOrCreateQuerySet


class ExpandQuerySet(models.QuerySet):
    """
    拓展querySet
    """

    def bulk_update_or_create(self, common_keys, unique_key_name, unique_key_to_defaults, batch_size=200):
        """
        common_keys: {field_name: field_value} 通用筛选条件
        unique_key_name: field_name # 唯一字段
        unique_key_to_defaults: {field_value: {field_name: field_value}} # 更新值
        """
        with transaction.atomic(using=self.db, savepoint=False):
            filter_kwargs = dict(common_keys)
            filter_kwargs[f"{unique_key_name}__in"] = unique_key_to_defaults.keys()
            existing_objs = {
                getattr(obj, unique_key_name): obj
                for obj in self.filter(**filter_kwargs).select_for_update()
            }
            # 批量创建
            create_data = {
                k: v for k, v in unique_key_to_defaults.items() if k not in existing_objs
            }
            for unique_key_value, obj in create_data.items():
                obj[unique_key_name] = unique_key_value
                obj.update(common_keys)
            creates = [self.model(**obj_data) for obj_data in create_data.values()]
            if creates:
                self.bulk_create(creates, batch_size=batch_size)
            # 如果使用了add_now来自动更新时间,update_fields必须包含此字段
            # 因queryset.update不会自动更新时间,只有save会
            update_fields = {"update_time"}
            # 批量更新
            updates = []
            for key, obj in existing_objs.items():
                for i in unique_key_to_defaults[key].items():
                    setattr(obj, i[0], i[1])
                # 将所有要更新的字段都统计出来
                update_fields.update(unique_key_to_defaults[key].keys())
                updates.append(obj)
            if existing_objs:
                self.bulk_update(updates, update_fields, batch_size=batch_size)
        return len(creates), len(updates)

    def update(self, **kwargs):
        if getattr(self.model, "update_time", None):
            kwargs.update({"update_time": time_tools.now()})
        return super().update(**kwargs)

class AbstractModel(models.Model):
    objects: ExpandQuerySet = ExpandQuerySet.as_manager()

我们需要在我们的模型基类中重定义objects为我们的扩展类。
bulk_update_or_create共分为三步:1、查询数据库中包含当前数据的数据。2、调用父类批量创建bulk_create方法,批量创建数据库中不存在的。3、调用父类批量更新bulk_update方法,批量更新数据库中存在的。
ps:我们会在批量更新中定义一个update_fields = {"update_time"}字段,
因为如果使用了add_now来自动更新时间,query.update 不会更新此此段,只有save中会,在save中对包含add_now的字段做了处理.

二、使用

        bulk_create_or_update_dict = {}
        for _ in self._contents:
            if "url" in _:
                unique = _["unique"]
                permission_dict = {
                    Permission.name.field.attname: 1,
                    Permission.valid.field.attname: True,
                    Permission.desc.field.attname: 1,
                    Permission.level.field.attname: 1,
                    Permission.uri.field.attname: 1,
                    Permission.lambda_name.field.attname: 1,
                    Permission.action_name.field.attname: 1,
                    Permission.permission.field.attname: unique
                }

                bulk_create_or_update_dict[unique] = permission_dict
        common_keys = {}  # 通用筛选项
        unique_key_name = Permission.permission.field.attname  # 唯一值
        unique_key_to_defaults = bulk_create_or_update_dict  # 默认值
        Permission.objects.using(PG_WRITE).bulk_update_or_create(common_keys, unique_key_name, unique_key_to_defaults)

common_keys :字典,通用的筛选项,会传递到orm的filter中
unique_key_name :字符串,唯一的字段的名称
unique_key_to_defaults :字典,key为唯一的值,value为更新的数据

### Django 中 `bulk_update` 方法的使用 #### 安装依赖 为了在 Django 项目中实现批量更新功能,可以安装第三方库 `django-bulk-update`。需要将其添加到项目的 `settings.py` 文件中的 `INSTALLED_APPS` 配置列表中[^2]。 ```python INSTALLED_APPS = [ # ... 'bulk_update', # ... ] ``` #### 数据模型定义 假设有一个简单的数据模型用于演示批量更新的功能。以下是示例模型定义[^3]: ```python from django.db import models class Text_one(models.Model): title = models.CharField(max_length=225) name = models.CharField(max_length=225, db_index=True) count = models.IntegerField(default=100) def __str__(self): return self.title ``` #### 批量更新操作 通过 `bulk_update` 方法可以在单次查询中完成多个对象的更新操作。以下是一个完整的使用示例: ##### 创建测试数据 首先创建一些初始记录以便后续进行批量更新: ```python Text_one.objects.bulk_create([ Text_one(title="Title A", name="Name A"), Text_one(title="Title B", name="Name B"), Text_one(title="Title C", name="Name C"), ]) ``` ##### 更新字段 接下来准备要更新的对象集合,并调用 `bulk_update` 进行批量更新: ```python objects_to_update = list(Text_one.objects.filter(name__in=["Name A", "Name B"])) for obj in objects_to_update: if obj.name == "Name A": obj.count += 10 elif obj.name == "Name B": obj.count -= 5 Text_one.objects.bulk_update(objects_to_update, fields=['count']) ``` 上述代码片段实现了对指定条件下的多条记录执行计数器增减的操作[^1]。 #### 注意事项 - **fields 参数**: 调用 `bulk_update` 时需明确指定哪些字段会被更新。这有助于提高性能并减少不必要的数据库写入。 - **事务管理**: 如果涉及大量数据更新,建议将整个过程包裹在一个事务块内以确保一致性[^4]: ```python from django.db import transaction with transaction.atomic(): Text_one.objects.bulk_update(objects_to_update, fields=['count']) ``` --- ### 总结 以上展示了如何利用 `django-bulk-update` 库以及原生支持来优化 Django批量更新场景。此方法显著减少了多次单独 SQL 查询带来的开销,从而提升了应用的整体效率[^5]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值