django-bulk-hooks 0.2.54__tar.gz → 0.2.56__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.2.54 → django_bulk_hooks-0.2.56}/PKG-INFO +1 -1
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/mti_handler.py +36 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/pyproject.toml +1 -1
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/LICENSE +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/README.md +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/changeset.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/conditions.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/dispatcher.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/factory.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/helpers.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/manager.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/__init__.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/analyzer.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/bulk_executor.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/coordinator.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/field_utils.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/mti_plans.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/record_classifier.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/queryset.py +0 -0
- {django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/registry.py +0 -0
{django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/mti_handler.py
RENAMED
|
@@ -207,6 +207,8 @@ class MTIHandler:
|
|
|
207
207
|
update_conflicts=update_conflicts,
|
|
208
208
|
unique_fields=unique_fields,
|
|
209
209
|
update_fields=update_fields,
|
|
210
|
+
existing_record_ids=existing_record_ids,
|
|
211
|
+
existing_pks_map=existing_pks_map,
|
|
210
212
|
)
|
|
211
213
|
|
|
212
214
|
# Build child object templates (without parent links - executor adds them)
|
|
@@ -235,6 +237,8 @@ class MTIHandler:
|
|
|
235
237
|
update_conflicts=False,
|
|
236
238
|
unique_fields=None,
|
|
237
239
|
update_fields=None,
|
|
240
|
+
existing_record_ids=None,
|
|
241
|
+
existing_pks_map=None,
|
|
238
242
|
):
|
|
239
243
|
"""
|
|
240
244
|
Build parent level objects for each level in the inheritance chain.
|
|
@@ -248,6 +252,12 @@ class MTIHandler:
|
|
|
248
252
|
|
|
249
253
|
parent_levels = []
|
|
250
254
|
parent_instances_map = {} # Maps obj id() -> {model_class: parent_instance}
|
|
255
|
+
|
|
256
|
+
# Set defaults
|
|
257
|
+
if existing_record_ids is None:
|
|
258
|
+
existing_record_ids = set()
|
|
259
|
+
if existing_pks_map is None:
|
|
260
|
+
existing_pks_map = {}
|
|
251
261
|
|
|
252
262
|
for level_idx, model_class in enumerate(inheritance_chain[:-1]):
|
|
253
263
|
parent_objs_for_level = []
|
|
@@ -301,6 +311,25 @@ class MTIHandler:
|
|
|
301
311
|
level_update_conflicts = True
|
|
302
312
|
level_unique_fields = normalized_unique
|
|
303
313
|
level_update_fields = filtered_updates
|
|
314
|
+
else:
|
|
315
|
+
# CRITICAL FIX: Even if this parent level doesn't have the unique constraint,
|
|
316
|
+
# we still need update_conflicts=True during MTI upsert. Without it, parent
|
|
317
|
+
# does plain INSERT creating a new parent record with a new PK, breaking the
|
|
318
|
+
# MTI relationship and causing child INSERT to fail on its unique constraint.
|
|
319
|
+
#
|
|
320
|
+
# Solution: Use the primary key as the unique field for parent levels.
|
|
321
|
+
# In MTI upsert with existing_pks_map, parent objects will get their PKs from
|
|
322
|
+
# bulk_create, and we need those PKs to match existing records, not create new ones.
|
|
323
|
+
if existing_record_ids and existing_pks_map:
|
|
324
|
+
# Use primary key for upsert matching at this level
|
|
325
|
+
pk_field = model_class._meta.pk
|
|
326
|
+
level_update_conflicts = True
|
|
327
|
+
level_unique_fields = [pk_field.name]
|
|
328
|
+
# Use a safe update field - pick the first available non-PK field
|
|
329
|
+
# or use the PK itself as a dummy (updating to itself is a no-op)
|
|
330
|
+
available_fields = [f.name for f in model_class._meta.local_fields
|
|
331
|
+
if not isinstance(f, AutoField) and f.name in model_fields_by_name]
|
|
332
|
+
level_update_fields = available_fields[:1] if available_fields else [pk_field.name]
|
|
304
333
|
|
|
305
334
|
# Create parent level
|
|
306
335
|
parent_level = ParentLevel(
|
|
@@ -361,6 +390,13 @@ class MTIHandler:
|
|
|
361
390
|
|
|
362
391
|
# Copy field values from source using centralized field extraction
|
|
363
392
|
for field in parent_model._meta.local_fields:
|
|
393
|
+
# Skip AutoField (primary key) - let Django's bulk_create with update_conflicts
|
|
394
|
+
# handle PK assignment based on unique_fields. Setting PKs manually here causes
|
|
395
|
+
# conflicts when parent records exist but update_conflicts isn't properly configured
|
|
396
|
+
# for this level, leading to IntegrityError on primary key constraint.
|
|
397
|
+
if isinstance(field, AutoField):
|
|
398
|
+
continue
|
|
399
|
+
|
|
364
400
|
if hasattr(source_obj, field.name):
|
|
365
401
|
# Use centralized field value extraction for consistent FK handling
|
|
366
402
|
value = get_field_value_for_db(source_obj, field.name, source_obj.__class__)
|
|
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
|
|
File without changes
|
{django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/__init__.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/analyzer.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/bulk_executor.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/coordinator.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/field_utils.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.54 → django_bulk_hooks-0.2.56}/django_bulk_hooks/operations/mti_plans.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|