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

@@ -627,17 +627,24 @@ class HookQuerySetMixin:
627
627
  # CRITICAL: Exclude auto_now fields from update_fields for existing records
628
628
  # This prevents Django from including them in the ON CONFLICT DO UPDATE clause
629
629
  if existing_records and update_fields:
630
+ logger.debug(f"Processing {len(existing_records)} existing records with update_fields: {update_fields}")
631
+
630
632
  # Identify auto_now fields that should be excluded from updates
631
633
  auto_now_fields_to_exclude = set()
632
634
  for field in model_cls._meta.local_fields:
633
635
  if hasattr(field, "auto_now") and field.auto_now:
634
636
  auto_now_fields_to_exclude.add(field.name)
635
637
 
638
+ logger.debug(f"Found auto_now fields: {auto_now_fields_to_exclude}")
639
+
636
640
  # Filter out auto_now fields from update_fields for existing records
637
641
  if auto_now_fields_to_exclude:
638
642
  # Create a filtered version of update_fields that excludes auto_now fields
639
643
  filtered_update_fields = [f for f in update_fields if f not in auto_now_fields_to_exclude]
640
644
 
645
+ logger.debug(f"Filtered update_fields: {filtered_update_fields}")
646
+ logger.debug(f"Excluded auto_now fields: {auto_now_fields_to_exclude}")
647
+
641
648
  # Store the original update_fields to restore later
642
649
  ctx.original_update_fields = update_fields
643
650
  ctx.auto_now_fields_excluded = auto_now_fields_to_exclude
@@ -645,6 +652,12 @@ class HookQuerySetMixin:
645
652
  # Use the filtered update_fields for the database operation
646
653
  # This prevents Django from overwriting the timestamps during upsert
647
654
  update_fields = filtered_update_fields
655
+
656
+ logger.debug(f"Final update_fields for DB operation: {update_fields}")
657
+ else:
658
+ logger.debug("No auto_now fields found to exclude")
659
+ else:
660
+ logger.debug(f"No existing records or update_fields to process. existing_records: {len(existing_records) if existing_records else 0}, update_fields: {update_fields}")
648
661
 
649
662
  # Run validation hooks on all records
650
663
  if not bypass_validation:
@@ -694,6 +707,10 @@ class HookQuerySetMixin:
694
707
  # but we need to call it on the base manager to avoid recursion
695
708
  # Filter out custom parameters that Django's bulk_create doesn't accept
696
709
 
710
+ logger.debug(f"Calling Django bulk_create with update_fields: {update_fields}")
711
+ logger.debug(f"Calling Django bulk_create with update_conflicts: {update_conflicts}")
712
+ logger.debug(f"Calling Django bulk_create with unique_fields: {unique_fields}")
713
+
697
714
  result = super().bulk_create(
698
715
  objs,
699
716
  batch_size=batch_size,
@@ -702,15 +719,19 @@ class HookQuerySetMixin:
702
719
  update_fields=update_fields,
703
720
  unique_fields=unique_fields,
704
721
  )
722
+
723
+ logger.debug(f"Django bulk_create completed with result: {result}")
705
724
 
706
725
  # Fire AFTER hooks
707
726
  if not bypass_hooks:
708
727
  if update_conflicts and unique_fields:
709
728
  # Restore original update_fields if we modified them
710
729
  if hasattr(ctx, 'original_update_fields'):
730
+ logger.debug(f"Restoring original update_fields: {ctx.original_update_fields}")
711
731
  update_fields = ctx.original_update_fields
712
732
  delattr(ctx, 'original_update_fields')
713
733
  delattr(ctx, 'auto_now_fields_excluded')
734
+ logger.debug(f"Restored update_fields: {update_fields}")
714
735
 
715
736
  # For upsert operations, we need to determine which records were actually created vs updated
716
737
  # Use the same logic as before to separate records
@@ -794,7 +815,7 @@ class HookQuerySetMixin:
794
815
  )
795
816
 
796
817
  logger.debug(
797
- f"bulk_update {model_cls.__name__} bypass_hooks={bypass_hooks} objs={len(objs)}"
818
+ f"bulk_update {model_cls.__name__} bypass_hooks={bypass_hooks} objs={len(objs)} fields={fields}"
798
819
  )
799
820
 
800
821
  # Check for MTI
@@ -818,15 +839,35 @@ class HookQuerySetMixin:
818
839
  # Handle auto_now fields like Django's update_or_create does
819
840
  fields_set = set(fields)
820
841
  pk_fields = model_cls._meta.pk_fields
842
+ auto_now_fields = []
843
+ logger.debug(f"Checking for auto_now fields in {model_cls.__name__}")
821
844
  for field in model_cls._meta.local_concrete_fields:
822
845
  # Only add auto_now fields (like updated_at) that aren't already in the fields list
823
846
  # Don't include auto_now_add fields (like created_at) as they should only be set on creation
824
847
  if hasattr(field, "auto_now") and field.auto_now:
848
+ logger.debug(f"Found auto_now field: {field.name}")
825
849
  if field.name not in fields_set and field.name not in pk_fields:
826
850
  fields_set.add(field.name)
827
851
  if field.name != field.attname:
828
852
  fields_set.add(field.attname)
853
+ auto_now_fields.append(field.name)
854
+ logger.debug(f"Added auto_now field {field.name} to fields list")
855
+ else:
856
+ logger.debug(f"Auto_now field {field.name} already in fields list or is PK")
857
+ elif hasattr(field, "auto_now_add") and field.auto_now_add:
858
+ logger.debug(f"Found auto_now_add field: {field.name} (skipping)")
859
+
860
+ logger.debug(f"Auto_now fields detected: {auto_now_fields}")
829
861
  fields = list(fields_set)
862
+
863
+ # Set auto_now field values to current timestamp
864
+ if auto_now_fields:
865
+ from django.utils import timezone
866
+ current_time = timezone.now()
867
+ logger.debug(f"Setting auto_now fields {auto_now_fields} to current time: {current_time}")
868
+ for obj in objs:
869
+ for field_name in auto_now_fields:
870
+ setattr(obj, field_name, current_time)
830
871
 
831
872
  # Handle MTI models differently
832
873
  if is_mti:
@@ -839,17 +880,20 @@ class HookQuerySetMixin:
839
880
  if k not in ["bypass_hooks", "bypass_validation"]
840
881
  }
841
882
  logger.debug("Calling Django bulk_update")
842
- # Build a per-object concrete value map to avoid leaking expressions into hooks
843
- value_map = {}
844
- for obj in objs:
845
- if obj.pk is None:
846
- continue
847
- field_values = {}
848
- for field_name in fields:
849
- # Capture raw values assigned on the object (not expressions)
850
- field_values[field_name] = getattr(obj, field_name)
851
- if field_values:
852
- value_map[obj.pk] = field_values
883
+ # Build a per-object concrete value map to avoid leaking expressions into hooks
884
+ value_map = {}
885
+ logger.debug(f"Building value map for {len(objs)} objects with fields: {fields}")
886
+ for obj in objs:
887
+ if obj.pk is None:
888
+ continue
889
+ field_values = {}
890
+ for field_name in fields:
891
+ # Capture raw values assigned on the object (not expressions)
892
+ field_values[field_name] = getattr(obj, field_name)
893
+ if field_name in auto_now_fields:
894
+ logger.debug(f"Object {obj.pk} {field_name}: {field_values[field_name]}")
895
+ if field_values:
896
+ value_map[obj.pk] = field_values
853
897
 
854
898
  # Make the value map available to the subsequent update() call
855
899
  if value_map:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.254
3
+ Version: 0.1.256
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=Bx-W6yyiciKMyy-BRxUt3CmRPCrX9_LhQgU-5LaJTjg,
9
9
  django_bulk_hooks/manager.py,sha256=nfWiwU5-yAoxdnQsUMohxtyCpkV0MBv6X3wmipr9eQY,3697
10
10
  django_bulk_hooks/models.py,sha256=WtSfc4GBOG_oOt8n37cVvid0MtFIGze9JYKSixil2y0,4370
11
11
  django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
12
- django_bulk_hooks/queryset.py,sha256=uuibzsusLx-7g2d-vngOS1RF3Y9SXhqSboudf0ch1DA,66207
12
+ django_bulk_hooks/queryset.py,sha256=DgKHRj43fOGWwDLfmp9SHLGxT11mNkQrmzcHTETi2xY,69024
13
13
  django_bulk_hooks/registry.py,sha256=GRUTGVQEO2sdkC9OaZ9Q3U7mM-3Ix83uTyvrlTtpatw,1317
14
- django_bulk_hooks-0.1.254.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.254.dist-info/METADATA,sha256=VxFyyCmAliymcO_xC9o5SY3phyXPeV53kskNhA3AAXk,9061
16
- django_bulk_hooks-0.1.254.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.254.dist-info/RECORD,,
14
+ django_bulk_hooks-0.1.256.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
+ django_bulk_hooks-0.1.256.dist-info/METADATA,sha256=N9BFHYP-N97zDM31wH7B9kZ811CaToZTJ1bmysMeN0E,9061
16
+ django_bulk_hooks-0.1.256.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
+ django_bulk_hooks-0.1.256.dist-info/RECORD,,