django-bulk-hooks 0.1.277__tar.gz → 0.1.279__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.1.277 → django_bulk_hooks-0.1.279}/PKG-INFO +1 -1
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/queryset.py +90 -27
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/pyproject.toml +1 -1
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/LICENSE +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/README.md +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/conditions.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/engine.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/manager.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/priority.py +0 -0
- {django_bulk_hooks-0.1.277 → django_bulk_hooks-0.1.279}/django_bulk_hooks/registry.py +0 -0
|
@@ -812,6 +812,16 @@ class HookQuerySetMixin:
|
|
|
812
812
|
"update_fields": update_fields,
|
|
813
813
|
"unique_fields": unique_fields,
|
|
814
814
|
}
|
|
815
|
+
|
|
816
|
+
# If we have classified records from upsert logic, pass them to MTI method
|
|
817
|
+
if (
|
|
818
|
+
update_conflicts
|
|
819
|
+
and unique_fields
|
|
820
|
+
and hasattr(ctx, "upsert_existing_records")
|
|
821
|
+
):
|
|
822
|
+
mti_kwargs["existing_records"] = ctx.upsert_existing_records
|
|
823
|
+
mti_kwargs["new_records"] = ctx.upsert_new_records
|
|
824
|
+
|
|
815
825
|
# Remove custom hook kwargs if present in self.bulk_create signature
|
|
816
826
|
result = self._mti_bulk_create(
|
|
817
827
|
objs,
|
|
@@ -1626,6 +1636,10 @@ class HookQuerySetMixin:
|
|
|
1626
1636
|
then single bulk insert into childmost table.
|
|
1627
1637
|
Sets auto_now_add/auto_now fields for each model in the chain.
|
|
1628
1638
|
"""
|
|
1639
|
+
# Extract classified records if available (for upsert operations)
|
|
1640
|
+
existing_records = kwargs.pop("existing_records", [])
|
|
1641
|
+
new_records = kwargs.pop("new_records", [])
|
|
1642
|
+
|
|
1629
1643
|
# Remove custom hook kwargs before passing to Django internals
|
|
1630
1644
|
django_kwargs = {
|
|
1631
1645
|
k: v
|
|
@@ -1647,12 +1661,23 @@ class HookQuerySetMixin:
|
|
|
1647
1661
|
for i in range(0, len(objs), batch_size):
|
|
1648
1662
|
batch = objs[i : i + batch_size]
|
|
1649
1663
|
batch_result = self._process_mti_bulk_create_batch(
|
|
1650
|
-
batch,
|
|
1664
|
+
batch,
|
|
1665
|
+
inheritance_chain,
|
|
1666
|
+
existing_records,
|
|
1667
|
+
new_records,
|
|
1668
|
+
**django_kwargs,
|
|
1651
1669
|
)
|
|
1652
1670
|
created_objects.extend(batch_result)
|
|
1653
1671
|
return created_objects
|
|
1654
1672
|
|
|
1655
|
-
def _process_mti_bulk_create_batch(
|
|
1673
|
+
def _process_mti_bulk_create_batch(
|
|
1674
|
+
self,
|
|
1675
|
+
batch,
|
|
1676
|
+
inheritance_chain,
|
|
1677
|
+
existing_records=None,
|
|
1678
|
+
new_records=None,
|
|
1679
|
+
**kwargs,
|
|
1680
|
+
):
|
|
1656
1681
|
"""
|
|
1657
1682
|
Process a single batch of objects through the inheritance chain.
|
|
1658
1683
|
Implements Django's suggested workaround #2: O(n) normal inserts into parent
|
|
@@ -1667,41 +1692,79 @@ class HookQuerySetMixin:
|
|
|
1667
1692
|
bypass_hooks = kwargs.get("bypass_hooks", False)
|
|
1668
1693
|
bypass_validation = kwargs.get("bypass_validation", False)
|
|
1669
1694
|
|
|
1695
|
+
# Create a list for lookup (since model instances without PKs are not hashable)
|
|
1696
|
+
existing_records_list = existing_records if existing_records else []
|
|
1697
|
+
|
|
1670
1698
|
for obj in batch:
|
|
1671
1699
|
parent_instances = {}
|
|
1672
1700
|
current_parent = None
|
|
1701
|
+
is_existing_record = obj in existing_records_list
|
|
1702
|
+
|
|
1673
1703
|
for model_class in inheritance_chain[:-1]:
|
|
1674
1704
|
parent_obj = self._create_parent_instance(
|
|
1675
1705
|
obj, model_class, current_parent
|
|
1676
1706
|
)
|
|
1677
1707
|
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1708
|
+
if is_existing_record:
|
|
1709
|
+
# For existing records, we need to update the parent object instead of creating
|
|
1710
|
+
# The parent_obj should already have the correct PK from the database lookup
|
|
1711
|
+
# Fire parent hooks for updates
|
|
1712
|
+
if not bypass_hooks:
|
|
1713
|
+
ctx = HookContext(model_class)
|
|
1714
|
+
if not bypass_validation:
|
|
1715
|
+
engine.run(
|
|
1716
|
+
model_class, VALIDATE_UPDATE, [parent_obj], ctx=ctx
|
|
1717
|
+
)
|
|
1718
|
+
engine.run(model_class, BEFORE_UPDATE, [parent_obj], ctx=ctx)
|
|
1719
|
+
|
|
1720
|
+
# Update the existing parent object
|
|
1721
|
+
# Filter update_fields to only include fields that exist in the parent model
|
|
1722
|
+
parent_update_fields = kwargs.get("update_fields")
|
|
1723
|
+
if parent_update_fields:
|
|
1724
|
+
# Only include fields that exist in the parent model
|
|
1725
|
+
parent_model_fields = {field.name for field in model_class._meta.local_fields}
|
|
1726
|
+
filtered_update_fields = [
|
|
1727
|
+
field for field in parent_update_fields
|
|
1728
|
+
if field in parent_model_fields
|
|
1729
|
+
]
|
|
1730
|
+
parent_obj.save(update_fields=filtered_update_fields)
|
|
1731
|
+
else:
|
|
1732
|
+
parent_obj.save()
|
|
1733
|
+
|
|
1734
|
+
# Fire AFTER_UPDATE hooks for parent
|
|
1735
|
+
if not bypass_hooks:
|
|
1736
|
+
engine.run(model_class, AFTER_UPDATE, [parent_obj], ctx=ctx)
|
|
1737
|
+
else:
|
|
1738
|
+
# For new records, create the parent object as before
|
|
1739
|
+
# Fire parent hooks if not bypassed
|
|
1740
|
+
if not bypass_hooks:
|
|
1741
|
+
ctx = HookContext(model_class)
|
|
1742
|
+
if not bypass_validation:
|
|
1743
|
+
engine.run(
|
|
1744
|
+
model_class, VALIDATE_CREATE, [parent_obj], ctx=ctx
|
|
1745
|
+
)
|
|
1746
|
+
engine.run(model_class, BEFORE_CREATE, [parent_obj], ctx=ctx)
|
|
1747
|
+
|
|
1748
|
+
# Use Django's base manager to create the object and get PKs back
|
|
1749
|
+
# This bypasses hooks and the MTI exception
|
|
1750
|
+
field_values = {
|
|
1751
|
+
field.name: getattr(parent_obj, field.name)
|
|
1752
|
+
for field in model_class._meta.local_fields
|
|
1753
|
+
if hasattr(parent_obj, field.name)
|
|
1754
|
+
and getattr(parent_obj, field.name) is not None
|
|
1755
|
+
}
|
|
1756
|
+
created_obj = model_class._base_manager.using(self.db).create(
|
|
1757
|
+
**field_values
|
|
1758
|
+
)
|
|
1696
1759
|
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1760
|
+
# Update the parent_obj with the created object's PK
|
|
1761
|
+
parent_obj.pk = created_obj.pk
|
|
1762
|
+
parent_obj._state.adding = False
|
|
1763
|
+
parent_obj._state.db = self.db
|
|
1701
1764
|
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1765
|
+
# Fire AFTER_CREATE hooks for parent
|
|
1766
|
+
if not bypass_hooks:
|
|
1767
|
+
engine.run(model_class, AFTER_CREATE, [parent_obj], ctx=ctx)
|
|
1705
1768
|
|
|
1706
1769
|
parent_instances[model_class] = parent_obj
|
|
1707
1770
|
current_parent = parent_obj
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "django-bulk-hooks"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.279"
|
|
4
4
|
description = "Hook-style hooks for Django bulk operations like bulk_create and bulk_update."
|
|
5
5
|
authors = ["Konrad Beck <konrad.beck@merchantcapital.co.za>"]
|
|
6
6
|
readme = "README.md"
|
|
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
|