django-bulk-hooks 0.1.260__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 +59 -39
- {django_bulk_hooks-0.1.260.dist-info → django_bulk_hooks-0.1.261.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.260.dist-info → django_bulk_hooks-0.1.261.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.260.dist-info → django_bulk_hooks-0.1.261.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.260.dist-info → django_bulk_hooks-0.1.261.dist-info}/WHEEL +0 -0
django_bulk_hooks/queryset.py
CHANGED
|
@@ -648,38 +648,38 @@ class HookQuerySetMixin:
|
|
|
648
648
|
|
|
649
649
|
# Remove duplicate code since we're now handling this above
|
|
650
650
|
|
|
651
|
-
# CRITICAL:
|
|
652
|
-
#
|
|
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
|
|
653
654
|
if existing_records and update_fields:
|
|
654
655
|
logger.debug(f"Processing {len(existing_records)} existing records with update_fields: {update_fields}")
|
|
655
|
-
|
|
656
|
-
# Identify auto_now fields
|
|
657
|
-
|
|
656
|
+
|
|
657
|
+
# Identify auto_now fields
|
|
658
|
+
auto_now_fields = set()
|
|
658
659
|
for field in model_cls._meta.local_fields:
|
|
659
660
|
if hasattr(field, "auto_now") and field.auto_now:
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
logger.debug(f"Found auto_now fields: {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
# Create a filtered version of update_fields that excludes auto_now fields
|
|
667
|
-
filtered_update_fields = [f for f in update_fields if f not in auto_now_fields_to_exclude]
|
|
668
|
-
|
|
669
|
-
logger.debug(f"Filtered update_fields: {filtered_update_fields}")
|
|
670
|
-
logger.debug(f"Excluded auto_now fields: {auto_now_fields_to_exclude}")
|
|
671
|
-
|
|
672
|
-
# 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
|
|
673
667
|
ctx.original_update_fields = update_fields
|
|
674
|
-
ctx.
|
|
675
|
-
|
|
676
|
-
#
|
|
677
|
-
# 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
|
|
678
678
|
update_fields = filtered_update_fields
|
|
679
|
-
|
|
679
|
+
|
|
680
680
|
logger.debug(f"Final update_fields for DB operation: {update_fields}")
|
|
681
681
|
else:
|
|
682
|
-
logger.debug("No auto_now fields found to
|
|
682
|
+
logger.debug("No auto_now fields found to handle")
|
|
683
683
|
else:
|
|
684
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}")
|
|
685
685
|
|
|
@@ -749,14 +749,32 @@ class HookQuerySetMixin:
|
|
|
749
749
|
# Fire AFTER hooks
|
|
750
750
|
if not bypass_hooks:
|
|
751
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
|
+
|
|
752
769
|
# Restore original update_fields if we modified them
|
|
753
770
|
if hasattr(ctx, 'original_update_fields'):
|
|
754
771
|
logger.debug(f"Restoring original update_fields: {ctx.original_update_fields}")
|
|
755
772
|
update_fields = ctx.original_update_fields
|
|
756
773
|
delattr(ctx, 'original_update_fields')
|
|
757
|
-
|
|
774
|
+
if hasattr(ctx, 'auto_now_fields'):
|
|
775
|
+
delattr(ctx, 'auto_now_fields')
|
|
758
776
|
logger.debug(f"Restored update_fields: {update_fields}")
|
|
759
|
-
|
|
777
|
+
|
|
760
778
|
# For upsert operations, reuse the existing/new records determination from BEFORE hooks
|
|
761
779
|
# This avoids duplicate queries and improves performance
|
|
762
780
|
if hasattr(ctx, 'upsert_existing_records') and hasattr(ctx, 'upsert_new_records'):
|
|
@@ -1291,13 +1309,14 @@ class HookQuerySetMixin:
|
|
|
1291
1309
|
|
|
1292
1310
|
return child_obj
|
|
1293
1311
|
|
|
1294
|
-
def _mti_bulk_update(self, objs, fields, **kwargs):
|
|
1312
|
+
def _mti_bulk_update(self, objs, fields, field_groups=None, inheritance_chain=None, **kwargs):
|
|
1295
1313
|
"""
|
|
1296
1314
|
Custom bulk update implementation for MTI models.
|
|
1297
1315
|
Updates each table in the inheritance chain efficiently using Django's batch_size.
|
|
1298
1316
|
"""
|
|
1299
1317
|
model_cls = self.model
|
|
1300
|
-
inheritance_chain
|
|
1318
|
+
if inheritance_chain is None:
|
|
1319
|
+
inheritance_chain = self._get_inheritance_chain()
|
|
1301
1320
|
|
|
1302
1321
|
# Remove custom hook kwargs before passing to Django internals
|
|
1303
1322
|
django_kwargs = {
|
|
@@ -1341,17 +1360,18 @@ class HookQuerySetMixin:
|
|
|
1341
1360
|
# Add custom fields that were updated to the fields list
|
|
1342
1361
|
all_fields = list(fields) + list(auto_now_fields) + custom_update_fields
|
|
1343
1362
|
|
|
1344
|
-
# Group fields by model in the inheritance chain
|
|
1345
|
-
field_groups
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
if
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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
|
|
1355
1375
|
|
|
1356
1376
|
# Process in batches
|
|
1357
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
|