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

@@ -575,6 +575,77 @@ class HookQuerySetMixin:
575
575
  # If no unique fields, treat all as new
576
576
  new_records = objs
577
577
 
578
+ # Handle auto_now fields intelligently for upsert operations
579
+ # Only set auto_now fields on records that will actually be created
580
+ for obj in new_records:
581
+ for field in model_cls._meta.local_fields:
582
+ if hasattr(field, "auto_now") and field.auto_now:
583
+ field.pre_save(obj, add=True)
584
+ elif hasattr(field, "auto_now_add") and field.auto_now_add:
585
+ if getattr(obj, field.name) is None:
586
+ field.pre_save(obj, add=True)
587
+
588
+ # For existing records, preserve their original auto_now values
589
+ # We'll need to fetch them from the database to preserve the timestamps
590
+ if existing_records:
591
+ # Get the unique field values for existing records
592
+ existing_unique_values = []
593
+ for obj in existing_records:
594
+ unique_value = {}
595
+ for field_name in unique_fields:
596
+ if hasattr(obj, field_name):
597
+ unique_value[field_name] = getattr(obj, field_name)
598
+ if unique_value:
599
+ existing_unique_values.append(unique_value)
600
+
601
+ if existing_unique_values:
602
+ # Build filter to fetch existing records
603
+ existing_filters = Q()
604
+ for unique_value in existing_unique_values:
605
+ filter_kwargs = {}
606
+ for field_name, value in unique_value.items():
607
+ filter_kwargs[field_name] = value
608
+ existing_filters |= Q(**filter_kwargs)
609
+
610
+ # Fetch existing records to preserve their auto_now values
611
+ existing_db_records = model_cls.objects.filter(existing_filters)
612
+ existing_db_map = {}
613
+ for db_record in existing_db_records:
614
+ key = tuple(getattr(db_record, field) for field in unique_fields)
615
+ existing_db_map[key] = db_record
616
+
617
+ # Preserve auto_now field values for existing records
618
+ for obj in existing_records:
619
+ key = tuple(getattr(obj, field) for field in unique_fields)
620
+ if key in existing_db_map:
621
+ db_record = existing_db_map[key]
622
+ for field in model_cls._meta.local_fields:
623
+ if hasattr(field, "auto_now") and field.auto_now:
624
+ # Preserve the original updated_at timestamp
625
+ setattr(obj, field.name, getattr(db_record, field.name))
626
+
627
+ # CRITICAL: Exclude auto_now fields from update_fields for existing records
628
+ # This prevents Django from including them in the ON CONFLICT DO UPDATE clause
629
+ if existing_records and update_fields:
630
+ # Identify auto_now fields that should be excluded from updates
631
+ auto_now_fields_to_exclude = set()
632
+ for field in model_cls._meta.local_fields:
633
+ if hasattr(field, "auto_now") and field.auto_now:
634
+ auto_now_fields_to_exclude.add(field.name)
635
+
636
+ # Filter out auto_now fields from update_fields for existing records
637
+ if auto_now_fields_to_exclude:
638
+ # Create a filtered version of update_fields that excludes auto_now fields
639
+ filtered_update_fields = [f for f in update_fields if f not in auto_now_fields_to_exclude]
640
+
641
+ # Store the original update_fields to restore later
642
+ ctx.original_update_fields = update_fields
643
+ ctx.auto_now_fields_excluded = auto_now_fields_to_exclude
644
+
645
+ # Use the filtered update_fields for the database operation
646
+ # This prevents Django from overwriting the timestamps during upsert
647
+ update_fields = filtered_update_fields
648
+
578
649
  # Run validation hooks on all records
579
650
  if not bypass_validation:
580
651
  engine.run(model_cls, VALIDATE_CREATE, objs, ctx=ctx)
@@ -586,6 +657,15 @@ class HookQuerySetMixin:
586
657
  engine.run(model_cls, BEFORE_UPDATE, existing_records, ctx=ctx)
587
658
  else:
588
659
  # For regular create operations, run create hooks before DB ops
660
+ # Handle auto_now fields normally for new records
661
+ for obj in objs:
662
+ for field in model_cls._meta.local_fields:
663
+ if hasattr(field, "auto_now") and field.auto_now:
664
+ field.pre_save(obj, add=True)
665
+ elif hasattr(field, "auto_now_add") and field.auto_now_add:
666
+ if getattr(obj, field.name) is None:
667
+ field.pre_save(obj, add=True)
668
+
589
669
  if not bypass_validation:
590
670
  engine.run(model_cls, VALIDATE_CREATE, objs, ctx=ctx)
591
671
  engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
@@ -626,6 +706,12 @@ class HookQuerySetMixin:
626
706
  # Fire AFTER hooks
627
707
  if not bypass_hooks:
628
708
  if update_conflicts and unique_fields:
709
+ # Restore original update_fields if we modified them
710
+ if hasattr(ctx, 'original_update_fields'):
711
+ update_fields = ctx.original_update_fields
712
+ delattr(ctx, 'original_update_fields')
713
+ delattr(ctx, 'auto_now_fields_excluded')
714
+
629
715
  # For upsert operations, we need to determine which records were actually created vs updated
630
716
  # Use the same logic as before to separate records
631
717
  existing_records = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.252
3
+ Version: 0.1.254
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=whv6QunaxRhos0nBlx600mKeleovjdv3XdeMSULh7n8,60739
12
+ django_bulk_hooks/queryset.py,sha256=uuibzsusLx-7g2d-vngOS1RF3Y9SXhqSboudf0ch1DA,66207
13
13
  django_bulk_hooks/registry.py,sha256=GRUTGVQEO2sdkC9OaZ9Q3U7mM-3Ix83uTyvrlTtpatw,1317
14
- django_bulk_hooks-0.1.252.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.252.dist-info/METADATA,sha256=xg9nPjRhZJPXem1plWls94mzENbAAoNiWI43J931rpE,9061
16
- django_bulk_hooks-0.1.252.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.252.dist-info/RECORD,,
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,,