django-bulk-hooks 0.1.131__tar.gz → 0.1.133__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.131 → django_bulk_hooks-0.1.133}/PKG-INFO +1 -1
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/queryset.py +62 -4
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/pyproject.toml +1 -1
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/LICENSE +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/README.md +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/conditions.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/engine.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/manager.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/priority.py +0 -0
- {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/registry.py +0 -0
|
@@ -97,10 +97,15 @@ class HookQuerySet(models.QuerySet):
|
|
|
97
97
|
# MultiTableParent -> ProxyChild. Simply checking self.model._meta.proxy would not
|
|
98
98
|
# identify that case as involving multiple tables.
|
|
99
99
|
is_mti = False
|
|
100
|
+
print(f"DEBUG: Checking for MTI on {model_cls.__name__}")
|
|
101
|
+
print(f"DEBUG: All parents: {[p.__name__ for p in model_cls._meta.all_parents]}")
|
|
100
102
|
for parent in model_cls._meta.all_parents:
|
|
103
|
+
print(f"DEBUG: Checking parent {parent.__name__} (concrete: {parent._meta.concrete_model.__name__}) vs {model_cls.__name__} (concrete: {model_cls._meta.concrete_model.__name__})")
|
|
101
104
|
if parent._meta.concrete_model is not model_cls._meta.concrete_model:
|
|
102
105
|
is_mti = True
|
|
106
|
+
print(f"DEBUG: MTI detected! {parent.__name__} and {model_cls.__name__} have different concrete models")
|
|
103
107
|
break
|
|
108
|
+
print(f"DEBUG: Final MTI result: {is_mti}")
|
|
104
109
|
|
|
105
110
|
if not objs:
|
|
106
111
|
return objs
|
|
@@ -119,6 +124,7 @@ class HookQuerySet(models.QuerySet):
|
|
|
119
124
|
|
|
120
125
|
# For MTI models, we need to handle them specially
|
|
121
126
|
if is_mti:
|
|
127
|
+
print(f"DEBUG: Using MTI-specific logic for {model_cls.__name__}")
|
|
122
128
|
# Use our MTI-specific logic
|
|
123
129
|
result = self._mti_bulk_create(
|
|
124
130
|
objs,
|
|
@@ -129,6 +135,7 @@ class HookQuerySet(models.QuerySet):
|
|
|
129
135
|
unique_fields=unique_fields,
|
|
130
136
|
)
|
|
131
137
|
else:
|
|
138
|
+
print(f"DEBUG: Using standard bulk_create for {model_cls.__name__}")
|
|
132
139
|
# For single-table models, use Django's built-in bulk_create
|
|
133
140
|
# but we need to call it on the base manager to avoid recursion
|
|
134
141
|
|
|
@@ -325,10 +332,13 @@ class HookQuerySet(models.QuerySet):
|
|
|
325
332
|
parent_objects_map = {}
|
|
326
333
|
|
|
327
334
|
# Step 1: Do O(n) normal inserts into parent tables to get primary keys back
|
|
335
|
+
print(f"DEBUG: Processing batch of {len(batch)} objects")
|
|
336
|
+
print(f"DEBUG: Inheritance chain: {[m.__name__ for m in inheritance_chain]}")
|
|
328
337
|
for obj in batch:
|
|
329
338
|
parent_instances = {}
|
|
330
339
|
current_parent = None
|
|
331
340
|
for model_class in inheritance_chain[:-1]:
|
|
341
|
+
print(f"DEBUG: Creating parent instance for {model_class.__name__}")
|
|
332
342
|
parent_obj = self._create_parent_instance(
|
|
333
343
|
obj, model_class, current_parent
|
|
334
344
|
)
|
|
@@ -339,7 +349,9 @@ class HookQuerySet(models.QuerySet):
|
|
|
339
349
|
for field in model_class._meta.local_fields
|
|
340
350
|
if hasattr(parent_obj, field.name) and getattr(parent_obj, field.name) is not None
|
|
341
351
|
}
|
|
352
|
+
print(f"DEBUG: Creating {model_class.__name__} with field_values: {field_values}")
|
|
342
353
|
created_obj = model_class._base_manager.using(self.db).create(**field_values)
|
|
354
|
+
print(f"DEBUG: Created {model_class.__name__} with PK: {created_obj.pk}")
|
|
343
355
|
# Update the parent_obj with the created object's PK
|
|
344
356
|
parent_obj.pk = created_obj.pk
|
|
345
357
|
parent_obj._state.adding = False
|
|
@@ -350,25 +362,71 @@ class HookQuerySet(models.QuerySet):
|
|
|
350
362
|
|
|
351
363
|
# Step 2: Create all child objects and do single bulk insert into childmost table
|
|
352
364
|
child_model = inheritance_chain[-1]
|
|
365
|
+
print(f"DEBUG: Creating child objects for {child_model.__name__}")
|
|
353
366
|
all_child_objects = []
|
|
354
367
|
for obj in batch:
|
|
355
368
|
child_obj = self._create_child_instance(
|
|
356
369
|
obj, child_model, parent_objects_map.get(id(obj), {})
|
|
357
370
|
)
|
|
358
371
|
all_child_objects.append(child_obj)
|
|
372
|
+
print(f"DEBUG: Created {len(all_child_objects)} child objects")
|
|
359
373
|
|
|
360
|
-
# Step 2.5:
|
|
374
|
+
# Step 2.5: Use Django's internal _batched_insert method directly
|
|
361
375
|
if all_child_objects:
|
|
362
|
-
|
|
363
|
-
|
|
376
|
+
print(f"DEBUG: Using Django's internal _batched_insert for {len(all_child_objects)} child objects")
|
|
377
|
+
|
|
378
|
+
# Get the base manager's queryset
|
|
379
|
+
base_qs = child_model._base_manager.using(self.db)
|
|
380
|
+
|
|
381
|
+
# Use Django's internal _batched_insert method directly
|
|
382
|
+
# This bypasses the MTI check but uses Django's optimized bulk insert
|
|
383
|
+
opts = child_model._meta
|
|
384
|
+
fields = [f for f in opts.concrete_fields if not f.generated]
|
|
385
|
+
|
|
386
|
+
# Prepare objects for bulk insert (same logic as Django's bulk_create)
|
|
387
|
+
objs_with_pk, objs_without_pk = base_qs._prepare_for_bulk_create(all_child_objects)
|
|
388
|
+
|
|
389
|
+
with transaction.atomic(using=self.db, savepoint=False):
|
|
390
|
+
if objs_with_pk:
|
|
391
|
+
returned_columns = base_qs._batched_insert(
|
|
392
|
+
objs_with_pk,
|
|
393
|
+
fields,
|
|
394
|
+
batch_size=None,
|
|
395
|
+
)
|
|
396
|
+
for obj_with_pk, results in zip(objs_with_pk, returned_columns):
|
|
397
|
+
for result, field in zip(results, opts.db_returning_fields):
|
|
398
|
+
if field != opts.pk:
|
|
399
|
+
setattr(obj_with_pk, field.attname, result)
|
|
400
|
+
for obj_with_pk in objs_with_pk:
|
|
401
|
+
obj_with_pk._state.adding = False
|
|
402
|
+
obj_with_pk._state.db = self.db
|
|
403
|
+
|
|
404
|
+
if objs_without_pk:
|
|
405
|
+
fields = [f for f in fields if not isinstance(f, AutoField)]
|
|
406
|
+
returned_columns = base_qs._batched_insert(
|
|
407
|
+
objs_without_pk,
|
|
408
|
+
fields,
|
|
409
|
+
batch_size=None,
|
|
410
|
+
)
|
|
411
|
+
for obj_without_pk, results in zip(objs_without_pk, returned_columns):
|
|
412
|
+
for result, field in zip(results, opts.db_returning_fields):
|
|
413
|
+
setattr(obj_without_pk, field.attname, result)
|
|
414
|
+
obj_without_pk._state.adding = False
|
|
415
|
+
obj_without_pk._state.db = self.db
|
|
416
|
+
|
|
417
|
+
print(f"DEBUG: Successfully bulk created child objects using Django's internal methods")
|
|
364
418
|
|
|
365
419
|
# Step 3: Update original objects with generated PKs and state
|
|
366
420
|
pk_field_name = child_model._meta.pk.name
|
|
421
|
+
print(f"DEBUG: Updating original objects with PK field: {pk_field_name}")
|
|
367
422
|
for orig_obj, child_obj in zip(batch, all_child_objects):
|
|
368
|
-
|
|
423
|
+
child_pk = getattr(child_obj, pk_field_name)
|
|
424
|
+
print(f"DEBUG: Setting {orig_obj.__class__.__name__} PK to {child_pk}")
|
|
425
|
+
setattr(orig_obj, pk_field_name, child_pk)
|
|
369
426
|
orig_obj._state.adding = False
|
|
370
427
|
orig_obj._state.db = self.db
|
|
371
428
|
|
|
429
|
+
print(f"DEBUG: Completed processing batch")
|
|
372
430
|
return batch
|
|
373
431
|
|
|
374
432
|
def _create_parent_instance(self, source_obj, parent_model, current_parent):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "django-bulk-hooks"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.133"
|
|
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
|