django-bulk-hooks 0.2.44__tar.gz → 0.2.45__tar.gz
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-0.2.44 → django_bulk_hooks-0.2.45}/PKG-INFO +1 -1
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/bulk_executor.py +13 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/coordinator.py +84 -63
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/pyproject.toml +1 -1
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/LICENSE +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/README.md +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/changeset.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/conditions.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/dispatcher.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/factory.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/helpers.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/manager.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/__init__.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/analyzer.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/mti_handler.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/mti_plans.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/record_classifier.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/queryset.py +0 -0
- {django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/registry.py +0 -0
{django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/bulk_executor.py
RENAMED
|
@@ -540,8 +540,21 @@ class BulkExecutor:
|
|
|
540
540
|
result_objects: List of objects returned from bulk operation
|
|
541
541
|
existing_record_ids: Set of id() for objects that existed before the operation
|
|
542
542
|
"""
|
|
543
|
+
created_count = 0
|
|
544
|
+
updated_count = 0
|
|
545
|
+
|
|
543
546
|
for obj in result_objects:
|
|
544
547
|
# Tag with metadata for hook dispatching
|
|
545
548
|
was_created = id(obj) not in existing_record_ids
|
|
546
549
|
obj._bulk_hooks_was_created = was_created
|
|
547
550
|
obj._bulk_hooks_upsert_metadata = True
|
|
551
|
+
|
|
552
|
+
if was_created:
|
|
553
|
+
created_count += 1
|
|
554
|
+
else:
|
|
555
|
+
updated_count += 1
|
|
556
|
+
|
|
557
|
+
logger.info(
|
|
558
|
+
f"Tagged upsert metadata: {created_count} created, {updated_count} updated "
|
|
559
|
+
f"(total={len(result_objects)}, existing_ids={len(existing_record_ids)})"
|
|
560
|
+
)
|
{django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/coordinator.py
RENAMED
|
@@ -29,7 +29,6 @@ class BulkOperationCoordinator:
|
|
|
29
29
|
Services are created lazily and cached.
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
def __init__(self, queryset):
|
|
34
33
|
"""
|
|
35
34
|
Initialize coordinator for a queryset.
|
|
@@ -137,13 +136,10 @@ class BulkOperationCoordinator:
|
|
|
137
136
|
existing_record_ids = set()
|
|
138
137
|
existing_pks_map = {}
|
|
139
138
|
if update_conflicts and unique_fields:
|
|
140
|
-
existing_record_ids, existing_pks_map = self.record_classifier.classify_for_upsert(
|
|
141
|
-
|
|
142
|
-
)
|
|
143
|
-
logger.
|
|
144
|
-
f"Upsert operation: {len(existing_record_ids)} existing, "
|
|
145
|
-
f"{len(objs) - len(existing_record_ids)} new records"
|
|
146
|
-
)
|
|
139
|
+
existing_record_ids, existing_pks_map = self.record_classifier.classify_for_upsert(objs, unique_fields)
|
|
140
|
+
logger.info(f"Upsert operation: {len(existing_record_ids)} existing, {len(objs) - len(existing_record_ids)} new records")
|
|
141
|
+
logger.debug(f"Existing record IDs: {existing_record_ids}")
|
|
142
|
+
logger.debug(f"Existing PKs map: {existing_pks_map}")
|
|
147
143
|
|
|
148
144
|
# Build initial changeset
|
|
149
145
|
changeset = build_changeset_for_create(
|
|
@@ -236,14 +232,17 @@ class BulkOperationCoordinator:
|
|
|
236
232
|
|
|
237
233
|
@transaction.atomic
|
|
238
234
|
def update_queryset(
|
|
239
|
-
self,
|
|
235
|
+
self,
|
|
236
|
+
update_kwargs,
|
|
237
|
+
bypass_hooks=False,
|
|
238
|
+
bypass_validation=False,
|
|
240
239
|
):
|
|
241
240
|
"""
|
|
242
241
|
Execute queryset.update() with full hook support.
|
|
243
|
-
|
|
242
|
+
|
|
244
243
|
ARCHITECTURE & PERFORMANCE TRADE-OFFS
|
|
245
244
|
======================================
|
|
246
|
-
|
|
245
|
+
|
|
247
246
|
To support hooks with queryset.update(), we must:
|
|
248
247
|
1. Fetch old state (SELECT all matching rows)
|
|
249
248
|
2. Execute database update (UPDATE in SQL)
|
|
@@ -252,29 +251,29 @@ class BulkOperationCoordinator:
|
|
|
252
251
|
5. Run BEFORE_UPDATE hooks (CAN modify instances)
|
|
253
252
|
6. Persist BEFORE_UPDATE modifications (bulk_update)
|
|
254
253
|
7. Run AFTER_UPDATE hooks (read-only side effects)
|
|
255
|
-
|
|
254
|
+
|
|
256
255
|
Performance Cost:
|
|
257
256
|
- 2 SELECT queries (before/after)
|
|
258
257
|
- 1 UPDATE query (actual update)
|
|
259
258
|
- 1 bulk_update (if hooks modify data)
|
|
260
|
-
|
|
259
|
+
|
|
261
260
|
Trade-off: Hooks require loading data into Python. If you need
|
|
262
261
|
maximum performance and don't need hooks, use bypass_hooks=True.
|
|
263
|
-
|
|
262
|
+
|
|
264
263
|
Hook Semantics:
|
|
265
264
|
- BEFORE_UPDATE hooks run after the DB update and CAN modify instances
|
|
266
265
|
- Modifications are auto-persisted (framework handles complexity)
|
|
267
266
|
- AFTER_UPDATE hooks run after BEFORE_UPDATE and are read-only
|
|
268
267
|
- This enables cascade logic and computed fields based on DB values
|
|
269
268
|
- User expectation: BEFORE_UPDATE hooks can modify data
|
|
270
|
-
|
|
269
|
+
|
|
271
270
|
Why this approach works well:
|
|
272
271
|
- Allows hooks to see Subquery/F() computed values
|
|
273
272
|
- Enables HasChanged conditions on complex expressions
|
|
274
273
|
- Maintains SQL performance (Subquery stays in database)
|
|
275
274
|
- Meets user expectations: BEFORE_UPDATE can modify instances
|
|
276
275
|
- Clean separation: BEFORE for modifications, AFTER for side effects
|
|
277
|
-
|
|
276
|
+
|
|
278
277
|
For true "prevent write" semantics, intercept at a higher level
|
|
279
278
|
or use bulk_update() directly (which has true before semantics).
|
|
280
279
|
"""
|
|
@@ -291,19 +290,21 @@ class BulkOperationCoordinator:
|
|
|
291
290
|
)
|
|
292
291
|
|
|
293
292
|
def _execute_queryset_update_with_hooks(
|
|
294
|
-
self,
|
|
293
|
+
self,
|
|
294
|
+
update_kwargs,
|
|
295
|
+
bypass_validation=False,
|
|
295
296
|
):
|
|
296
297
|
"""
|
|
297
298
|
Execute queryset update with full hook lifecycle support.
|
|
298
|
-
|
|
299
|
+
|
|
299
300
|
This method implements the fetch-update-fetch pattern required
|
|
300
301
|
to support hooks with queryset.update(). BEFORE_UPDATE hooks can
|
|
301
302
|
modify instances and modifications are auto-persisted.
|
|
302
|
-
|
|
303
|
+
|
|
303
304
|
Args:
|
|
304
305
|
update_kwargs: Dict of fields to update
|
|
305
306
|
bypass_validation: Skip validation hooks if True
|
|
306
|
-
|
|
307
|
+
|
|
307
308
|
Returns:
|
|
308
309
|
Number of rows updated
|
|
309
310
|
"""
|
|
@@ -387,11 +388,11 @@ class BulkOperationCoordinator:
|
|
|
387
388
|
def _run_before_update_hooks_with_tracking(self, instances, models_in_chain, changeset):
|
|
388
389
|
"""
|
|
389
390
|
Run BEFORE_UPDATE hooks and detect modifications.
|
|
390
|
-
|
|
391
|
+
|
|
391
392
|
This is what users expect - BEFORE_UPDATE hooks can modify instances
|
|
392
393
|
and those modifications will be automatically persisted. The framework
|
|
393
394
|
handles the complexity internally.
|
|
394
|
-
|
|
395
|
+
|
|
395
396
|
Returns:
|
|
396
397
|
Set of field names that were modified by hooks
|
|
397
398
|
"""
|
|
@@ -413,10 +414,10 @@ class BulkOperationCoordinator:
|
|
|
413
414
|
def _snapshot_instance_state(self, instances):
|
|
414
415
|
"""
|
|
415
416
|
Create a snapshot of current instance field values.
|
|
416
|
-
|
|
417
|
+
|
|
417
418
|
Args:
|
|
418
419
|
instances: List of model instances
|
|
419
|
-
|
|
420
|
+
|
|
420
421
|
Returns:
|
|
421
422
|
Dict mapping pk -> {field_name: value}
|
|
422
423
|
"""
|
|
@@ -446,11 +447,11 @@ class BulkOperationCoordinator:
|
|
|
446
447
|
def _detect_modifications(self, instances, pre_hook_state):
|
|
447
448
|
"""
|
|
448
449
|
Detect which fields were modified by comparing to snapshot.
|
|
449
|
-
|
|
450
|
+
|
|
450
451
|
Args:
|
|
451
452
|
instances: List of model instances
|
|
452
453
|
pre_hook_state: Previous state snapshot from _snapshot_instance_state
|
|
453
|
-
|
|
454
|
+
|
|
454
455
|
Returns:
|
|
455
456
|
Set of field names that were modified
|
|
456
457
|
"""
|
|
@@ -477,16 +478,15 @@ class BulkOperationCoordinator:
|
|
|
477
478
|
def _persist_hook_modifications(self, instances, modified_fields):
|
|
478
479
|
"""
|
|
479
480
|
Persist modifications made by hooks using bulk_update.
|
|
480
|
-
|
|
481
|
+
|
|
481
482
|
This creates a "cascade" effect similar to Salesforce workflows.
|
|
482
|
-
|
|
483
|
+
|
|
483
484
|
Args:
|
|
484
485
|
instances: List of modified instances
|
|
485
486
|
modified_fields: Set of field names that were modified
|
|
486
487
|
"""
|
|
487
488
|
logger.info(
|
|
488
|
-
f"Hooks modified {len(modified_fields)} field(s): "
|
|
489
|
-
f"{', '.join(sorted(modified_fields))}",
|
|
489
|
+
f"Hooks modified {len(modified_fields)} field(s): {', '.join(sorted(modified_fields))}",
|
|
490
490
|
)
|
|
491
491
|
logger.info("Auto-persisting modifications via bulk_update")
|
|
492
492
|
|
|
@@ -569,14 +569,14 @@ class BulkOperationCoordinator:
|
|
|
569
569
|
def _build_changeset_for_model(self, original_changeset, target_model_cls):
|
|
570
570
|
"""
|
|
571
571
|
Build a changeset for a specific model in the MTI inheritance chain.
|
|
572
|
-
|
|
572
|
+
|
|
573
573
|
This allows parent model hooks to receive the same instances but with
|
|
574
574
|
the correct model_cls for hook registration matching.
|
|
575
|
-
|
|
575
|
+
|
|
576
576
|
Args:
|
|
577
577
|
original_changeset: The original changeset (for child model)
|
|
578
578
|
target_model_cls: The model class to build changeset for (parent model)
|
|
579
|
-
|
|
579
|
+
|
|
580
580
|
Returns:
|
|
581
581
|
ChangeSet for the target model
|
|
582
582
|
"""
|
|
@@ -600,18 +600,18 @@ class BulkOperationCoordinator:
|
|
|
600
600
|
):
|
|
601
601
|
"""
|
|
602
602
|
Execute operation with hooks for entire MTI inheritance chain.
|
|
603
|
-
|
|
603
|
+
|
|
604
604
|
This method dispatches hooks for both child and parent models when
|
|
605
605
|
dealing with MTI models, ensuring parent model hooks fire when
|
|
606
606
|
child instances are created/updated/deleted.
|
|
607
|
-
|
|
607
|
+
|
|
608
608
|
Args:
|
|
609
609
|
changeset: ChangeSet for the child model
|
|
610
610
|
operation: Callable that performs the actual DB operation
|
|
611
611
|
event_prefix: 'create', 'update', or 'delete'
|
|
612
612
|
bypass_hooks: Skip all hooks if True
|
|
613
613
|
bypass_validation: Skip validation hooks if True
|
|
614
|
-
|
|
614
|
+
|
|
615
615
|
Returns:
|
|
616
616
|
Result of operation
|
|
617
617
|
"""
|
|
@@ -649,8 +649,9 @@ class BulkOperationCoordinator:
|
|
|
649
649
|
else:
|
|
650
650
|
# Normal create operation
|
|
651
651
|
from django_bulk_hooks.helpers import build_changeset_for_create
|
|
652
|
+
|
|
652
653
|
changeset = build_changeset_for_create(changeset.model_cls, result)
|
|
653
|
-
|
|
654
|
+
|
|
654
655
|
for model_cls in models_in_chain:
|
|
655
656
|
model_changeset = self._build_changeset_for_model(changeset, model_cls)
|
|
656
657
|
self.dispatcher.dispatch(model_changeset, f"after_{event_prefix}", bypass_hooks=False)
|
|
@@ -680,11 +681,13 @@ class BulkOperationCoordinator:
|
|
|
680
681
|
for field_name in update_kwargs.keys():
|
|
681
682
|
try:
|
|
682
683
|
field = self.model_cls._meta.get_field(field_name)
|
|
683
|
-
if (
|
|
684
|
-
|
|
685
|
-
not field.
|
|
686
|
-
|
|
687
|
-
field
|
|
684
|
+
if (
|
|
685
|
+
field.is_relation
|
|
686
|
+
and not field.many_to_many
|
|
687
|
+
and not field.one_to_many
|
|
688
|
+
and hasattr(field, "attname")
|
|
689
|
+
and field.attname == field_name
|
|
690
|
+
):
|
|
688
691
|
# This is a FK field being updated by its attname (e.g., business_id)
|
|
689
692
|
# Add the relationship name (e.g., 'business') to skip list
|
|
690
693
|
fk_relationships.add(field.name)
|
|
@@ -697,27 +700,27 @@ class BulkOperationCoordinator:
|
|
|
697
700
|
def _is_upsert_operation(self, result_objects):
|
|
698
701
|
"""
|
|
699
702
|
Check if the operation was an upsert (mixed create/update).
|
|
700
|
-
|
|
703
|
+
|
|
701
704
|
Args:
|
|
702
705
|
result_objects: List of objects returned from the operation
|
|
703
|
-
|
|
706
|
+
|
|
704
707
|
Returns:
|
|
705
708
|
True if this was an upsert operation, False otherwise
|
|
706
709
|
"""
|
|
707
710
|
if not result_objects:
|
|
708
711
|
return False
|
|
709
|
-
|
|
712
|
+
|
|
710
713
|
# Check if any object has upsert metadata
|
|
711
|
-
return hasattr(result_objects[0],
|
|
714
|
+
return hasattr(result_objects[0], "_bulk_hooks_upsert_metadata")
|
|
712
715
|
|
|
713
716
|
def _dispatch_upsert_after_hooks(self, result_objects, models_in_chain):
|
|
714
717
|
"""
|
|
715
718
|
Dispatch after hooks for upsert operations, splitting by create/update.
|
|
716
|
-
|
|
719
|
+
|
|
717
720
|
This matches Salesforce behavior:
|
|
718
721
|
- Records that were created fire after_create hooks
|
|
719
722
|
- Records that were updated fire after_update hooks
|
|
720
|
-
|
|
723
|
+
|
|
721
724
|
Args:
|
|
722
725
|
result_objects: List of objects returned from the operation
|
|
723
726
|
models_in_chain: List of model classes in the MTI inheritance chain
|
|
@@ -725,57 +728,75 @@ class BulkOperationCoordinator:
|
|
|
725
728
|
# Split objects by operation type
|
|
726
729
|
created_objects = []
|
|
727
730
|
updated_objects = []
|
|
731
|
+
missing_metadata_count = 0
|
|
728
732
|
|
|
729
733
|
for obj in result_objects:
|
|
730
|
-
|
|
734
|
+
# Check if metadata was set (it MUST be set for upsert operations)
|
|
735
|
+
if not hasattr(obj, '_bulk_hooks_upsert_metadata'):
|
|
736
|
+
# This should never happen - log and treat as created to maintain backward compat
|
|
737
|
+
missing_metadata_count += 1
|
|
738
|
+
logger.warning(
|
|
739
|
+
f"Object {obj} (id={id(obj)}, pk={getattr(obj, 'pk', None)}) "
|
|
740
|
+
f"missing upsert metadata - defaulting to 'created'. "
|
|
741
|
+
f"This may indicate a bug in the upsert metadata tagging.",
|
|
742
|
+
)
|
|
743
|
+
was_created = True
|
|
744
|
+
else:
|
|
745
|
+
was_created = getattr(obj, "_bulk_hooks_was_created", True)
|
|
746
|
+
|
|
731
747
|
if was_created:
|
|
732
748
|
created_objects.append(obj)
|
|
733
749
|
else:
|
|
734
750
|
updated_objects.append(obj)
|
|
735
751
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
752
|
+
if missing_metadata_count > 0:
|
|
753
|
+
logger.error(
|
|
754
|
+
f"UPSERT METADATA BUG: {missing_metadata_count}/{len(result_objects)} objects "
|
|
755
|
+
f"missing metadata. This will cause incorrect hook firing!",
|
|
756
|
+
)
|
|
740
757
|
|
|
758
|
+
logger.info(f"Upsert after hooks: {len(created_objects)} created, {len(updated_objects)} updated")
|
|
759
|
+
|
|
741
760
|
# Dispatch after_create hooks for created objects
|
|
742
761
|
if created_objects:
|
|
743
762
|
from django_bulk_hooks.helpers import build_changeset_for_create
|
|
763
|
+
|
|
744
764
|
create_changeset = build_changeset_for_create(self.model_cls, created_objects)
|
|
745
|
-
|
|
765
|
+
|
|
746
766
|
for model_cls in models_in_chain:
|
|
747
767
|
model_changeset = self._build_changeset_for_model(create_changeset, model_cls)
|
|
748
768
|
self.dispatcher.dispatch(model_changeset, "after_create", bypass_hooks=False)
|
|
749
|
-
|
|
769
|
+
|
|
750
770
|
# Dispatch after_update hooks for updated objects
|
|
751
771
|
if updated_objects:
|
|
752
772
|
# Fetch old records for proper change detection
|
|
753
773
|
old_records_map = self.analyzer.fetch_old_records_map(updated_objects)
|
|
754
|
-
|
|
774
|
+
|
|
755
775
|
from django_bulk_hooks.helpers import build_changeset_for_update
|
|
776
|
+
|
|
756
777
|
update_changeset = build_changeset_for_update(
|
|
757
778
|
self.model_cls,
|
|
758
779
|
updated_objects,
|
|
759
780
|
update_kwargs={}, # Empty since we don't know specific fields
|
|
760
781
|
old_records_map=old_records_map,
|
|
761
782
|
)
|
|
762
|
-
|
|
783
|
+
|
|
763
784
|
for model_cls in models_in_chain:
|
|
764
785
|
model_changeset = self._build_changeset_for_model(update_changeset, model_cls)
|
|
765
786
|
self.dispatcher.dispatch(model_changeset, "after_update", bypass_hooks=False)
|
|
766
|
-
|
|
787
|
+
|
|
767
788
|
# Clean up temporary metadata
|
|
768
789
|
self._cleanup_upsert_metadata(result_objects)
|
|
769
790
|
|
|
770
791
|
def _cleanup_upsert_metadata(self, result_objects):
|
|
771
792
|
"""
|
|
772
793
|
Clean up temporary metadata added during upsert operations.
|
|
773
|
-
|
|
794
|
+
|
|
774
795
|
Args:
|
|
775
796
|
result_objects: List of objects to clean up
|
|
776
797
|
"""
|
|
777
798
|
for obj in result_objects:
|
|
778
|
-
if hasattr(obj,
|
|
779
|
-
delattr(obj,
|
|
780
|
-
if hasattr(obj,
|
|
781
|
-
delattr(obj,
|
|
799
|
+
if hasattr(obj, "_bulk_hooks_was_created"):
|
|
800
|
+
delattr(obj, "_bulk_hooks_was_created")
|
|
801
|
+
if hasattr(obj, "_bulk_hooks_upsert_metadata"):
|
|
802
|
+
delattr(obj, "_bulk_hooks_upsert_metadata")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/__init__.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/analyzer.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/mti_handler.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.44 → django_bulk_hooks-0.2.45}/django_bulk_hooks/operations/mti_plans.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|