django-bulk-hooks 0.1.179__py3-none-any.whl → 0.1.181__py3-none-any.whl
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/queryset.py +79 -31
- {django_bulk_hooks-0.1.179.dist-info → django_bulk_hooks-0.1.181.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.179.dist-info → django_bulk_hooks-0.1.181.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.179.dist-info → django_bulk_hooks-0.1.181.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.179.dist-info → django_bulk_hooks-0.1.181.dist-info}/WHEEL +0 -0
django_bulk_hooks/queryset.py
CHANGED
|
@@ -17,29 +17,27 @@ from django_bulk_hooks.context import HookContext
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class HookQuerySet(models.QuerySet):
|
|
20
|
-
CHUNK_SIZE = 200
|
|
21
|
-
|
|
22
20
|
@transaction.atomic
|
|
23
21
|
def delete(self):
|
|
24
22
|
objs = list(self)
|
|
25
23
|
if not objs:
|
|
26
24
|
return 0
|
|
27
|
-
|
|
25
|
+
|
|
28
26
|
model_cls = self.model
|
|
29
27
|
ctx = HookContext(model_cls)
|
|
30
|
-
|
|
28
|
+
|
|
31
29
|
# Run validation hooks first
|
|
32
30
|
engine.run(model_cls, VALIDATE_DELETE, objs, ctx=ctx)
|
|
33
|
-
|
|
31
|
+
|
|
34
32
|
# Then run business logic hooks
|
|
35
33
|
engine.run(model_cls, BEFORE_DELETE, objs, ctx=ctx)
|
|
36
|
-
|
|
34
|
+
|
|
37
35
|
# Use Django's standard delete() method
|
|
38
36
|
result = super().delete()
|
|
39
|
-
|
|
37
|
+
|
|
40
38
|
# Run AFTER_DELETE hooks
|
|
41
39
|
engine.run(model_cls, AFTER_DELETE, objs, ctx=ctx)
|
|
42
|
-
|
|
40
|
+
|
|
43
41
|
return result
|
|
44
42
|
|
|
45
43
|
@transaction.atomic
|
|
@@ -109,10 +107,10 @@ class HookQuerySet(models.QuerySet):
|
|
|
109
107
|
# trickier so it's not done yet.
|
|
110
108
|
if batch_size is not None and batch_size <= 0:
|
|
111
109
|
raise ValueError("Batch size must be a positive integer.")
|
|
112
|
-
|
|
110
|
+
|
|
113
111
|
if not objs:
|
|
114
112
|
return objs
|
|
115
|
-
|
|
113
|
+
|
|
116
114
|
if any(not isinstance(obj, model_cls) for obj in objs):
|
|
117
115
|
raise TypeError(
|
|
118
116
|
f"bulk_create expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
@@ -177,7 +175,7 @@ class HookQuerySet(models.QuerySet):
|
|
|
177
175
|
self, objs, fields, bypass_hooks=False, bypass_validation=False, **kwargs
|
|
178
176
|
):
|
|
179
177
|
"""
|
|
180
|
-
Bulk update objects in the database.
|
|
178
|
+
Bulk update objects in the database with MTI support.
|
|
181
179
|
"""
|
|
182
180
|
model_cls = self.model
|
|
183
181
|
|
|
@@ -189,9 +187,15 @@ class HookQuerySet(models.QuerySet):
|
|
|
189
187
|
f"bulk_update expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
190
188
|
)
|
|
191
189
|
|
|
190
|
+
# Check for MTI
|
|
191
|
+
is_mti = False
|
|
192
|
+
for parent in model_cls._meta.all_parents:
|
|
193
|
+
if parent._meta.concrete_model is not model_cls._meta.concrete_model:
|
|
194
|
+
is_mti = True
|
|
195
|
+
break
|
|
196
|
+
|
|
192
197
|
if not bypass_hooks:
|
|
193
|
-
# Load originals for hook comparison
|
|
194
|
-
# Use the base manager to avoid recursion
|
|
198
|
+
# Load originals for hook comparison
|
|
195
199
|
original_map = {
|
|
196
200
|
obj.pk: obj
|
|
197
201
|
for obj in model_cls._base_manager.filter(
|
|
@@ -207,28 +211,31 @@ class HookQuerySet(models.QuerySet):
|
|
|
207
211
|
engine.run(model_cls, VALIDATE_UPDATE, objs, originals, ctx=ctx)
|
|
208
212
|
|
|
209
213
|
# Then run business logic hooks
|
|
210
|
-
|
|
211
|
-
engine.run(model_cls, BEFORE_UPDATE, objs, originals, ctx=ctx)
|
|
214
|
+
engine.run(model_cls, BEFORE_UPDATE, objs, originals, ctx=ctx)
|
|
212
215
|
|
|
213
|
-
#
|
|
216
|
+
# Detect modified fields during hooks
|
|
214
217
|
modified_fields = self._detect_modified_fields(objs, originals)
|
|
215
218
|
if modified_fields:
|
|
216
|
-
# Convert to set for efficient union operation
|
|
217
219
|
fields_set = set(fields)
|
|
218
220
|
fields_set.update(modified_fields)
|
|
219
221
|
fields = list(fields_set)
|
|
220
222
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
# Handle MTI models differently
|
|
224
|
+
if is_mti:
|
|
225
|
+
result = self._mti_bulk_update(objs, fields, **kwargs)
|
|
226
|
+
else:
|
|
227
|
+
# For single-table models, use Django's built-in bulk_update
|
|
228
|
+
django_kwargs = {
|
|
229
|
+
k: v
|
|
230
|
+
for k, v in kwargs.items()
|
|
231
|
+
if k not in ["bypass_hooks", "bypass_validation"]
|
|
232
|
+
}
|
|
233
|
+
result = super().bulk_update(objs, fields, **django_kwargs)
|
|
227
234
|
|
|
228
235
|
if not bypass_hooks:
|
|
229
236
|
engine.run(model_cls, AFTER_UPDATE, objs, originals, ctx=ctx)
|
|
230
237
|
|
|
231
|
-
return
|
|
238
|
+
return result
|
|
232
239
|
|
|
233
240
|
def _detect_modified_fields(self, new_instances, original_instances):
|
|
234
241
|
"""
|
|
@@ -302,13 +309,6 @@ class HookQuerySet(models.QuerySet):
|
|
|
302
309
|
if inheritance_chain is None:
|
|
303
310
|
inheritance_chain = self._get_inheritance_chain()
|
|
304
311
|
|
|
305
|
-
print("inheritance_chain")
|
|
306
|
-
print(inheritance_chain)
|
|
307
|
-
|
|
308
|
-
for inheritance in inheritance_chain:
|
|
309
|
-
print(inheritance)
|
|
310
|
-
|
|
311
|
-
|
|
312
312
|
# Safety check to prevent infinite recursion
|
|
313
313
|
if len(inheritance_chain) > 10: # Arbitrary limit to prevent infinite loops
|
|
314
314
|
raise ValueError(
|
|
@@ -527,3 +527,51 @@ class HookQuerySet(models.QuerySet):
|
|
|
527
527
|
field.pre_save(child_obj, add=True)
|
|
528
528
|
|
|
529
529
|
return child_obj
|
|
530
|
+
|
|
531
|
+
def _mti_bulk_update(self, objs, fields, **kwargs):
|
|
532
|
+
"""
|
|
533
|
+
Custom bulk update implementation for MTI models.
|
|
534
|
+
Updates each table in the inheritance chain efficiently using Django's batch_size.
|
|
535
|
+
"""
|
|
536
|
+
model_cls = self.model
|
|
537
|
+
inheritance_chain = self._get_inheritance_chain()
|
|
538
|
+
|
|
539
|
+
# Group fields by model in the inheritance chain
|
|
540
|
+
field_groups = {}
|
|
541
|
+
for field_name in fields:
|
|
542
|
+
field = model_cls._meta.get_field(field_name)
|
|
543
|
+
# Find which model in the inheritance chain this field belongs to
|
|
544
|
+
for model in inheritance_chain:
|
|
545
|
+
if field in model._meta.local_fields:
|
|
546
|
+
if model not in field_groups:
|
|
547
|
+
field_groups[model] = []
|
|
548
|
+
field_groups[model].append(field_name)
|
|
549
|
+
break
|
|
550
|
+
|
|
551
|
+
# Update each table in the inheritance chain
|
|
552
|
+
total_updated = 0
|
|
553
|
+
for model, model_fields in field_groups.items():
|
|
554
|
+
if not model_fields:
|
|
555
|
+
continue
|
|
556
|
+
|
|
557
|
+
# Get objects that have this model's fields
|
|
558
|
+
model_objs = []
|
|
559
|
+
for obj in objs:
|
|
560
|
+
# Create a temporary object with just this model's fields
|
|
561
|
+
temp_obj = model()
|
|
562
|
+
temp_obj.pk = obj.pk # Set the primary key
|
|
563
|
+
|
|
564
|
+
# Copy only the fields for this model
|
|
565
|
+
for field_name in model_fields:
|
|
566
|
+
if hasattr(obj, field_name):
|
|
567
|
+
setattr(temp_obj, field_name, getattr(obj, field_name))
|
|
568
|
+
|
|
569
|
+
model_objs.append(temp_obj)
|
|
570
|
+
|
|
571
|
+
# Use Django's bulk_update for this model's table
|
|
572
|
+
# This will automatically use batch_size from kwargs
|
|
573
|
+
base_qs = model._base_manager.using(self.db)
|
|
574
|
+
updated_count = base_qs.bulk_update(model_objs, model_fields, **kwargs)
|
|
575
|
+
total_updated += updated_count
|
|
576
|
+
|
|
577
|
+
return total_updated
|
|
@@ -9,9 +9,9 @@ django_bulk_hooks/handler.py,sha256=xZt8iNdYF-ACz-MnKMY0co6scWINU5V5wC1lyDn844k,
|
|
|
9
9
|
django_bulk_hooks/manager.py,sha256=KLEjpQRt4WlzgBAf_X3XOAPUQM8Jmc1fIt8yr62FPQc,3044
|
|
10
10
|
django_bulk_hooks/models.py,sha256=7fnx5xd4HWXfLVlFhhiRzR92JRWFEuxgk6aSWLEsyJg,3996
|
|
11
11
|
django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
|
|
12
|
-
django_bulk_hooks/queryset.py,sha256
|
|
12
|
+
django_bulk_hooks/queryset.py,sha256=VRTivPjxF51uAPAOJ-Ytchg7NDl9EmiEWIBMOQ00Bos,24359
|
|
13
13
|
django_bulk_hooks/registry.py,sha256=-mQBizJ06nz_tajZBinViKx_uP2Tbc1tIpTEMv7lwKA,705
|
|
14
|
-
django_bulk_hooks-0.1.
|
|
15
|
-
django_bulk_hooks-0.1.
|
|
16
|
-
django_bulk_hooks-0.1.
|
|
17
|
-
django_bulk_hooks-0.1.
|
|
14
|
+
django_bulk_hooks-0.1.181.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
+
django_bulk_hooks-0.1.181.dist-info/METADATA,sha256=P_xiN8oxEXO0ryUGTnvH6S5ias0FldD9UAEQBgY27G4,6951
|
|
16
|
+
django_bulk_hooks-0.1.181.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
17
|
+
django_bulk_hooks-0.1.181.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|