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.

@@ -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._process_mti_batch(
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 _process_mti_batch(self, batch, inheritance_chain, **kwargs):
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
- # This shouldn't happen in proper MTI, but handle gracefully
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
- updated_count = base_qs.filter(**{f"{filter_field}__in": pks}).update(**case_statements)
631
- total_updated += updated_count
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.185
3
+ Version: 0.1.187
4
4
  Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
5
5
  License: MIT
6
6
  Keywords: django,bulk,hooks
@@ -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=9ME8FNy_DdF6As-q1mb81u7agzfMR2rNGz6NlVB5bRI,26976
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.185.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.185.dist-info/METADATA,sha256=KO4qnnw3JdSvWDFUiXipL5SIHPKm79HufxSJBNqgDTk,6951
16
- django_bulk_hooks-0.1.185.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.185.dist-info/RECORD,,
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,,