django-bulk-hooks 0.1.191__py3-none-any.whl → 0.1.193__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.

@@ -14,7 +14,7 @@ from django_bulk_hooks.constants import (
14
14
  VALIDATE_UPDATE,
15
15
  )
16
16
  from django_bulk_hooks.context import HookContext
17
- from django.db.models import When, Value, Case
17
+ from django.db.models import When, Value, Case, Field
18
18
 
19
19
 
20
20
  class HookQuerySetMixin:
@@ -183,17 +183,9 @@ class HookQuerySetMixin:
183
183
  """
184
184
  Bulk update objects in the database with MTI support.
185
185
  """
186
- print(f"\n=== BULK UPDATE DEBUG ===")
187
- print(f"Model: {self.model.__name__}")
188
- print(f"Number of objects: {len(objs)}")
189
- print(f"Fields: {fields}")
190
- print(f"Bypass hooks: {bypass_hooks}")
191
- print(f"Bypass validation: {bypass_validation}")
192
-
193
186
  model_cls = self.model
194
187
 
195
188
  if not objs:
196
- print("No objects to update")
197
189
  return []
198
190
 
199
191
  if any(not isinstance(obj, model_cls) for obj in objs):
@@ -207,11 +199,6 @@ class HookQuerySetMixin:
207
199
  if parent._meta.concrete_model is not model_cls._meta.concrete_model:
208
200
  is_mti = True
209
201
  break
210
-
211
- print(f"Is MTI: {is_mti}")
212
- print(f"Model concrete model: {model_cls._meta.concrete_model.__name__}")
213
- for parent in model_cls._meta.all_parents:
214
- print(f" Parent {parent.__name__}: concrete_model = {parent._meta.concrete_model.__name__}")
215
202
 
216
203
  if not bypass_hooks:
217
204
  # Load originals for hook comparison
@@ -238,14 +225,23 @@ class HookQuerySetMixin:
238
225
  fields_set = set(fields)
239
226
  fields_set.update(modified_fields)
240
227
  fields = list(fields_set)
241
- print(f"Modified fields detected: {modified_fields}")
228
+
229
+ # Handle auto_now fields like Django's update_or_create does
230
+ fields_set = set(fields)
231
+ pk_fields = model_cls._meta.pk_fields
232
+ for field in model_cls._meta.local_concrete_fields:
233
+ if not (
234
+ field in pk_fields or field.__class__.pre_save is Field.pre_save
235
+ ):
236
+ fields_set.add(field.name)
237
+ if field.name != field.attname:
238
+ fields_set.add(field.attname)
239
+ fields = list(fields_set)
242
240
 
243
241
  # Handle MTI models differently
244
242
  if is_mti:
245
- print("Using MTI bulk update logic")
246
243
  result = self._mti_bulk_update(objs, fields, **kwargs)
247
244
  else:
248
- print("Using standard Django bulk_update")
249
245
  # For single-table models, use Django's built-in bulk_update
250
246
  django_kwargs = {
251
247
  k: v
@@ -257,7 +253,6 @@ class HookQuerySetMixin:
257
253
  if not bypass_hooks:
258
254
  engine.run(model_cls, AFTER_UPDATE, objs, originals, ctx=ctx)
259
255
 
260
- print(f"Bulk update result: {result}")
261
256
  return result
262
257
 
263
258
  def _detect_modified_fields(self, new_instances, original_instances):
@@ -302,29 +297,20 @@ class HookQuerySetMixin:
302
297
  Get the complete inheritance chain from root parent to current model.
303
298
  Returns list of model classes in order: [RootParent, Parent, Child]
304
299
  """
305
- print(f"\n=== GET INHERITANCE CHAIN DEBUG ===")
306
- print(f"Current model: {self.model.__name__}")
307
-
308
300
  chain = []
309
301
  current_model = self.model
310
302
  while current_model:
311
- print(f"Processing model: {current_model.__name__}")
312
303
  if not current_model._meta.proxy:
313
304
  chain.append(current_model)
314
- print(f" Added to chain: {current_model.__name__}")
315
- else:
316
- print(f" Skipped proxy model: {current_model.__name__}")
317
305
 
318
306
  parents = [
319
307
  parent
320
308
  for parent in current_model._meta.parents.keys()
321
309
  if not parent._meta.proxy
322
310
  ]
323
- print(f" Parents: {[p.__name__ for p in parents]}")
324
311
  current_model = parents[0] if parents else None
325
312
 
326
313
  chain.reverse()
327
- print(f"Final inheritance chain: {[m.__name__ for m in chain]}")
328
314
  return chain
329
315
 
330
316
  def _mti_bulk_create(self, objs, inheritance_chain=None, **kwargs):
@@ -567,14 +553,8 @@ class HookQuerySetMixin:
567
553
  Custom bulk update implementation for MTI models.
568
554
  Updates each table in the inheritance chain efficiently using Django's batch_size.
569
555
  """
570
- print(f"\n=== MTI BULK UPDATE DEBUG ===")
571
- print(f"Model: {self.model.__name__}")
572
- print(f"Number of objects: {len(objs)}")
573
- print(f"Fields to update: {fields}")
574
-
575
556
  model_cls = self.model
576
557
  inheritance_chain = self._get_inheritance_chain()
577
- print(f"Inheritance chain: {[m.__name__ for m in inheritance_chain]}")
578
558
 
579
559
  # Remove custom hook kwargs before passing to Django internals
580
560
  django_kwargs = {
@@ -589,6 +569,12 @@ class HookQuerySetMixin:
589
569
  "Inheritance chain too deep - possible infinite recursion detected"
590
570
  )
591
571
 
572
+ # Handle auto_now fields by calling pre_save on objects
573
+ for obj in objs:
574
+ for field in model_cls._meta.local_fields:
575
+ if hasattr(field, "auto_now") and field.auto_now:
576
+ field.pre_save(obj, add=False)
577
+
592
578
  # Group fields by model in the inheritance chain
593
579
  field_groups = {}
594
580
  for field_name in fields:
@@ -599,28 +585,20 @@ class HookQuerySetMixin:
599
585
  if model not in field_groups:
600
586
  field_groups[model] = []
601
587
  field_groups[model].append(field_name)
602
- print(f"Field '{field_name}' belongs to model '{model.__name__}'")
603
588
  break
604
-
605
- print(f"Field groups: {field_groups}")
606
589
 
607
590
  # Process in batches
608
591
  batch_size = django_kwargs.get("batch_size") or len(objs)
609
592
  total_updated = 0
610
593
 
611
- print(f"Processing in batches of size: {batch_size}")
612
-
613
594
  with transaction.atomic(using=self.db, savepoint=False):
614
595
  for i in range(0, len(objs), batch_size):
615
596
  batch = objs[i : i + batch_size]
616
- print(f"\n--- Processing batch {i//batch_size + 1} ({len(batch)} objects) ---")
617
597
  batch_result = self._process_mti_bulk_update_batch(
618
598
  batch, field_groups, inheritance_chain, **django_kwargs
619
599
  )
620
600
  total_updated += batch_result
621
- print(f"Batch {i//batch_size + 1} updated {batch_result} rows")
622
601
 
623
- print(f"\n=== TOTAL UPDATED: {total_updated} ===")
624
602
  return total_updated
625
603
 
626
604
  def _process_mti_bulk_update_batch(self, batch, field_groups, inheritance_chain, **kwargs):
@@ -630,9 +608,6 @@ class HookQuerySetMixin:
630
608
  """
631
609
  total_updated = 0
632
610
 
633
- print(f"Processing batch with {len(batch)} objects")
634
- print(f"Field groups: {field_groups}")
635
-
636
611
  # For MTI, we need to handle parent links correctly
637
612
  # The root model (first in chain) has its own PK
638
613
  # Child models use the parent link to reference the root PK
@@ -649,31 +624,21 @@ class HookQuerySetMixin:
649
624
 
650
625
  if pk_value is not None:
651
626
  root_pks.append(pk_value)
652
- print(f"Found PK {pk_value} for object {obj}")
653
627
  else:
654
- print(f"WARNING: Object {obj} has no primary key (pk={getattr(obj, 'pk', None)}, id={getattr(obj, 'id', None)})")
655
628
  continue
656
629
 
657
- print(f"Root PKs to update: {root_pks}")
658
-
659
630
  if not root_pks:
660
- print("No valid primary keys found, skipping update")
661
631
  return 0
662
632
 
663
633
  # Update each table in the inheritance chain
664
634
  for model, model_fields in field_groups.items():
665
- print(f"\n--- Updating model: {model.__name__} ---")
666
- print(f"Fields to update: {model_fields}")
667
-
668
635
  if not model_fields:
669
- print("No fields to update, skipping")
670
636
  continue
671
637
 
672
638
  if model == inheritance_chain[0]:
673
639
  # Root model - use primary keys directly
674
640
  pks = root_pks
675
641
  filter_field = 'pk'
676
- print(f"Root model - using PKs: {pks}")
677
642
  else:
678
643
  # Child model - use parent link field
679
644
  parent_link = None
@@ -683,27 +648,19 @@ class HookQuerySetMixin:
683
648
  break
684
649
 
685
650
  if parent_link is None:
686
- print(f"No parent link found for {model.__name__}, skipping")
687
651
  continue
688
652
 
689
- print(f"Parent link field: {parent_link.name} ({parent_link.attname})")
690
-
691
653
  # For child models, the parent link values should be the same as root PKs
692
654
  pks = root_pks
693
655
  filter_field = parent_link.attname
694
- print(f"Child model - using parent link values: {pks}")
695
656
 
696
657
  if pks:
697
658
  base_qs = model._base_manager.using(self.db)
698
- print(f"Filter field: {filter_field}")
699
- print(f"PKs to filter by: {pks}")
700
659
 
701
660
  # Check if records exist
702
661
  existing_count = base_qs.filter(**{f"{filter_field}__in": pks}).count()
703
- print(f"Existing records with these PKs: {existing_count}")
704
662
 
705
663
  if existing_count == 0:
706
- print("WARNING: No existing records found with these PKs!")
707
664
  continue
708
665
 
709
666
  # Build CASE statements for each field to perform a single bulk update
@@ -712,7 +669,6 @@ class HookQuerySetMixin:
712
669
  field = model._meta.get_field(field_name)
713
670
  when_statements = []
714
671
 
715
- print(f"Building CASE statement for field: {field_name}")
716
672
  for pk, obj in zip(pks, batch):
717
673
  # Check both pk and id attributes for the object
718
674
  obj_pk = getattr(obj, 'pk', None)
@@ -722,26 +678,18 @@ class HookQuerySetMixin:
722
678
  if obj_pk is None:
723
679
  continue
724
680
  value = getattr(obj, field_name)
725
- print(f" PK {pk}: {field_name} = {value}")
726
681
  when_statements.append(When(**{filter_field: pk}, then=Value(value, output_field=field)))
727
682
 
728
683
  case_statements[field_name] = Case(*when_statements, output_field=field)
729
684
 
730
- print(f"Case statements built: {list(case_statements.keys())}")
731
-
732
685
  # Execute a single bulk update for all objects in this model
733
686
  try:
734
687
  updated_count = base_qs.filter(**{f"{filter_field}__in": pks}).update(**case_statements)
735
- print(f"UPDATE QUERY EXECUTED - Updated {updated_count} rows")
736
688
  total_updated += updated_count
737
689
  except Exception as e:
738
- print(f"ERROR during update: {e}")
739
690
  import traceback
740
691
  traceback.print_exc()
741
- else:
742
- print("No PKs found, skipping update")
743
692
 
744
- print(f"Batch total updated: {total_updated}")
745
693
  return total_updated
746
694
 
747
695
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.191
3
+ Version: 0.1.193
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=nfWiwU5-yAoxdnQsUMohxtyCpkV0MBv6X3wmipr9eQY,3697
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=eaeSmYW3VrJjhom_wXKzndmEkrKm-xaG0qflYhKSgIA,31663
12
+ django_bulk_hooks/queryset.py,sha256=nIygGty8Ex9F0Iz8CRIIDxkNZeT3M4LF1vgMh8_o1Jc,28678
13
13
  django_bulk_hooks/registry.py,sha256=-mQBizJ06nz_tajZBinViKx_uP2Tbc1tIpTEMv7lwKA,705
14
- django_bulk_hooks-0.1.191.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.191.dist-info/METADATA,sha256=mRfmlZrQZOUaD8dQ50605aqAzIOF8-Z7fxzjl0z_oyU,7418
16
- django_bulk_hooks-0.1.191.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.191.dist-info/RECORD,,
14
+ django_bulk_hooks-0.1.193.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
+ django_bulk_hooks-0.1.193.dist-info/METADATA,sha256=XkCe1wym7M3k0k3pcOOYSH6zdVxatdzziz12gDrEvwM,7418
16
+ django_bulk_hooks-0.1.193.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
+ django_bulk_hooks-0.1.193.dist-info/RECORD,,