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.

@@ -648,38 +648,38 @@ class HookQuerySetMixin:
648
648
 
649
649
  # Remove duplicate code since we're now handling this above
650
650
 
651
- # CRITICAL: Exclude auto_now fields from update_fields for existing records
652
- # This prevents Django from including them in the ON CONFLICT DO UPDATE clause
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 that should be excluded from updates
657
- auto_now_fields_to_exclude = set()
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
- auto_now_fields_to_exclude.add(field.name)
661
-
662
- logger.debug(f"Found auto_now fields: {auto_now_fields_to_exclude}")
663
-
664
- # Filter out auto_now fields from update_fields for existing records
665
- if auto_now_fields_to_exclude:
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.auto_now_fields_excluded = auto_now_fields_to_exclude
675
-
676
- # Use the filtered update_fields for the database operation
677
- # This prevents Django from overwriting the timestamps during upsert
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 exclude")
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
- delattr(ctx, 'auto_now_fields_excluded')
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 = self._get_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
- for field_name in all_fields:
1347
- field = model_cls._meta.get_field(field_name)
1348
- # Find which model in the inheritance chain this field belongs to
1349
- for model in inheritance_chain:
1350
- if field in model._meta.local_fields:
1351
- if model not in field_groups:
1352
- field_groups[model] = []
1353
- field_groups[model].append(field_name)
1354
- break
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.260
3
+ Version: 0.1.261
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=LZv6iS7hKb-gSmcBt0EDwu8zfGytZfnthNmji6I1QbU,75863
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.260.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.260.dist-info/METADATA,sha256=bUETT2dh4Zg_1wiLGwl2TLySV2Hq4ubvEej7Xxk0DjI,9061
16
- django_bulk_hooks-0.1.260.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.260.dist-info/RECORD,,
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,,