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.
- django_bulk_hooks/queryset.py +86 -0
- {django_bulk_hooks-0.1.252.dist-info → django_bulk_hooks-0.1.254.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.252.dist-info → django_bulk_hooks-0.1.254.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.252.dist-info → django_bulk_hooks-0.1.254.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.252.dist-info → django_bulk_hooks-0.1.254.dist-info}/WHEEL +0 -0
django_bulk_hooks/queryset.py
CHANGED
|
@@ -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 = []
|
|
@@ -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=
|
|
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.
|
|
15
|
-
django_bulk_hooks-0.1.
|
|
16
|
-
django_bulk_hooks-0.1.
|
|
17
|
-
django_bulk_hooks-0.1.
|
|
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,,
|
|
File without changes
|
|
File without changes
|