django-bulk-hooks 0.1.117__tar.gz → 0.1.118__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-bulk-hooks might be problematic. Click here for more details.
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/PKG-INFO +3 -3
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/manager.py +84 -28
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/pyproject.toml +1 -1
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/LICENSE +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/README.md +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/conditions.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/engine.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/priority.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/queryset.py +0 -0
- {django_bulk_hooks-0.1.117 → django_bulk_hooks-0.1.118}/django_bulk_hooks/registry.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: django-bulk-hooks
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.118
|
|
4
4
|
Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
|
|
5
|
-
Home-page: https://github.com/AugendLimited/django-bulk-hooks
|
|
6
5
|
License: MIT
|
|
7
6
|
Keywords: django,bulk,hooks
|
|
8
7
|
Author: Konrad Beck
|
|
@@ -14,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
15
|
Requires-Dist: Django (>=4.0)
|
|
16
|
+
Project-URL: Homepage, https://github.com/AugendLimited/django-bulk-hooks
|
|
17
17
|
Project-URL: Repository, https://github.com/AugendLimited/django-bulk-hooks
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from django.db import models, transaction
|
|
1
|
+
from django.db import models, transaction, connections
|
|
2
2
|
from django.db.models import AutoField
|
|
3
3
|
|
|
4
4
|
from django_bulk_hooks import engine
|
|
@@ -95,9 +95,10 @@ class BulkHookManager(models.Manager):
|
|
|
95
95
|
raise ValueError("Batch size must be a positive integer.")
|
|
96
96
|
|
|
97
97
|
# Check that the parents share the same concrete model with our model to detect inheritance pattern
|
|
98
|
+
# (Do NOT raise for MTI, just skip the exception)
|
|
98
99
|
for parent in model_cls._meta.all_parents:
|
|
99
100
|
if parent._meta.concrete_model is not model_cls._meta.concrete_model:
|
|
100
|
-
#
|
|
101
|
+
# Do not raise, just continue
|
|
101
102
|
break
|
|
102
103
|
|
|
103
104
|
if not objs:
|
|
@@ -118,34 +119,68 @@ class BulkHookManager(models.Manager):
|
|
|
118
119
|
engine.run(model_cls, VALIDATE_CREATE, objs, ctx=ctx)
|
|
119
120
|
engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
122
|
+
opts = model_cls._meta
|
|
123
|
+
if unique_fields:
|
|
124
|
+
unique_fields = [
|
|
125
|
+
model_cls._meta.get_field(opts.pk.name if name == "pk" else name)
|
|
126
|
+
for name in unique_fields
|
|
127
|
+
]
|
|
128
|
+
if update_fields:
|
|
129
|
+
update_fields = [model_cls._meta.get_field(name) for name in update_fields]
|
|
130
|
+
on_conflict = self._check_bulk_create_options(
|
|
131
|
+
ignore_conflicts,
|
|
132
|
+
update_conflicts,
|
|
133
|
+
update_fields,
|
|
134
|
+
unique_fields,
|
|
135
|
+
)
|
|
136
|
+
self._for_write = True
|
|
137
|
+
fields = [f for f in opts.concrete_fields if not f.generated]
|
|
138
|
+
objs = list(objs)
|
|
139
|
+
objs_with_pk, objs_without_pk = self._prepare_for_bulk_create(objs)
|
|
140
|
+
with transaction.atomic(using=self.db, savepoint=False):
|
|
141
|
+
self._handle_order_with_respect_to(objs)
|
|
142
|
+
if objs_with_pk:
|
|
143
|
+
returned_columns = self._batched_insert(
|
|
144
|
+
objs_with_pk,
|
|
145
|
+
fields,
|
|
146
|
+
batch_size,
|
|
147
|
+
on_conflict=on_conflict,
|
|
148
|
+
update_fields=update_fields,
|
|
149
|
+
unique_fields=unique_fields,
|
|
140
150
|
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
for obj_with_pk, results in zip(objs_with_pk, returned_columns):
|
|
152
|
+
for result, field in zip(results, opts.db_returning_fields):
|
|
153
|
+
if field != opts.pk:
|
|
154
|
+
setattr(obj_with_pk, field.attname, result)
|
|
155
|
+
for obj_with_pk in objs_with_pk:
|
|
156
|
+
obj_with_pk._state.adding = False
|
|
157
|
+
obj_with_pk._state.db = self.db
|
|
158
|
+
if objs_without_pk:
|
|
159
|
+
fields_wo_pk = [f for f in fields if not isinstance(f, AutoField)]
|
|
160
|
+
returned_columns = self._batched_insert(
|
|
161
|
+
objs_without_pk,
|
|
162
|
+
fields_wo_pk,
|
|
163
|
+
batch_size,
|
|
164
|
+
on_conflict=on_conflict,
|
|
165
|
+
update_fields=update_fields,
|
|
166
|
+
unique_fields=unique_fields,
|
|
167
|
+
)
|
|
168
|
+
connection = connections[self.db]
|
|
169
|
+
if (
|
|
170
|
+
connection.features.can_return_rows_from_bulk_insert
|
|
171
|
+
and on_conflict is None
|
|
172
|
+
):
|
|
173
|
+
assert len(returned_columns) == len(objs_without_pk)
|
|
174
|
+
for obj_without_pk, results in zip(objs_without_pk, returned_columns):
|
|
175
|
+
for result, field in zip(results, opts.db_returning_fields):
|
|
176
|
+
setattr(obj_without_pk, field.attname, result)
|
|
177
|
+
obj_without_pk._state.adding = False
|
|
178
|
+
obj_without_pk._state.db = self.db
|
|
144
179
|
|
|
145
180
|
if not bypass_hooks:
|
|
146
|
-
engine.run(model_cls, AFTER_CREATE,
|
|
181
|
+
engine.run(model_cls, AFTER_CREATE, objs, ctx=ctx)
|
|
147
182
|
|
|
148
|
-
return
|
|
183
|
+
return objs
|
|
149
184
|
|
|
150
185
|
# --- Private helper methods (moved to bottom for clarity) ---
|
|
151
186
|
|
|
@@ -249,6 +284,8 @@ class BulkHookManager(models.Manager):
|
|
|
249
284
|
obj, child_model, parent_objects_map.get(id(obj), {})
|
|
250
285
|
)
|
|
251
286
|
child_objects.append(child_obj)
|
|
287
|
+
# Handle order_with_respect_to like Django's bulk_create
|
|
288
|
+
self._handle_order_with_respect_to(child_objects)
|
|
252
289
|
# If the child model is still MTI, call our own logic recursively
|
|
253
290
|
if len([p for p in child_model._meta.parents.keys() if not p._meta.proxy]) > 0:
|
|
254
291
|
# Build inheritance chain for the child model
|
|
@@ -362,8 +399,6 @@ class BulkHookManager(models.Manager):
|
|
|
362
399
|
if not bypass_hooks:
|
|
363
400
|
engine.run(model_cls, AFTER_DELETE, objs, ctx=ctx)
|
|
364
401
|
|
|
365
|
-
return objs
|
|
366
|
-
|
|
367
402
|
@transaction.atomic
|
|
368
403
|
def update(self, **kwargs):
|
|
369
404
|
objs = list(self.all())
|
|
@@ -393,3 +428,24 @@ class BulkHookManager(models.Manager):
|
|
|
393
428
|
else:
|
|
394
429
|
self.bulk_create([obj])
|
|
395
430
|
return obj
|
|
431
|
+
|
|
432
|
+
def _handle_order_with_respect_to(self, objs):
|
|
433
|
+
"""
|
|
434
|
+
Set _order fields for models with order_with_respect_to.
|
|
435
|
+
"""
|
|
436
|
+
for obj in objs:
|
|
437
|
+
order_with_respect_to = obj.__class__._meta.order_with_respect_to
|
|
438
|
+
if order_with_respect_to:
|
|
439
|
+
key = getattr(obj, order_with_respect_to.attname)
|
|
440
|
+
obj._order = key
|
|
441
|
+
# Group by the value of order_with_respect_to
|
|
442
|
+
groups = defaultdict(list)
|
|
443
|
+
for obj in objs:
|
|
444
|
+
order_with_respect_to = obj.__class__._meta.order_with_respect_to
|
|
445
|
+
if order_with_respect_to:
|
|
446
|
+
key = getattr(obj, order_with_respect_to.attname)
|
|
447
|
+
groups[key].append(obj)
|
|
448
|
+
# Enumerate within each group
|
|
449
|
+
for group_objs in groups.values():
|
|
450
|
+
for i, obj in enumerate(group_objs):
|
|
451
|
+
obj._order = i
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "django-bulk-hooks"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.118"
|
|
4
4
|
description = "Hook-style hooks for Django bulk operations like bulk_create and bulk_update."
|
|
5
5
|
authors = ["Konrad Beck <konrad.beck@merchantcapital.co.za>"]
|
|
6
6
|
readme = "README.md"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|