django-bulk-hooks 0.1.103__tar.gz → 0.1.104__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.103 → django_bulk_hooks-0.1.104}/PKG-INFO +3 -3
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/manager.py +121 -1
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/pyproject.toml +1 -1
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/LICENSE +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/README.md +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/conditions.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/engine.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/priority.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/queryset.py +0 -0
- {django_bulk_hooks-0.1.103 → django_bulk_hooks-0.1.104}/django_bulk_hooks/registry.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: django-bulk-hooks
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.104
|
|
4
4
|
Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
|
|
5
|
-
Home-page: https://github.com/AugendLimited/django-bulk-hooks
|
|
6
5
|
License: MIT
|
|
7
6
|
Keywords: django,bulk,hooks
|
|
8
7
|
Author: Konrad Beck
|
|
@@ -14,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
15
|
Requires-Dist: Django (>=4.0)
|
|
16
|
+
Project-URL: Homepage, https://github.com/AugendLimited/django-bulk-hooks
|
|
17
17
|
Project-URL: Repository, https://github.com/AugendLimited/django-bulk-hooks
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
|
|
@@ -22,6 +22,114 @@ class BulkHookManager(models.Manager):
|
|
|
22
22
|
def get_queryset(self):
|
|
23
23
|
return HookQuerySet(self.model, using=self._db)
|
|
24
24
|
|
|
25
|
+
def _has_multi_table_inheritance(self, model_cls):
|
|
26
|
+
"""
|
|
27
|
+
Check if this model uses multi-table inheritance.
|
|
28
|
+
"""
|
|
29
|
+
return (
|
|
30
|
+
model_cls._meta.parents and
|
|
31
|
+
not all(parent._meta.abstract for parent in model_cls._meta.parents.values())
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _get_base_model(self, model_cls):
|
|
35
|
+
"""
|
|
36
|
+
Get the base model (first non-abstract parent or self).
|
|
37
|
+
"""
|
|
38
|
+
base_model = model_cls
|
|
39
|
+
while base_model._meta.parents:
|
|
40
|
+
parent = next(iter(base_model._meta.parents.values()))
|
|
41
|
+
if not parent._meta.abstract:
|
|
42
|
+
base_model = parent
|
|
43
|
+
else:
|
|
44
|
+
break
|
|
45
|
+
return base_model
|
|
46
|
+
|
|
47
|
+
def _extract_base_objects(self, objs, model_cls):
|
|
48
|
+
"""
|
|
49
|
+
Extract base model objects from inherited objects.
|
|
50
|
+
"""
|
|
51
|
+
base_model = self._get_base_model(model_cls)
|
|
52
|
+
base_objects = []
|
|
53
|
+
|
|
54
|
+
for obj in objs:
|
|
55
|
+
base_obj = base_model()
|
|
56
|
+
for field in base_model._meta.fields:
|
|
57
|
+
if field.name != 'id' and hasattr(obj, field.name):
|
|
58
|
+
setattr(base_obj, field.name, getattr(obj, field.name))
|
|
59
|
+
base_objects.append(base_obj)
|
|
60
|
+
|
|
61
|
+
return base_objects
|
|
62
|
+
|
|
63
|
+
def _extract_child_objects(self, objs, model_cls):
|
|
64
|
+
"""
|
|
65
|
+
Extract child model objects from inherited objects.
|
|
66
|
+
"""
|
|
67
|
+
child_objects = []
|
|
68
|
+
|
|
69
|
+
for obj in objs:
|
|
70
|
+
child_obj = model_cls()
|
|
71
|
+
child_obj.pk = obj.pk # Set the same PK as base
|
|
72
|
+
|
|
73
|
+
# Copy only fields specific to this model
|
|
74
|
+
for field in model_cls._meta.fields:
|
|
75
|
+
if (field.name != 'id' and
|
|
76
|
+
field.model == model_cls and
|
|
77
|
+
hasattr(obj, field.name)):
|
|
78
|
+
setattr(child_obj, field.name, getattr(obj, field.name))
|
|
79
|
+
|
|
80
|
+
child_objects.append(child_obj)
|
|
81
|
+
|
|
82
|
+
return child_objects
|
|
83
|
+
|
|
84
|
+
def _bulk_create_inherited(self, objs, **kwargs):
|
|
85
|
+
"""
|
|
86
|
+
Handle bulk create for inherited models by handling each table separately.
|
|
87
|
+
"""
|
|
88
|
+
if not objs:
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
model_cls = self.model
|
|
92
|
+
result = []
|
|
93
|
+
|
|
94
|
+
# Group objects by their actual class
|
|
95
|
+
objects_by_class = {}
|
|
96
|
+
for obj in objs:
|
|
97
|
+
obj_class = obj.__class__
|
|
98
|
+
if obj_class not in objects_by_class:
|
|
99
|
+
objects_by_class[obj_class] = []
|
|
100
|
+
objects_by_class[obj_class].append(obj)
|
|
101
|
+
|
|
102
|
+
for obj_class, class_objects in objects_by_class.items():
|
|
103
|
+
# Check if this class has multi-table inheritance
|
|
104
|
+
parent_models = [p for p in obj_class._meta.get_parent_list()
|
|
105
|
+
if not p._meta.abstract]
|
|
106
|
+
|
|
107
|
+
if not parent_models:
|
|
108
|
+
# No inheritance, use standard bulk_create
|
|
109
|
+
chunk_result = super(models.Manager, self).bulk_create(class_objects, **kwargs)
|
|
110
|
+
result.extend(chunk_result)
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
# Handle multi-table inheritance
|
|
114
|
+
# Step 1: Bulk create base objects without hooks
|
|
115
|
+
base_objects = self._extract_base_objects(class_objects, obj_class)
|
|
116
|
+
created_base = super(models.Manager, self).bulk_create(base_objects, **kwargs)
|
|
117
|
+
|
|
118
|
+
# Step 2: Update original objects with base IDs
|
|
119
|
+
for obj, base_obj in zip(class_objects, created_base):
|
|
120
|
+
obj.pk = base_obj.pk
|
|
121
|
+
obj._state.adding = False
|
|
122
|
+
|
|
123
|
+
# Step 3: Bulk create child objects without hooks
|
|
124
|
+
child_objects = self._extract_child_objects(class_objects, obj_class)
|
|
125
|
+
if child_objects:
|
|
126
|
+
# Use _base_manager to avoid recursion
|
|
127
|
+
obj_class._base_manager.bulk_create(child_objects, **kwargs)
|
|
128
|
+
|
|
129
|
+
result.extend(class_objects)
|
|
130
|
+
|
|
131
|
+
return result
|
|
132
|
+
|
|
25
133
|
@transaction.atomic
|
|
26
134
|
def bulk_update(
|
|
27
135
|
self, objs, fields, bypass_hooks=False, bypass_validation=False, **kwargs
|
|
@@ -116,6 +224,9 @@ class BulkHookManager(models.Manager):
|
|
|
116
224
|
f"bulk_create expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
117
225
|
)
|
|
118
226
|
|
|
227
|
+
# Check if this model uses multi-table inheritance
|
|
228
|
+
has_multi_table_inheritance = self._has_multi_table_inheritance(model_cls)
|
|
229
|
+
|
|
119
230
|
result = []
|
|
120
231
|
|
|
121
232
|
if not bypass_hooks:
|
|
@@ -128,9 +239,18 @@ class BulkHookManager(models.Manager):
|
|
|
128
239
|
# Then run business logic hooks
|
|
129
240
|
engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
|
|
130
241
|
|
|
242
|
+
# Perform bulk create in chunks
|
|
131
243
|
for i in range(0, len(objs), self.CHUNK_SIZE):
|
|
132
244
|
chunk = objs[i : i + self.CHUNK_SIZE]
|
|
133
|
-
|
|
245
|
+
|
|
246
|
+
if has_multi_table_inheritance:
|
|
247
|
+
# Use our multi-table bulk create
|
|
248
|
+
created_chunk = self._bulk_create_inherited(chunk, **kwargs)
|
|
249
|
+
else:
|
|
250
|
+
# Use Django's standard bulk create
|
|
251
|
+
created_chunk = super(models.Manager, self).bulk_create(chunk, **kwargs)
|
|
252
|
+
|
|
253
|
+
result.extend(created_chunk)
|
|
134
254
|
|
|
135
255
|
if not bypass_hooks:
|
|
136
256
|
engine.run(model_cls, AFTER_CREATE, result, ctx=ctx)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "django-bulk-hooks"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.104"
|
|
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
|