django-bulk-hooks 0.1.259__py3-none-any.whl → 0.1.261__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 +196 -112
- {django_bulk_hooks-0.1.259.dist-info → django_bulk_hooks-0.1.261.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.259.dist-info → django_bulk_hooks-0.1.261.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.259.dist-info → django_bulk_hooks-0.1.261.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.259.dist-info → django_bulk_hooks-0.1.261.dist-info}/WHEEL +0 -0
django_bulk_hooks/queryset.py
CHANGED
|
@@ -89,7 +89,7 @@ class HookQuerySetMixin:
|
|
|
89
89
|
try:
|
|
90
90
|
from django.db.models import Subquery
|
|
91
91
|
|
|
92
|
-
logger.debug(
|
|
92
|
+
logger.debug("Successfully imported Subquery from django.db.models")
|
|
93
93
|
except ImportError as e:
|
|
94
94
|
logger.error(f"Failed to import Subquery: {e}")
|
|
95
95
|
raise
|
|
@@ -367,7 +367,7 @@ class HookQuerySetMixin:
|
|
|
367
367
|
|
|
368
368
|
if has_nested_subquery:
|
|
369
369
|
logger.debug(
|
|
370
|
-
|
|
370
|
+
"Expression contains Subquery, ensuring proper output_field"
|
|
371
371
|
)
|
|
372
372
|
# Try to resolve the expression to ensure it's properly formatted
|
|
373
373
|
try:
|
|
@@ -531,6 +531,10 @@ class HookQuerySetMixin:
|
|
|
531
531
|
# Check which records already exist in the database based on unique fields
|
|
532
532
|
existing_records = []
|
|
533
533
|
new_records = []
|
|
534
|
+
|
|
535
|
+
# Store the records for AFTER hooks to avoid duplicate queries
|
|
536
|
+
ctx.upsert_existing_records = existing_records
|
|
537
|
+
ctx.upsert_new_records = new_records
|
|
534
538
|
|
|
535
539
|
# Build a filter to check which records already exist
|
|
536
540
|
unique_values = []
|
|
@@ -541,9 +545,9 @@ class HookQuerySetMixin:
|
|
|
541
545
|
unique_value[field_name] = getattr(obj, field_name)
|
|
542
546
|
if unique_value:
|
|
543
547
|
unique_values.append(unique_value)
|
|
544
|
-
|
|
548
|
+
|
|
545
549
|
if unique_values:
|
|
546
|
-
# Query the database to see which records already exist
|
|
550
|
+
# Query the database to see which records already exist - SINGLE BULK QUERY
|
|
547
551
|
from django.db.models import Q
|
|
548
552
|
existing_filters = Q()
|
|
549
553
|
for unique_value in unique_values:
|
|
@@ -551,25 +555,25 @@ class HookQuerySetMixin:
|
|
|
551
555
|
for field_name, value in unique_value.items():
|
|
552
556
|
filter_kwargs[field_name] = value
|
|
553
557
|
existing_filters |= Q(**filter_kwargs)
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
)
|
|
558
|
-
|
|
558
|
+
|
|
559
|
+
# Get all existing records in one query and create a lookup set
|
|
560
|
+
existing_records_lookup = set()
|
|
561
|
+
for existing_record in model_cls.objects.filter(existing_filters).values_list(*unique_fields):
|
|
562
|
+
# Convert tuple to a hashable key for lookup
|
|
563
|
+
existing_records_lookup.add(existing_record)
|
|
564
|
+
|
|
559
565
|
# Separate records based on whether they already exist
|
|
560
566
|
for obj in objs:
|
|
561
567
|
obj_unique_value = {}
|
|
562
568
|
for field_name in unique_fields:
|
|
563
569
|
if hasattr(obj, field_name):
|
|
564
570
|
obj_unique_value[field_name] = getattr(obj, field_name)
|
|
565
|
-
|
|
566
|
-
# Check if this record already exists
|
|
571
|
+
|
|
572
|
+
# Check if this record already exists using our bulk lookup
|
|
567
573
|
if obj_unique_value:
|
|
568
|
-
|
|
569
|
-
for field_name
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
if model_cls.objects.filter(existing_q).exists():
|
|
574
|
+
# Convert object values to tuple for comparison with existing records
|
|
575
|
+
obj_unique_tuple = tuple(obj_unique_value[field_name] for field_name in unique_fields)
|
|
576
|
+
if obj_unique_tuple in existing_records_lookup:
|
|
573
577
|
existing_records.append(obj)
|
|
574
578
|
else:
|
|
575
579
|
new_records.append(obj)
|
|
@@ -579,7 +583,7 @@ class HookQuerySetMixin:
|
|
|
579
583
|
else:
|
|
580
584
|
# If no unique fields, treat all as new
|
|
581
585
|
new_records = objs
|
|
582
|
-
|
|
586
|
+
|
|
583
587
|
# Handle auto_now fields intelligently for upsert operations
|
|
584
588
|
# Only set auto_now fields on records that will actually be created
|
|
585
589
|
for obj in new_records:
|
|
@@ -619,11 +623,23 @@ class HookQuerySetMixin:
|
|
|
619
623
|
key = tuple(getattr(db_record, field) for field in unique_fields)
|
|
620
624
|
existing_db_map[key] = db_record
|
|
621
625
|
|
|
622
|
-
# For existing records,
|
|
626
|
+
# For existing records, populate all fields from database and set auto_now fields
|
|
623
627
|
for obj in existing_records:
|
|
624
628
|
key = tuple(getattr(obj, field) for field in unique_fields)
|
|
625
629
|
if key in existing_db_map:
|
|
626
|
-
|
|
630
|
+
db_record = existing_db_map[key]
|
|
631
|
+
# Copy all fields from the database record to ensure completeness
|
|
632
|
+
populated_fields = []
|
|
633
|
+
for field in model_cls._meta.local_fields:
|
|
634
|
+
if field.name != 'id': # Don't overwrite the ID
|
|
635
|
+
db_value = getattr(db_record, field.name)
|
|
636
|
+
if db_value is not None: # Only set non-None values
|
|
637
|
+
setattr(obj, field.name, db_value)
|
|
638
|
+
populated_fields.append(field.name)
|
|
639
|
+
print(f"DEBUG: Populated {len(populated_fields)} fields for existing record: {populated_fields}")
|
|
640
|
+
logger.debug(f"Populated {len(populated_fields)} fields for existing record: {populated_fields}")
|
|
641
|
+
|
|
642
|
+
# Now set auto_now fields using Django's pre_save method
|
|
627
643
|
for field in model_cls._meta.local_fields:
|
|
628
644
|
if hasattr(field, "auto_now") and field.auto_now:
|
|
629
645
|
field.pre_save(obj, add=False) # add=False for updates
|
|
@@ -632,38 +648,38 @@ class HookQuerySetMixin:
|
|
|
632
648
|
|
|
633
649
|
# Remove duplicate code since we're now handling this above
|
|
634
650
|
|
|
635
|
-
# CRITICAL:
|
|
636
|
-
#
|
|
651
|
+
# CRITICAL: Handle auto_now fields intelligently for existing records
|
|
652
|
+
# We need to exclude them from Django's ON CONFLICT DO UPDATE clause to prevent
|
|
653
|
+
# Django's default behavior, but still ensure they get updated via pre_save
|
|
637
654
|
if existing_records and update_fields:
|
|
638
655
|
logger.debug(f"Processing {len(existing_records)} existing records with update_fields: {update_fields}")
|
|
639
|
-
|
|
640
|
-
# Identify auto_now fields
|
|
641
|
-
|
|
656
|
+
|
|
657
|
+
# Identify auto_now fields
|
|
658
|
+
auto_now_fields = set()
|
|
642
659
|
for field in model_cls._meta.local_fields:
|
|
643
660
|
if hasattr(field, "auto_now") and field.auto_now:
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
logger.debug(f"Found auto_now fields: {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
# Create a filtered version of update_fields that excludes auto_now fields
|
|
651
|
-
filtered_update_fields = [f for f in update_fields if f not in auto_now_fields_to_exclude]
|
|
652
|
-
|
|
653
|
-
logger.debug(f"Filtered update_fields: {filtered_update_fields}")
|
|
654
|
-
logger.debug(f"Excluded auto_now fields: {auto_now_fields_to_exclude}")
|
|
655
|
-
|
|
656
|
-
# Store the original update_fields to restore later
|
|
661
|
+
auto_now_fields.add(field.name)
|
|
662
|
+
|
|
663
|
+
logger.debug(f"Found auto_now fields: {auto_now_fields}")
|
|
664
|
+
|
|
665
|
+
if auto_now_fields:
|
|
666
|
+
# Store original update_fields and auto_now fields for later restoration
|
|
657
667
|
ctx.original_update_fields = update_fields
|
|
658
|
-
ctx.
|
|
659
|
-
|
|
660
|
-
#
|
|
661
|
-
# This prevents Django from
|
|
668
|
+
ctx.auto_now_fields = auto_now_fields
|
|
669
|
+
|
|
670
|
+
# Filter out auto_now fields from update_fields for the database operation
|
|
671
|
+
# This prevents Django from including them in ON CONFLICT DO UPDATE
|
|
672
|
+
filtered_update_fields = [f for f in update_fields if f not in auto_now_fields]
|
|
673
|
+
|
|
674
|
+
logger.debug(f"Filtered update_fields: {filtered_update_fields}")
|
|
675
|
+
logger.debug(f"Excluded auto_now fields: {auto_now_fields}")
|
|
676
|
+
|
|
677
|
+
# Use filtered update_fields for Django's bulk_create operation
|
|
662
678
|
update_fields = filtered_update_fields
|
|
663
|
-
|
|
679
|
+
|
|
664
680
|
logger.debug(f"Final update_fields for DB operation: {update_fields}")
|
|
665
681
|
else:
|
|
666
|
-
logger.debug("No auto_now fields found to
|
|
682
|
+
logger.debug("No auto_now fields found to handle")
|
|
667
683
|
else:
|
|
668
684
|
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}")
|
|
669
685
|
|
|
@@ -733,67 +749,92 @@ class HookQuerySetMixin:
|
|
|
733
749
|
# Fire AFTER hooks
|
|
734
750
|
if not bypass_hooks:
|
|
735
751
|
if update_conflicts and unique_fields:
|
|
752
|
+
# Handle auto_now fields that were excluded from the main update
|
|
753
|
+
if hasattr(ctx, 'auto_now_fields') and existing_records:
|
|
754
|
+
logger.debug(f"Performing separate update for auto_now fields: {ctx.auto_now_fields}")
|
|
755
|
+
|
|
756
|
+
# Perform a separate bulk_update for the auto_now fields that were set via pre_save
|
|
757
|
+
# This ensures they get saved to the database even though they were excluded from the main upsert
|
|
758
|
+
try:
|
|
759
|
+
# Use Django's base manager to bypass hooks and ensure the update happens
|
|
760
|
+
base_manager = model_cls._base_manager
|
|
761
|
+
auto_now_update_result = base_manager.bulk_update(
|
|
762
|
+
existing_records, list(ctx.auto_now_fields)
|
|
763
|
+
)
|
|
764
|
+
logger.debug(f"Auto_now fields update completed with result: {auto_now_update_result}")
|
|
765
|
+
except Exception as e:
|
|
766
|
+
logger.error(f"Failed to update auto_now fields: {e}")
|
|
767
|
+
# Don't raise the exception - the main operation succeeded
|
|
768
|
+
|
|
736
769
|
# Restore original update_fields if we modified them
|
|
737
770
|
if hasattr(ctx, 'original_update_fields'):
|
|
738
771
|
logger.debug(f"Restoring original update_fields: {ctx.original_update_fields}")
|
|
739
772
|
update_fields = ctx.original_update_fields
|
|
740
773
|
delattr(ctx, 'original_update_fields')
|
|
741
|
-
|
|
774
|
+
if hasattr(ctx, 'auto_now_fields'):
|
|
775
|
+
delattr(ctx, 'auto_now_fields')
|
|
742
776
|
logger.debug(f"Restored update_fields: {update_fields}")
|
|
743
|
-
|
|
744
|
-
# For upsert operations,
|
|
745
|
-
#
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
if unique_values:
|
|
760
|
-
# Query the database to see which records already exist
|
|
761
|
-
from django.db.models import Q
|
|
762
|
-
existing_filters = Q()
|
|
763
|
-
for unique_value in unique_values:
|
|
764
|
-
filter_kwargs = {}
|
|
765
|
-
for field_name, value in unique_value.items():
|
|
766
|
-
filter_kwargs[field_name] = value
|
|
767
|
-
existing_filters |= Q(**filter_kwargs)
|
|
768
|
-
|
|
769
|
-
existing_pks = set(
|
|
770
|
-
model_cls.objects.filter(existing_filters).values_list('pk', flat=True)
|
|
771
|
-
)
|
|
772
|
-
|
|
773
|
-
# Separate records based on whether they already exist
|
|
777
|
+
|
|
778
|
+
# For upsert operations, reuse the existing/new records determination from BEFORE hooks
|
|
779
|
+
# This avoids duplicate queries and improves performance
|
|
780
|
+
if hasattr(ctx, 'upsert_existing_records') and hasattr(ctx, 'upsert_new_records'):
|
|
781
|
+
existing_records = ctx.upsert_existing_records
|
|
782
|
+
new_records = ctx.upsert_new_records
|
|
783
|
+
logger.debug(f"Reusing upsert record classification from BEFORE hooks: {len(existing_records)} existing, {len(new_records)} new")
|
|
784
|
+
else:
|
|
785
|
+
# Fallback: determine records that actually exist after bulk operation
|
|
786
|
+
logger.warning("Upsert record classification not found in context, performing fallback query")
|
|
787
|
+
existing_records = []
|
|
788
|
+
new_records = []
|
|
789
|
+
|
|
790
|
+
# Build a filter to check which records now exist
|
|
791
|
+
unique_values = []
|
|
774
792
|
for obj in objs:
|
|
775
|
-
|
|
793
|
+
unique_value = {}
|
|
776
794
|
for field_name in unique_fields:
|
|
777
795
|
if hasattr(obj, field_name):
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
796
|
+
unique_value[field_name] = getattr(obj, field_name)
|
|
797
|
+
if unique_value:
|
|
798
|
+
unique_values.append(unique_value)
|
|
799
|
+
|
|
800
|
+
if unique_values:
|
|
801
|
+
# Query the database to see which records exist after bulk operation
|
|
802
|
+
from django.db.models import Q
|
|
803
|
+
existing_filters = Q()
|
|
804
|
+
for unique_value in unique_values:
|
|
805
|
+
filter_kwargs = {}
|
|
806
|
+
for field_name, value in unique_value.items():
|
|
807
|
+
filter_kwargs[field_name] = value
|
|
808
|
+
existing_filters |= Q(**filter_kwargs)
|
|
809
|
+
|
|
810
|
+
# Get all existing records in one query and create a lookup set
|
|
811
|
+
existing_records_lookup = set()
|
|
812
|
+
for existing_record in model_cls.objects.filter(existing_filters).values_list(*unique_fields):
|
|
813
|
+
# Convert tuple to a hashable key for lookup
|
|
814
|
+
existing_records_lookup.add(existing_record)
|
|
815
|
+
|
|
816
|
+
# Separate records based on whether they now exist
|
|
817
|
+
for obj in objs:
|
|
818
|
+
obj_unique_value = {}
|
|
819
|
+
for field_name in unique_fields:
|
|
820
|
+
if hasattr(obj, field_name):
|
|
821
|
+
obj_unique_value[field_name] = getattr(obj, field_name)
|
|
822
|
+
|
|
823
|
+
# Check if this record exists using our bulk lookup
|
|
824
|
+
if obj_unique_value:
|
|
825
|
+
# Convert object values to tuple for comparison with existing records
|
|
826
|
+
obj_unique_tuple = tuple(obj_unique_value[field_name] for field_name in unique_fields)
|
|
827
|
+
if obj_unique_tuple in existing_records_lookup:
|
|
828
|
+
existing_records.append(obj)
|
|
829
|
+
else:
|
|
830
|
+
new_records.append(obj)
|
|
788
831
|
else:
|
|
832
|
+
# If we can't determine uniqueness, treat as new
|
|
789
833
|
new_records.append(obj)
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
# If no unique fields, treat all as new
|
|
795
|
-
new_records = objs
|
|
796
|
-
|
|
834
|
+
else:
|
|
835
|
+
# If no unique fields, treat all as new
|
|
836
|
+
new_records = objs
|
|
837
|
+
|
|
797
838
|
# Run appropriate AFTER hooks based on what actually happened
|
|
798
839
|
if new_records:
|
|
799
840
|
engine.run(model_cls, AFTER_CREATE, new_records, ctx=ctx)
|
|
@@ -849,7 +890,8 @@ class HookQuerySetMixin:
|
|
|
849
890
|
fields_set = set(fields)
|
|
850
891
|
pk_fields = model_cls._meta.pk_fields
|
|
851
892
|
auto_now_fields = []
|
|
852
|
-
|
|
893
|
+
custom_update_fields = [] # Fields that need pre_save() called on update
|
|
894
|
+
logger.debug(f"Checking for auto_now and custom update fields in {model_cls.__name__}")
|
|
853
895
|
for field in model_cls._meta.local_concrete_fields:
|
|
854
896
|
# Only add auto_now fields (like updated_at) that aren't already in the fields list
|
|
855
897
|
# Don't include auto_now_add fields (like created_at) as they should only be set on creation
|
|
@@ -868,6 +910,13 @@ class HookQuerySetMixin:
|
|
|
868
910
|
print(f"DEBUG: Auto_now field {field.name} already in fields list or is PK")
|
|
869
911
|
elif hasattr(field, "auto_now_add") and field.auto_now_add:
|
|
870
912
|
logger.debug(f"Found auto_now_add field: {field.name} (skipping)")
|
|
913
|
+
# Check for custom fields that might need pre_save() on update (like CurrentUserField)
|
|
914
|
+
elif hasattr(field, 'pre_save'):
|
|
915
|
+
# Only call pre_save on fields that aren't already being updated
|
|
916
|
+
if field.name not in fields_set and field.name not in pk_fields:
|
|
917
|
+
custom_update_fields.append(field)
|
|
918
|
+
logger.debug(f"Found custom field with pre_save: {field.name}")
|
|
919
|
+
print(f"DEBUG: Found custom field with pre_save: {field.name}")
|
|
871
920
|
|
|
872
921
|
logger.debug(f"Auto_now fields detected: {auto_now_fields}")
|
|
873
922
|
print(f"DEBUG: Auto_now fields detected: {auto_now_fields}")
|
|
@@ -884,6 +933,28 @@ class HookQuerySetMixin:
|
|
|
884
933
|
setattr(obj, field_name, current_time)
|
|
885
934
|
print(f"DEBUG: Set {field_name} to {current_time} for object {obj.pk}")
|
|
886
935
|
|
|
936
|
+
# Call pre_save() on custom fields that need update handling
|
|
937
|
+
if custom_update_fields:
|
|
938
|
+
logger.debug(f"Calling pre_save() on custom update fields: {[f.name for f in custom_update_fields]}")
|
|
939
|
+
print(f"DEBUG: Calling pre_save() on custom update fields: {[f.name for f in custom_update_fields]}")
|
|
940
|
+
for obj in objs:
|
|
941
|
+
for field in custom_update_fields:
|
|
942
|
+
try:
|
|
943
|
+
# Call pre_save with add=False to indicate this is an update
|
|
944
|
+
new_value = field.pre_save(obj, add=False)
|
|
945
|
+
# Only update the field if pre_save returned a new value
|
|
946
|
+
if new_value is not None:
|
|
947
|
+
setattr(obj, field.name, new_value)
|
|
948
|
+
# Add this field to the update fields if it's not already there
|
|
949
|
+
if field.name not in fields_set:
|
|
950
|
+
fields_set.add(field.name)
|
|
951
|
+
fields.append(field.name)
|
|
952
|
+
logger.debug(f"Custom field {field.name} updated via pre_save() for object {obj.pk}")
|
|
953
|
+
print(f"DEBUG: Custom field {field.name} updated via pre_save() for object {obj.pk}")
|
|
954
|
+
except Exception as e:
|
|
955
|
+
logger.warning(f"Failed to call pre_save() on custom field {field.name}: {e}")
|
|
956
|
+
print(f"DEBUG: Failed to call pre_save() on custom field {field.name}: {e}")
|
|
957
|
+
|
|
887
958
|
# Handle MTI models differently
|
|
888
959
|
if is_mti:
|
|
889
960
|
result = self._mti_bulk_update(objs, fields, **kwargs)
|
|
@@ -1238,13 +1309,14 @@ class HookQuerySetMixin:
|
|
|
1238
1309
|
|
|
1239
1310
|
return child_obj
|
|
1240
1311
|
|
|
1241
|
-
def _mti_bulk_update(self, objs, fields, **kwargs):
|
|
1312
|
+
def _mti_bulk_update(self, objs, fields, field_groups=None, inheritance_chain=None, **kwargs):
|
|
1242
1313
|
"""
|
|
1243
1314
|
Custom bulk update implementation for MTI models.
|
|
1244
1315
|
Updates each table in the inheritance chain efficiently using Django's batch_size.
|
|
1245
1316
|
"""
|
|
1246
1317
|
model_cls = self.model
|
|
1247
|
-
inheritance_chain
|
|
1318
|
+
if inheritance_chain is None:
|
|
1319
|
+
inheritance_chain = self._get_inheritance_chain()
|
|
1248
1320
|
|
|
1249
1321
|
# Remove custom hook kwargs before passing to Django internals
|
|
1250
1322
|
django_kwargs = {
|
|
@@ -1259,13 +1331,24 @@ class HookQuerySetMixin:
|
|
|
1259
1331
|
"Inheritance chain too deep - possible infinite recursion detected"
|
|
1260
1332
|
)
|
|
1261
1333
|
|
|
1262
|
-
# Handle auto_now fields by calling pre_save on objects
|
|
1263
|
-
# Check all models in the inheritance chain for auto_now fields
|
|
1334
|
+
# Handle auto_now fields and custom fields by calling pre_save on objects
|
|
1335
|
+
# Check all models in the inheritance chain for auto_now and custom fields
|
|
1336
|
+
custom_update_fields = []
|
|
1264
1337
|
for obj in objs:
|
|
1265
1338
|
for model in inheritance_chain:
|
|
1266
1339
|
for field in model._meta.local_fields:
|
|
1267
1340
|
if hasattr(field, "auto_now") and field.auto_now:
|
|
1268
1341
|
field.pre_save(obj, add=False)
|
|
1342
|
+
# Check for custom fields that might need pre_save() on update (like CurrentUserField)
|
|
1343
|
+
elif hasattr(field, 'pre_save') and field.name not in fields:
|
|
1344
|
+
try:
|
|
1345
|
+
new_value = field.pre_save(obj, add=False)
|
|
1346
|
+
if new_value is not None:
|
|
1347
|
+
setattr(obj, field.name, new_value)
|
|
1348
|
+
custom_update_fields.append(field.name)
|
|
1349
|
+
logger.debug(f"Custom field {field.name} updated via pre_save() for MTI object {obj.pk}")
|
|
1350
|
+
except Exception as e:
|
|
1351
|
+
logger.warning(f"Failed to call pre_save() on custom field {field.name} in MTI: {e}")
|
|
1269
1352
|
|
|
1270
1353
|
# Add auto_now fields to the fields list so they get updated in the database
|
|
1271
1354
|
auto_now_fields = set()
|
|
@@ -1274,20 +1357,21 @@ class HookQuerySetMixin:
|
|
|
1274
1357
|
if hasattr(field, "auto_now") and field.auto_now:
|
|
1275
1358
|
auto_now_fields.add(field.name)
|
|
1276
1359
|
|
|
1277
|
-
#
|
|
1278
|
-
all_fields = list(fields) + list(auto_now_fields)
|
|
1279
|
-
|
|
1280
|
-
# Group fields by model in the inheritance chain
|
|
1281
|
-
field_groups
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
if
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1360
|
+
# Add custom fields that were updated to the fields list
|
|
1361
|
+
all_fields = list(fields) + list(auto_now_fields) + custom_update_fields
|
|
1362
|
+
|
|
1363
|
+
# Group fields by model in the inheritance chain (if not provided)
|
|
1364
|
+
if field_groups is None:
|
|
1365
|
+
field_groups = {}
|
|
1366
|
+
for field_name in all_fields:
|
|
1367
|
+
field = model_cls._meta.get_field(field_name)
|
|
1368
|
+
# Find which model in the inheritance chain this field belongs to
|
|
1369
|
+
for model in inheritance_chain:
|
|
1370
|
+
if field in model._meta.local_fields:
|
|
1371
|
+
if model not in field_groups:
|
|
1372
|
+
field_groups[model] = []
|
|
1373
|
+
field_groups[model].append(field_name)
|
|
1374
|
+
break
|
|
1291
1375
|
|
|
1292
1376
|
# Process in batches
|
|
1293
1377
|
batch_size = django_kwargs.get("batch_size") or len(objs)
|
|
@@ -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=5ynRNPmwfl5piL94DevFXc_N33a2_GSzgmPjINTW9ec,76997
|
|
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.261.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
+
django_bulk_hooks-0.1.261.dist-info/METADATA,sha256=hqXelN--kvvcqf0PBJKTdJOxt0iPUsX6hmIldNezDj8,9061
|
|
16
|
+
django_bulk_hooks-0.1.261.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
17
|
+
django_bulk_hooks-0.1.261.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|