django-bulk-hooks 0.1.115__py3-none-any.whl → 0.1.117__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/manager.py +98 -41
- {django_bulk_hooks-0.1.115.dist-info → django_bulk_hooks-0.1.117.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.115.dist-info → django_bulk_hooks-0.1.117.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.115.dist-info → django_bulk_hooks-0.1.117.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.115.dist-info → django_bulk_hooks-0.1.117.dist-info}/WHEEL +0 -0
django_bulk_hooks/manager.py
CHANGED
|
@@ -72,6 +72,83 @@ class BulkHookManager(models.Manager):
|
|
|
72
72
|
|
|
73
73
|
return objs
|
|
74
74
|
|
|
75
|
+
@transaction.atomic
|
|
76
|
+
def bulk_create(
|
|
77
|
+
self,
|
|
78
|
+
objs,
|
|
79
|
+
batch_size=None,
|
|
80
|
+
ignore_conflicts=False,
|
|
81
|
+
update_conflicts=False,
|
|
82
|
+
update_fields=None,
|
|
83
|
+
unique_fields=None,
|
|
84
|
+
bypass_hooks=False,
|
|
85
|
+
bypass_validation=False,
|
|
86
|
+
):
|
|
87
|
+
"""
|
|
88
|
+
Insert each of the instances into the database. Behaves like Django's bulk_create,
|
|
89
|
+
but supports multi-table inheritance (MTI) models. All arguments are supported and
|
|
90
|
+
passed through to the correct logic. For MTI, only a subset of options may be supported.
|
|
91
|
+
"""
|
|
92
|
+
model_cls = self.model
|
|
93
|
+
|
|
94
|
+
if batch_size is not None and batch_size <= 0:
|
|
95
|
+
raise ValueError("Batch size must be a positive integer.")
|
|
96
|
+
|
|
97
|
+
# Check that the parents share the same concrete model with our model to detect inheritance pattern
|
|
98
|
+
for parent in model_cls._meta.all_parents:
|
|
99
|
+
if parent._meta.concrete_model is not model_cls._meta.concrete_model:
|
|
100
|
+
# We allow this for MTI, but not for single-table
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
if not objs:
|
|
104
|
+
return objs
|
|
105
|
+
|
|
106
|
+
if any(not isinstance(obj, model_cls) for obj in objs):
|
|
107
|
+
raise TypeError(
|
|
108
|
+
f"bulk_create expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Set auto_now_add/auto_now fields before DB ops
|
|
112
|
+
self._set_auto_now_fields(objs, model_cls)
|
|
113
|
+
|
|
114
|
+
# Fire hooks before DB ops
|
|
115
|
+
if not bypass_hooks:
|
|
116
|
+
ctx = HookContext(model_cls)
|
|
117
|
+
if not bypass_validation:
|
|
118
|
+
engine.run(model_cls, VALIDATE_CREATE, objs, ctx=ctx)
|
|
119
|
+
engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
|
|
120
|
+
|
|
121
|
+
# MTI detection: if inheritance chain > 1, use MTI logic
|
|
122
|
+
inheritance_chain = self._get_inheritance_chain()
|
|
123
|
+
if len(inheritance_chain) <= 1:
|
|
124
|
+
# Single-table: use Django's standard bulk_create
|
|
125
|
+
# Pass through all supported arguments
|
|
126
|
+
result = super(models.Manager, self).bulk_create(
|
|
127
|
+
objs,
|
|
128
|
+
batch_size=batch_size,
|
|
129
|
+
ignore_conflicts=ignore_conflicts,
|
|
130
|
+
update_conflicts=update_conflicts,
|
|
131
|
+
update_fields=update_fields,
|
|
132
|
+
unique_fields=unique_fields,
|
|
133
|
+
)
|
|
134
|
+
else:
|
|
135
|
+
# Multi-table: use workaround (parent saves, child bulk)
|
|
136
|
+
# Only batch_size is supported for MTI; others will raise NotImplementedError
|
|
137
|
+
if ignore_conflicts or update_conflicts or update_fields or unique_fields:
|
|
138
|
+
raise NotImplementedError(
|
|
139
|
+
"bulk_create with ignore_conflicts, update_conflicts, update_fields, or unique_fields is not supported for multi-table inheritance models."
|
|
140
|
+
)
|
|
141
|
+
result = self._mti_bulk_create(
|
|
142
|
+
objs, inheritance_chain, batch_size=batch_size
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if not bypass_hooks:
|
|
146
|
+
engine.run(model_cls, AFTER_CREATE, result, ctx=ctx)
|
|
147
|
+
|
|
148
|
+
return result
|
|
149
|
+
|
|
150
|
+
# --- Private helper methods (moved to bottom for clarity) ---
|
|
151
|
+
|
|
75
152
|
def _detect_modified_fields(self, new_instances, original_instances):
|
|
76
153
|
"""
|
|
77
154
|
Detect fields that were modified during BEFORE_UPDATE hooks by comparing
|
|
@@ -109,47 +186,6 @@ class BulkHookManager(models.Manager):
|
|
|
109
186
|
|
|
110
187
|
return modified_fields
|
|
111
188
|
|
|
112
|
-
@transaction.atomic
|
|
113
|
-
def bulk_create(self, objs, bypass_hooks=False, bypass_validation=False, **kwargs):
|
|
114
|
-
"""
|
|
115
|
-
Enhanced bulk_create that handles multi-table inheritance (MTI) and single-table models.
|
|
116
|
-
Falls back to Django's standard bulk_create for single-table models.
|
|
117
|
-
Fires hooks as usual.
|
|
118
|
-
"""
|
|
119
|
-
model_cls = self.model
|
|
120
|
-
|
|
121
|
-
if not objs:
|
|
122
|
-
return []
|
|
123
|
-
|
|
124
|
-
if any(not isinstance(obj, model_cls) for obj in objs):
|
|
125
|
-
raise TypeError(
|
|
126
|
-
f"bulk_create expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
# Fire hooks before DB ops
|
|
130
|
-
if not bypass_hooks:
|
|
131
|
-
ctx = HookContext(model_cls)
|
|
132
|
-
if not bypass_validation:
|
|
133
|
-
engine.run(model_cls, VALIDATE_CREATE, objs, ctx=ctx)
|
|
134
|
-
engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
|
|
135
|
-
|
|
136
|
-
# MTI detection: if inheritance chain > 1, use MTI logic
|
|
137
|
-
inheritance_chain = self._get_inheritance_chain()
|
|
138
|
-
if len(inheritance_chain) <= 1:
|
|
139
|
-
# Single-table: use Django's standard bulk_create
|
|
140
|
-
result = []
|
|
141
|
-
for i in range(0, len(objs), self.CHUNK_SIZE):
|
|
142
|
-
chunk = objs[i : i + self.CHUNK_SIZE]
|
|
143
|
-
result.extend(super(models.Manager, self).bulk_create(chunk, **kwargs))
|
|
144
|
-
else:
|
|
145
|
-
# Multi-table: use workaround (parent saves, child bulk)
|
|
146
|
-
result = self._mti_bulk_create(objs, inheritance_chain, **kwargs)
|
|
147
|
-
|
|
148
|
-
if not bypass_hooks:
|
|
149
|
-
engine.run(model_cls, AFTER_CREATE, result, ctx=ctx)
|
|
150
|
-
|
|
151
|
-
return result
|
|
152
|
-
|
|
153
189
|
def _get_inheritance_chain(self):
|
|
154
190
|
"""
|
|
155
191
|
Get the complete inheritance chain from root parent to current model.
|
|
@@ -172,12 +208,16 @@ class BulkHookManager(models.Manager):
|
|
|
172
208
|
def _mti_bulk_create(self, objs, inheritance_chain, **kwargs):
|
|
173
209
|
"""
|
|
174
210
|
Implements workaround: individual saves for parents, bulk create for child.
|
|
211
|
+
Sets auto_now_add/auto_now fields for each model in the chain.
|
|
175
212
|
"""
|
|
176
213
|
batch_size = kwargs.get("batch_size") or len(objs)
|
|
177
214
|
created_objects = []
|
|
178
215
|
with transaction.atomic(using=self.db, savepoint=False):
|
|
179
216
|
for i in range(0, len(objs), batch_size):
|
|
180
217
|
batch = objs[i : i + batch_size]
|
|
218
|
+
# Set auto_now fields for each model in the chain
|
|
219
|
+
for model in inheritance_chain:
|
|
220
|
+
self._set_auto_now_fields(batch, model)
|
|
181
221
|
batch_result = self._process_mti_batch(
|
|
182
222
|
batch, inheritance_chain, **kwargs
|
|
183
223
|
)
|
|
@@ -272,6 +312,23 @@ class BulkHookManager(models.Manager):
|
|
|
272
312
|
setattr(child_obj, parent_link.name, parent_instance)
|
|
273
313
|
return child_obj
|
|
274
314
|
|
|
315
|
+
def _set_auto_now_fields(self, objs, model):
|
|
316
|
+
"""
|
|
317
|
+
Set auto_now_add and auto_now fields on objects before bulk_create.
|
|
318
|
+
"""
|
|
319
|
+
from django.utils import timezone
|
|
320
|
+
|
|
321
|
+
now = timezone.now()
|
|
322
|
+
for obj in objs:
|
|
323
|
+
for field in model._meta.local_fields:
|
|
324
|
+
if (
|
|
325
|
+
getattr(field, "auto_now_add", False)
|
|
326
|
+
and getattr(obj, field.name, None) is None
|
|
327
|
+
):
|
|
328
|
+
setattr(obj, field.name, now)
|
|
329
|
+
if getattr(field, "auto_now", False):
|
|
330
|
+
setattr(obj, field.name, now)
|
|
331
|
+
|
|
275
332
|
@transaction.atomic
|
|
276
333
|
def bulk_delete(
|
|
277
334
|
self, objs, batch_size=None, bypass_hooks=False, bypass_validation=False
|
|
@@ -6,12 +6,12 @@ django_bulk_hooks/decorators.py,sha256=tckDcxtOzKCbgvS9QydgeIAWTFDEl-ch3_Q--ruEG
|
|
|
6
6
|
django_bulk_hooks/engine.py,sha256=3HbgV12JRYIy9IlygHPxZiHnFXj7EwzLyTuJNQeVIoI,1402
|
|
7
7
|
django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
|
|
8
8
|
django_bulk_hooks/handler.py,sha256=xZt8iNdYF-ACz-MnKMY0co6scWINU5V5wC1lyDn844k,4854
|
|
9
|
-
django_bulk_hooks/manager.py,sha256=
|
|
9
|
+
django_bulk_hooks/manager.py,sha256=3WtqgKBqL8uWkOeGnw0xPp3AGpaxJSbT63ltgDxb2fU,15747
|
|
10
10
|
django_bulk_hooks/models.py,sha256=7RG7GrOdHXFjGVPV4FPRZVNMIHHW-hMCi6hn9LH_hVI,3331
|
|
11
11
|
django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
|
|
12
12
|
django_bulk_hooks/queryset.py,sha256=iet4z-9SKhnresA4FBQbxx9rdYnoaOWbw9LUlGftlP0,1466
|
|
13
13
|
django_bulk_hooks/registry.py,sha256=-mQBizJ06nz_tajZBinViKx_uP2Tbc1tIpTEMv7lwKA,705
|
|
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.117.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
+
django_bulk_hooks-0.1.117.dist-info/METADATA,sha256=b1mTw0rJZLbei3sm2bN480iVoU5KzXOs6D-_qyeVcrQ,6939
|
|
16
|
+
django_bulk_hooks-0.1.117.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
17
|
+
django_bulk_hooks-0.1.117.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|