django-bulk-hooks 0.1.123__tar.gz → 0.1.125__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.123 → django_bulk_hooks-0.1.125}/PKG-INFO +1 -1
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/queryset.py +32 -41
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/pyproject.toml +1 -1
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/LICENSE +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/README.md +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/conditions.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/engine.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/manager.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/priority.py +0 -0
- {django_bulk_hooks-0.1.123 → django_bulk_hooks-0.1.125}/django_bulk_hooks/registry.py +0 -0
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
from django.db
|
|
3
|
-
from django.db import
|
|
4
|
-
from django.db.models.constants import OnConflict
|
|
5
|
-
from django.db.models.expressions import DatabaseDefault
|
|
6
|
-
import operator
|
|
7
|
-
from functools import reduce
|
|
1
|
+
|
|
2
|
+
from django.db import models, transaction
|
|
3
|
+
from django.db.models import AutoField
|
|
8
4
|
|
|
9
5
|
from django_bulk_hooks import engine
|
|
10
6
|
from django_bulk_hooks.constants import (
|
|
@@ -302,9 +298,13 @@ class HookQuerySet(models.QuerySet):
|
|
|
302
298
|
def _process_mti_batch(self, batch, inheritance_chain, **kwargs):
|
|
303
299
|
"""
|
|
304
300
|
Process a single batch of objects through the inheritance chain.
|
|
301
|
+
Reuses Django's internal functions as much as possible.
|
|
305
302
|
"""
|
|
306
|
-
#
|
|
303
|
+
# For MTI, we need to save parent objects first to get PKs
|
|
304
|
+
# Then we can use Django's bulk_create for the child objects
|
|
307
305
|
parent_objects_map = {}
|
|
306
|
+
|
|
307
|
+
# Step 1: Save parent objects (we need their PKs for child objects)
|
|
308
308
|
for obj in batch:
|
|
309
309
|
parent_instances = {}
|
|
310
310
|
current_parent = None
|
|
@@ -312,11 +312,13 @@ class HookQuerySet(models.QuerySet):
|
|
|
312
312
|
parent_obj = self._create_parent_instance(
|
|
313
313
|
obj, model_class, current_parent
|
|
314
314
|
)
|
|
315
|
-
|
|
315
|
+
# Use Django's internal save method to avoid hooks
|
|
316
|
+
models.Model.save(parent_obj)
|
|
316
317
|
parent_instances[model_class] = parent_obj
|
|
317
318
|
current_parent = parent_obj
|
|
318
319
|
parent_objects_map[id(obj)] = parent_instances
|
|
319
|
-
|
|
320
|
+
|
|
321
|
+
# Step 2: Create and bulk insert child objects
|
|
320
322
|
child_model = inheritance_chain[-1]
|
|
321
323
|
child_objects = []
|
|
322
324
|
for obj in batch:
|
|
@@ -324,43 +326,19 @@ class HookQuerySet(models.QuerySet):
|
|
|
324
326
|
obj, child_model, parent_objects_map.get(id(obj), {})
|
|
325
327
|
)
|
|
326
328
|
child_objects.append(child_obj)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if not current_model._meta.proxy:
|
|
334
|
-
child_inheritance_chain.append(current_model)
|
|
335
|
-
parents = [
|
|
336
|
-
parent
|
|
337
|
-
for parent in current_model._meta.parents.keys()
|
|
338
|
-
if not parent._meta.proxy
|
|
339
|
-
]
|
|
340
|
-
current_model = parents[0] if parents else None
|
|
341
|
-
child_inheritance_chain.reverse()
|
|
342
|
-
|
|
343
|
-
# For nested MTI, we can't use bulk operations recursively
|
|
344
|
-
# because it would create infinite recursion. Instead, we save each child individually.
|
|
345
|
-
# We use super().save() to avoid triggering hooks that would cause recursion.
|
|
346
|
-
created = []
|
|
347
|
-
for child_obj in child_objects:
|
|
348
|
-
# Use the base model's save method to avoid triggering hooks
|
|
349
|
-
# This prevents infinite recursion when hooks try to query the database
|
|
350
|
-
super(child_obj.__class__, child_obj).save()
|
|
351
|
-
created.append(child_obj)
|
|
352
|
-
else:
|
|
353
|
-
# Single-table, safe to use bulk_create
|
|
354
|
-
|
|
355
|
-
child_manager = child_model._base_manager
|
|
356
|
-
child_manager._for_write = True
|
|
357
|
-
created = child_manager.bulk_create(child_objects, **kwargs)
|
|
329
|
+
|
|
330
|
+
# Use Django's bulk_create for child objects - this handles auto_now_add correctly
|
|
331
|
+
child_manager = child_model._base_manager
|
|
332
|
+
child_manager._for_write = True
|
|
333
|
+
created = child_manager.bulk_create(child_objects, **kwargs)
|
|
334
|
+
|
|
358
335
|
# Step 3: Update original objects with generated PKs and state
|
|
359
336
|
pk_field_name = child_model._meta.pk.name
|
|
360
337
|
for orig_obj, child_obj in zip(batch, created):
|
|
361
338
|
setattr(orig_obj, pk_field_name, getattr(child_obj, pk_field_name))
|
|
362
339
|
orig_obj._state.adding = False
|
|
363
340
|
orig_obj._state.db = self.db
|
|
341
|
+
|
|
364
342
|
return batch
|
|
365
343
|
|
|
366
344
|
def _create_parent_instance(self, source_obj, parent_model, current_parent):
|
|
@@ -388,6 +366,12 @@ class HookQuerySet(models.QuerySet):
|
|
|
388
366
|
elif hasattr(field, 'auto_now') and field.auto_now:
|
|
389
367
|
field.pre_save(parent_obj, add=True)
|
|
390
368
|
|
|
369
|
+
# Ensure auto_now_add fields are explicitly set to prevent null constraint violations
|
|
370
|
+
for field in parent_model._meta.local_fields:
|
|
371
|
+
if hasattr(field, 'auto_now_add') and field.auto_now_add:
|
|
372
|
+
if getattr(parent_obj, field.name) is None:
|
|
373
|
+
field.pre_save(parent_obj, add=True)
|
|
374
|
+
|
|
391
375
|
return parent_obj
|
|
392
376
|
|
|
393
377
|
def _create_child_instance(self, source_obj, child_model, parent_instances):
|
|
@@ -411,4 +395,11 @@ class HookQuerySet(models.QuerySet):
|
|
|
411
395
|
elif hasattr(field, 'auto_now') and field.auto_now:
|
|
412
396
|
field.pre_save(child_obj, add=True)
|
|
413
397
|
|
|
398
|
+
# Ensure auto_now_add fields are explicitly set to prevent null constraint violations
|
|
399
|
+
for field in child_model._meta.local_fields:
|
|
400
|
+
if hasattr(field, 'auto_now_add') and field.auto_now_add:
|
|
401
|
+
if getattr(child_obj, field.name) is None:
|
|
402
|
+
field.pre_save(child_obj, add=True)
|
|
403
|
+
|
|
414
404
|
return child_obj
|
|
405
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "django-bulk-hooks"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.125"
|
|
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
|