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.

Files changed (17) hide show
  1. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/PKG-INFO +1 -1
  2. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/queryset.py +62 -4
  3. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/pyproject.toml +1 -1
  4. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/LICENSE +0 -0
  5. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/README.md +0 -0
  6. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/__init__.py +0 -0
  7. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/conditions.py +0 -0
  8. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/constants.py +0 -0
  9. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/context.py +0 -0
  10. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/decorators.py +0 -0
  11. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/engine.py +0 -0
  12. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/enums.py +0 -0
  13. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/handler.py +0 -0
  14. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/manager.py +0 -0
  15. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/models.py +0 -0
  16. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/priority.py +0 -0
  17. {django_bulk_hooks-0.1.131 → django_bulk_hooks-0.1.133}/django_bulk_hooks/registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.131
3
+ Version: 0.1.133
4
4
  Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
5
5
  License: MIT
6
6
  Keywords: django,bulk,hooks
@@ -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: Single bulk insert into childmost table
374
+ # Step 2.5: Use Django's internal _batched_insert method directly
361
375
  if all_child_objects:
362
- # Use Django's internal bulk_create to bypass MTI exception
363
- child_model._base_manager.bulk_create(all_child_objects)
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
- setattr(orig_obj, pk_field_name, getattr(child_obj, pk_field_name))
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.131"
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"