Update the given fields in each of the given objects in the database.
(self, objs, fields, batch_size=None)
| 950 | abulk_create.alters_data = True |
| 951 | |
| 952 | def bulk_update(self, objs, fields, batch_size=None): |
| 953 | """ |
| 954 | Update the given fields in each of the given objects in the database. |
| 955 | """ |
| 956 | if batch_size is not None and batch_size <= 0: |
| 957 | raise ValueError("Batch size must be a positive integer.") |
| 958 | if not fields: |
| 959 | raise ValueError("Field names must be given to bulk_update().") |
| 960 | objs = tuple(objs) |
| 961 | if not all(obj._is_pk_set() for obj in objs): |
| 962 | raise ValueError("All bulk_update() objects must have a primary key set.") |
| 963 | opts = self.model._meta |
| 964 | fields = [opts.get_field(name) for name in fields] |
| 965 | if any(not f.concrete for f in fields): |
| 966 | raise ValueError("bulk_update() can only be used with concrete fields.") |
| 967 | all_pk_fields = set(opts.pk_fields) |
| 968 | for parent in opts.all_parents: |
| 969 | all_pk_fields.update(parent._meta.pk_fields) |
| 970 | if any(f in all_pk_fields for f in fields): |
| 971 | raise ValueError("bulk_update() cannot be used with primary key fields.") |
| 972 | if not objs: |
| 973 | return 0 |
| 974 | for obj in objs: |
| 975 | obj._prepare_related_fields_for_save( |
| 976 | operation_name="bulk_update", fields=fields |
| 977 | ) |
| 978 | # PK is used twice in the resulting update query, once in the filter |
| 979 | # and once in the WHEN. Each field will also have one CAST. |
| 980 | self._for_write = True |
| 981 | connection = connections[self.db] |
| 982 | max_batch_size = connection.ops.bulk_batch_size( |
| 983 | [opts.pk, opts.pk, *fields], objs |
| 984 | ) |
| 985 | batch_size = min(batch_size, max_batch_size) if batch_size else max_batch_size |
| 986 | requires_casting = connection.features.requires_casted_case_in_updates |
| 987 | batches = (objs[i : i + batch_size] for i in range(0, len(objs), batch_size)) |
| 988 | updates = [] |
| 989 | for batch_objs in batches: |
| 990 | update_kwargs = {} |
| 991 | for field in fields: |
| 992 | when_statements = [] |
| 993 | for obj in batch_objs: |
| 994 | attr = getattr(obj, field.attname) |
| 995 | if not hasattr(attr, "resolve_expression"): |
| 996 | attr = Value(attr, output_field=field) |
| 997 | when_statements.append(When(pk=obj.pk, then=attr)) |
| 998 | case_statement = Case(*when_statements, output_field=field) |
| 999 | if requires_casting: |
| 1000 | case_statement = Cast(case_statement, output_field=field) |
| 1001 | update_kwargs[field.attname] = case_statement |
| 1002 | updates.append(([obj.pk for obj in batch_objs], update_kwargs)) |
| 1003 | rows_updated = 0 |
| 1004 | queryset = self.using(self.db) |
| 1005 | with transaction.atomic(using=self.db, savepoint=False): |
| 1006 | for pks, update_kwargs in updates: |
| 1007 | rows_updated += queryset.filter(pk__in=pks).update(**update_kwargs) |
| 1008 | return rows_updated |
| 1009 |