django-bulk-hooks 0.1.276__py3-none-any.whl → 0.1.278__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 +86 -27
- {django_bulk_hooks-0.1.276.dist-info → django_bulk_hooks-0.1.278.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.276.dist-info → django_bulk_hooks-0.1.278.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.276.dist-info → django_bulk_hooks-0.1.278.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.276.dist-info → django_bulk_hooks-0.1.278.dist-info}/WHEEL +0 -0
django_bulk_hooks/queryset.py
CHANGED
|
@@ -698,9 +698,16 @@ class HookQuerySetMixin:
|
|
|
698
698
|
if key in existing_db_map:
|
|
699
699
|
db_record = existing_db_map[key]
|
|
700
700
|
# Copy all fields from the database record to ensure completeness
|
|
701
|
+
# but exclude auto_now_add fields which should never be updated
|
|
701
702
|
populated_fields = []
|
|
702
703
|
for field in model_cls._meta.local_fields:
|
|
703
704
|
if field.name != "id": # Don't overwrite the ID
|
|
705
|
+
# Skip auto_now_add fields for existing records
|
|
706
|
+
if (
|
|
707
|
+
hasattr(field, "auto_now_add")
|
|
708
|
+
and field.auto_now_add
|
|
709
|
+
):
|
|
710
|
+
continue
|
|
704
711
|
db_value = getattr(db_record, field.name)
|
|
705
712
|
if (
|
|
706
713
|
db_value is not None
|
|
@@ -805,6 +812,16 @@ class HookQuerySetMixin:
|
|
|
805
812
|
"update_fields": update_fields,
|
|
806
813
|
"unique_fields": unique_fields,
|
|
807
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
|
+
|
|
808
825
|
# Remove custom hook kwargs if present in self.bulk_create signature
|
|
809
826
|
result = self._mti_bulk_create(
|
|
810
827
|
objs,
|
|
@@ -1619,6 +1636,10 @@ class HookQuerySetMixin:
|
|
|
1619
1636
|
then single bulk insert into childmost table.
|
|
1620
1637
|
Sets auto_now_add/auto_now fields for each model in the chain.
|
|
1621
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
|
+
|
|
1622
1643
|
# Remove custom hook kwargs before passing to Django internals
|
|
1623
1644
|
django_kwargs = {
|
|
1624
1645
|
k: v
|
|
@@ -1640,12 +1661,23 @@ class HookQuerySetMixin:
|
|
|
1640
1661
|
for i in range(0, len(objs), batch_size):
|
|
1641
1662
|
batch = objs[i : i + batch_size]
|
|
1642
1663
|
batch_result = self._process_mti_bulk_create_batch(
|
|
1643
|
-
batch,
|
|
1664
|
+
batch,
|
|
1665
|
+
inheritance_chain,
|
|
1666
|
+
existing_records,
|
|
1667
|
+
new_records,
|
|
1668
|
+
**django_kwargs,
|
|
1644
1669
|
)
|
|
1645
1670
|
created_objects.extend(batch_result)
|
|
1646
1671
|
return created_objects
|
|
1647
1672
|
|
|
1648
|
-
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
|
+
):
|
|
1649
1681
|
"""
|
|
1650
1682
|
Process a single batch of objects through the inheritance chain.
|
|
1651
1683
|
Implements Django's suggested workaround #2: O(n) normal inserts into parent
|
|
@@ -1660,41 +1692,68 @@ class HookQuerySetMixin:
|
|
|
1660
1692
|
bypass_hooks = kwargs.get("bypass_hooks", False)
|
|
1661
1693
|
bypass_validation = kwargs.get("bypass_validation", False)
|
|
1662
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
|
+
|
|
1663
1698
|
for obj in batch:
|
|
1664
1699
|
parent_instances = {}
|
|
1665
1700
|
current_parent = None
|
|
1701
|
+
is_existing_record = obj in existing_records_list
|
|
1702
|
+
|
|
1666
1703
|
for model_class in inheritance_chain[:-1]:
|
|
1667
1704
|
parent_obj = self._create_parent_instance(
|
|
1668
1705
|
obj, model_class, current_parent
|
|
1669
1706
|
)
|
|
1670
1707
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
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
|
+
parent_obj.save(update_fields=kwargs.get("update_fields"))
|
|
1722
|
+
|
|
1723
|
+
# Fire AFTER_UPDATE hooks for parent
|
|
1724
|
+
if not bypass_hooks:
|
|
1725
|
+
engine.run(model_class, AFTER_UPDATE, [parent_obj], ctx=ctx)
|
|
1726
|
+
else:
|
|
1727
|
+
# For new records, create the parent object as before
|
|
1728
|
+
# Fire parent hooks if not bypassed
|
|
1729
|
+
if not bypass_hooks:
|
|
1730
|
+
ctx = HookContext(model_class)
|
|
1731
|
+
if not bypass_validation:
|
|
1732
|
+
engine.run(
|
|
1733
|
+
model_class, VALIDATE_CREATE, [parent_obj], ctx=ctx
|
|
1734
|
+
)
|
|
1735
|
+
engine.run(model_class, BEFORE_CREATE, [parent_obj], ctx=ctx)
|
|
1736
|
+
|
|
1737
|
+
# Use Django's base manager to create the object and get PKs back
|
|
1738
|
+
# This bypasses hooks and the MTI exception
|
|
1739
|
+
field_values = {
|
|
1740
|
+
field.name: getattr(parent_obj, field.name)
|
|
1741
|
+
for field in model_class._meta.local_fields
|
|
1742
|
+
if hasattr(parent_obj, field.name)
|
|
1743
|
+
and getattr(parent_obj, field.name) is not None
|
|
1744
|
+
}
|
|
1745
|
+
created_obj = model_class._base_manager.using(self.db).create(
|
|
1746
|
+
**field_values
|
|
1747
|
+
)
|
|
1689
1748
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1749
|
+
# Update the parent_obj with the created object's PK
|
|
1750
|
+
parent_obj.pk = created_obj.pk
|
|
1751
|
+
parent_obj._state.adding = False
|
|
1752
|
+
parent_obj._state.db = self.db
|
|
1694
1753
|
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1754
|
+
# Fire AFTER_CREATE hooks for parent
|
|
1755
|
+
if not bypass_hooks:
|
|
1756
|
+
engine.run(model_class, AFTER_CREATE, [parent_obj], ctx=ctx)
|
|
1698
1757
|
|
|
1699
1758
|
parent_instances[model_class] = parent_obj
|
|
1700
1759
|
current_parent = parent_obj
|
|
@@ -9,9 +9,9 @@ django_bulk_hooks/handler.py,sha256=e_GACTQT-pFF-zL7POeo232MgOikUoCLcxDVInAUiBw,
|
|
|
9
9
|
django_bulk_hooks/manager.py,sha256=3jNWL-EkvGScsliNc7mW-ozQCG6HyaEevI1u1BFS4AA,3836
|
|
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=fk3ekLxOut8F_Ox4NHH8buJwBgHFWzkizFypKwbxJJE,97211
|
|
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.278.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
+
django_bulk_hooks-0.1.278.dist-info/METADATA,sha256=YdGIP02B81MI5OHZdqcckaN3gG-7_cEqb4mhMWazQQ8,9103
|
|
16
|
+
django_bulk_hooks-0.1.278.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
17
|
+
django_bulk_hooks-0.1.278.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|