django-bulk-hooks 0.1.185__py3-none-any.whl → 0.1.187__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 +77 -5
- {django_bulk_hooks-0.1.185.dist-info → django_bulk_hooks-0.1.187.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.185.dist-info → django_bulk_hooks-0.1.187.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.185.dist-info → django_bulk_hooks-0.1.187.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.185.dist-info → django_bulk_hooks-0.1.187.dist-info}/WHEEL +0 -0
django_bulk_hooks/queryset.py
CHANGED
|
@@ -178,9 +178,17 @@ class HookQuerySet(models.QuerySet):
|
|
|
178
178
|
"""
|
|
179
179
|
Bulk update objects in the database with MTI support.
|
|
180
180
|
"""
|
|
181
|
+
print(f"\n=== BULK UPDATE DEBUG ===")
|
|
182
|
+
print(f"Model: {self.model.__name__}")
|
|
183
|
+
print(f"Number of objects: {len(objs)}")
|
|
184
|
+
print(f"Fields: {fields}")
|
|
185
|
+
print(f"Bypass hooks: {bypass_hooks}")
|
|
186
|
+
print(f"Bypass validation: {bypass_validation}")
|
|
187
|
+
|
|
181
188
|
model_cls = self.model
|
|
182
189
|
|
|
183
190
|
if not objs:
|
|
191
|
+
print("No objects to update")
|
|
184
192
|
return []
|
|
185
193
|
|
|
186
194
|
if any(not isinstance(obj, model_cls) for obj in objs):
|
|
@@ -194,6 +202,11 @@ class HookQuerySet(models.QuerySet):
|
|
|
194
202
|
if parent._meta.concrete_model is not model_cls._meta.concrete_model:
|
|
195
203
|
is_mti = True
|
|
196
204
|
break
|
|
205
|
+
|
|
206
|
+
print(f"Is MTI: {is_mti}")
|
|
207
|
+
print(f"Model concrete model: {model_cls._meta.concrete_model.__name__}")
|
|
208
|
+
for parent in model_cls._meta.all_parents:
|
|
209
|
+
print(f" Parent {parent.__name__}: concrete_model = {parent._meta.concrete_model.__name__}")
|
|
197
210
|
|
|
198
211
|
if not bypass_hooks:
|
|
199
212
|
# Load originals for hook comparison
|
|
@@ -220,11 +233,14 @@ class HookQuerySet(models.QuerySet):
|
|
|
220
233
|
fields_set = set(fields)
|
|
221
234
|
fields_set.update(modified_fields)
|
|
222
235
|
fields = list(fields_set)
|
|
236
|
+
print(f"Modified fields detected: {modified_fields}")
|
|
223
237
|
|
|
224
238
|
# Handle MTI models differently
|
|
225
239
|
if is_mti:
|
|
240
|
+
print("Using MTI bulk update logic")
|
|
226
241
|
result = self._mti_bulk_update(objs, fields, **kwargs)
|
|
227
242
|
else:
|
|
243
|
+
print("Using standard Django bulk_update")
|
|
228
244
|
# For single-table models, use Django's built-in bulk_update
|
|
229
245
|
django_kwargs = {
|
|
230
246
|
k: v
|
|
@@ -236,6 +252,7 @@ class HookQuerySet(models.QuerySet):
|
|
|
236
252
|
if not bypass_hooks:
|
|
237
253
|
engine.run(model_cls, AFTER_UPDATE, objs, originals, ctx=ctx)
|
|
238
254
|
|
|
255
|
+
print(f"Bulk update result: {result}")
|
|
239
256
|
return result
|
|
240
257
|
|
|
241
258
|
def _detect_modified_fields(self, new_instances, original_instances):
|
|
@@ -280,18 +297,29 @@ class HookQuerySet(models.QuerySet):
|
|
|
280
297
|
Get the complete inheritance chain from root parent to current model.
|
|
281
298
|
Returns list of model classes in order: [RootParent, Parent, Child]
|
|
282
299
|
"""
|
|
300
|
+
print(f"\n=== GET INHERITANCE CHAIN DEBUG ===")
|
|
301
|
+
print(f"Current model: {self.model.__name__}")
|
|
302
|
+
|
|
283
303
|
chain = []
|
|
284
304
|
current_model = self.model
|
|
285
305
|
while current_model:
|
|
306
|
+
print(f"Processing model: {current_model.__name__}")
|
|
286
307
|
if not current_model._meta.proxy:
|
|
287
308
|
chain.append(current_model)
|
|
309
|
+
print(f" Added to chain: {current_model.__name__}")
|
|
310
|
+
else:
|
|
311
|
+
print(f" Skipped proxy model: {current_model.__name__}")
|
|
312
|
+
|
|
288
313
|
parents = [
|
|
289
314
|
parent
|
|
290
315
|
for parent in current_model._meta.parents.keys()
|
|
291
316
|
if not parent._meta.proxy
|
|
292
317
|
]
|
|
318
|
+
print(f" Parents: {[p.__name__ for p in parents]}")
|
|
293
319
|
current_model = parents[0] if parents else None
|
|
320
|
+
|
|
294
321
|
chain.reverse()
|
|
322
|
+
print(f"Final inheritance chain: {[m.__name__ for m in chain]}")
|
|
295
323
|
return chain
|
|
296
324
|
|
|
297
325
|
def _mti_bulk_create(self, objs, inheritance_chain=None, **kwargs):
|
|
@@ -321,13 +349,13 @@ class HookQuerySet(models.QuerySet):
|
|
|
321
349
|
with transaction.atomic(using=self.db, savepoint=False):
|
|
322
350
|
for i in range(0, len(objs), batch_size):
|
|
323
351
|
batch = objs[i : i + batch_size]
|
|
324
|
-
batch_result = self.
|
|
352
|
+
batch_result = self._process_mti_bulk_create_batch(
|
|
325
353
|
batch, inheritance_chain, **django_kwargs
|
|
326
354
|
)
|
|
327
355
|
created_objects.extend(batch_result)
|
|
328
356
|
return created_objects
|
|
329
357
|
|
|
330
|
-
def
|
|
358
|
+
def _process_mti_bulk_create_batch(self, batch, inheritance_chain, **kwargs):
|
|
331
359
|
"""
|
|
332
360
|
Process a single batch of objects through the inheritance chain.
|
|
333
361
|
Implements Django's suggested workaround #2: O(n) normal inserts into parent
|
|
@@ -534,8 +562,14 @@ class HookQuerySet(models.QuerySet):
|
|
|
534
562
|
Custom bulk update implementation for MTI models.
|
|
535
563
|
Updates each table in the inheritance chain efficiently using Django's batch_size.
|
|
536
564
|
"""
|
|
565
|
+
print(f"\n=== MTI BULK UPDATE DEBUG ===")
|
|
566
|
+
print(f"Model: {self.model.__name__}")
|
|
567
|
+
print(f"Number of objects: {len(objs)}")
|
|
568
|
+
print(f"Fields to update: {fields}")
|
|
569
|
+
|
|
537
570
|
model_cls = self.model
|
|
538
571
|
inheritance_chain = self._get_inheritance_chain()
|
|
572
|
+
print(f"Inheritance chain: {[m.__name__ for m in inheritance_chain]}")
|
|
539
573
|
|
|
540
574
|
# Remove custom hook kwargs before passing to Django internals
|
|
541
575
|
django_kwargs = {
|
|
@@ -560,20 +594,28 @@ class HookQuerySet(models.QuerySet):
|
|
|
560
594
|
if model not in field_groups:
|
|
561
595
|
field_groups[model] = []
|
|
562
596
|
field_groups[model].append(field_name)
|
|
597
|
+
print(f"Field '{field_name}' belongs to model '{model.__name__}'")
|
|
563
598
|
break
|
|
599
|
+
|
|
600
|
+
print(f"Field groups: {field_groups}")
|
|
564
601
|
|
|
565
602
|
# Process in batches
|
|
566
603
|
batch_size = django_kwargs.get("batch_size") or len(objs)
|
|
567
604
|
total_updated = 0
|
|
568
605
|
|
|
606
|
+
print(f"Processing in batches of size: {batch_size}")
|
|
607
|
+
|
|
569
608
|
with transaction.atomic(using=self.db, savepoint=False):
|
|
570
609
|
for i in range(0, len(objs), batch_size):
|
|
571
610
|
batch = objs[i : i + batch_size]
|
|
611
|
+
print(f"\n--- Processing batch {i//batch_size + 1} ({len(batch)} objects) ---")
|
|
572
612
|
batch_result = self._process_mti_bulk_update_batch(
|
|
573
613
|
batch, field_groups, inheritance_chain, **django_kwargs
|
|
574
614
|
)
|
|
575
615
|
total_updated += batch_result
|
|
616
|
+
print(f"Batch {i//batch_size + 1} updated {batch_result} rows")
|
|
576
617
|
|
|
618
|
+
print(f"\n=== TOTAL UPDATED: {total_updated} ===")
|
|
577
619
|
return total_updated
|
|
578
620
|
|
|
579
621
|
def _process_mti_bulk_update_batch(self, batch, field_groups, inheritance_chain, **kwargs):
|
|
@@ -583,9 +625,16 @@ class HookQuerySet(models.QuerySet):
|
|
|
583
625
|
"""
|
|
584
626
|
total_updated = 0
|
|
585
627
|
|
|
628
|
+
print(f"Processing batch with {len(batch)} objects")
|
|
629
|
+
print(f"Field groups: {field_groups}")
|
|
630
|
+
|
|
586
631
|
# Update each table in the inheritance chain
|
|
587
632
|
for model, model_fields in field_groups.items():
|
|
633
|
+
print(f"\n--- Updating model: {model.__name__} ---")
|
|
634
|
+
print(f"Fields to update: {model_fields}")
|
|
635
|
+
|
|
588
636
|
if not model_fields:
|
|
637
|
+
print("No fields to update, skipping")
|
|
589
638
|
continue
|
|
590
639
|
|
|
591
640
|
# For MTI, we need to handle parent links correctly
|
|
@@ -595,6 +644,7 @@ class HookQuerySet(models.QuerySet):
|
|
|
595
644
|
# Root model - use primary keys directly
|
|
596
645
|
pks = [obj.pk for obj in batch]
|
|
597
646
|
filter_field = 'pk'
|
|
647
|
+
print(f"Root model - using PKs: {pks}")
|
|
598
648
|
else:
|
|
599
649
|
# Child model - use parent link field
|
|
600
650
|
parent_link = None
|
|
@@ -604,15 +654,24 @@ class HookQuerySet(models.QuerySet):
|
|
|
604
654
|
break
|
|
605
655
|
|
|
606
656
|
if parent_link is None:
|
|
607
|
-
|
|
657
|
+
print(f"No parent link found for {model.__name__}, skipping")
|
|
608
658
|
continue
|
|
609
659
|
|
|
660
|
+
print(f"Parent link field: {parent_link.name} ({parent_link.attname})")
|
|
661
|
+
|
|
610
662
|
# Get the parent link values (these should be the same as the root PKs)
|
|
611
663
|
pks = [getattr(obj, parent_link.attname) for obj in batch]
|
|
612
664
|
filter_field = parent_link.attname
|
|
665
|
+
print(f"Child model - using parent link values: {pks}")
|
|
613
666
|
|
|
614
667
|
if pks:
|
|
615
668
|
base_qs = model._base_manager.using(self.db)
|
|
669
|
+
print(f"Filter field: {filter_field}")
|
|
670
|
+
print(f"PKs to filter by: {pks}")
|
|
671
|
+
|
|
672
|
+
# Check if records exist
|
|
673
|
+
existing_count = base_qs.filter(**{f"{filter_field}__in": pks}).count()
|
|
674
|
+
print(f"Existing records with these PKs: {existing_count}")
|
|
616
675
|
|
|
617
676
|
# Build CASE statements for each field to perform a single bulk update
|
|
618
677
|
case_statements = {}
|
|
@@ -620,14 +679,27 @@ class HookQuerySet(models.QuerySet):
|
|
|
620
679
|
field = model._meta.get_field(field_name)
|
|
621
680
|
when_statements = []
|
|
622
681
|
|
|
682
|
+
print(f"Building CASE statement for field: {field_name}")
|
|
623
683
|
for pk, obj in zip(pks, batch):
|
|
624
684
|
value = getattr(obj, field_name)
|
|
685
|
+
print(f" PK {pk}: {field_name} = {value}")
|
|
625
686
|
when_statements.append(When(**{filter_field: pk}, then=Value(value, output_field=field)))
|
|
626
687
|
|
|
627
688
|
case_statements[field_name] = Case(*when_statements, output_field=field)
|
|
628
689
|
|
|
690
|
+
print(f"Case statements built: {list(case_statements.keys())}")
|
|
691
|
+
|
|
629
692
|
# Execute a single bulk update for all objects in this model
|
|
630
|
-
|
|
631
|
-
|
|
693
|
+
try:
|
|
694
|
+
updated_count = base_qs.filter(**{f"{filter_field}__in": pks}).update(**case_statements)
|
|
695
|
+
print(f"UPDATE QUERY EXECUTED - Updated {updated_count} rows")
|
|
696
|
+
total_updated += updated_count
|
|
697
|
+
except Exception as e:
|
|
698
|
+
print(f"ERROR during update: {e}")
|
|
699
|
+
import traceback
|
|
700
|
+
traceback.print_exc()
|
|
701
|
+
else:
|
|
702
|
+
print("No PKs found, skipping update")
|
|
632
703
|
|
|
704
|
+
print(f"Batch total updated: {total_updated}")
|
|
633
705
|
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=DYt_oK8g7D1oHGIT9gzE--F9qdXKaMEgPJq5x-t9cSw,30664
|
|
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.187.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
+
django_bulk_hooks-0.1.187.dist-info/METADATA,sha256=TgXRIiryaRvkIg_IJ5l9xRaGCwkQqsAahFpNff_eymQ,6951
|
|
16
|
+
django_bulk_hooks-0.1.187.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
17
|
+
django_bulk_hooks-0.1.187.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|