django-bulk-hooks 0.2.44__py3-none-any.whl → 0.2.93__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.
- django_bulk_hooks/__init__.py +0 -3
- django_bulk_hooks/changeset.py +214 -230
- django_bulk_hooks/conditions.py +7 -3
- django_bulk_hooks/decorators.py +5 -15
- django_bulk_hooks/dispatcher.py +546 -242
- django_bulk_hooks/handler.py +2 -2
- django_bulk_hooks/helpers.py +258 -100
- django_bulk_hooks/manager.py +134 -130
- django_bulk_hooks/models.py +89 -75
- django_bulk_hooks/operations/analyzer.py +466 -315
- django_bulk_hooks/operations/bulk_executor.py +608 -413
- django_bulk_hooks/operations/coordinator.py +601 -454
- django_bulk_hooks/operations/field_utils.py +335 -0
- django_bulk_hooks/operations/mti_handler.py +696 -511
- django_bulk_hooks/operations/mti_plans.py +103 -96
- django_bulk_hooks/operations/record_classifier.py +35 -23
- django_bulk_hooks/queryset.py +60 -15
- django_bulk_hooks/registry.py +0 -2
- {django_bulk_hooks-0.2.44.dist-info → django_bulk_hooks-0.2.93.dist-info}/METADATA +55 -4
- django_bulk_hooks-0.2.93.dist-info/RECORD +27 -0
- django_bulk_hooks-0.2.44.dist-info/RECORD +0 -26
- {django_bulk_hooks-0.2.44.dist-info → django_bulk_hooks-0.2.93.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.2.44.dist-info → django_bulk_hooks-0.2.93.dist-info}/WHEEL +0 -0
django_bulk_hooks/models.py
CHANGED
|
@@ -1,75 +1,89 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from django.db import models
|
|
4
|
-
|
|
5
|
-
from django_bulk_hooks.manager import BulkHookManager
|
|
6
|
-
|
|
7
|
-
logger = logging.getLogger(__name__)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class HookModelMixin(models.Model):
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return self
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
|
|
5
|
+
from django_bulk_hooks.manager import BulkHookManager
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HookModelMixin(models.Model):
|
|
11
|
+
"""Mixin providing hook functionality."""
|
|
12
|
+
|
|
13
|
+
objects = BulkHookManager()
|
|
14
|
+
|
|
15
|
+
class Meta:
|
|
16
|
+
abstract = True
|
|
17
|
+
|
|
18
|
+
def clean(self, bypass_hooks=False):
|
|
19
|
+
"""
|
|
20
|
+
Override clean() to hook validation hooks.
|
|
21
|
+
This ensures that when Django calls clean() (like in admin forms),
|
|
22
|
+
it hooks the VALIDATE_* hooks for validation only.
|
|
23
|
+
"""
|
|
24
|
+
super().clean()
|
|
25
|
+
|
|
26
|
+
# If bypass_hooks is True, skip validation hooks
|
|
27
|
+
if bypass_hooks:
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
# Delegate to coordinator (consistent with save/delete)
|
|
31
|
+
is_create = self.pk is None
|
|
32
|
+
self.__class__.objects.get_queryset().coordinator.clean(
|
|
33
|
+
[self],
|
|
34
|
+
is_create=is_create,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def save(self, *args, bypass_hooks=False, **kwargs):
|
|
38
|
+
"""
|
|
39
|
+
Save the model instance.
|
|
40
|
+
|
|
41
|
+
Delegates to bulk_create/bulk_update which handle all hook logic
|
|
42
|
+
including MTI parent hooks.
|
|
43
|
+
"""
|
|
44
|
+
if bypass_hooks:
|
|
45
|
+
# Use super().save() to call Django's default save without our hook logic
|
|
46
|
+
return super().save(*args, **kwargs)
|
|
47
|
+
|
|
48
|
+
is_create = self.pk is None
|
|
49
|
+
|
|
50
|
+
logger.debug("💾 SAVE_START: model=%s, pk=%s, is_create=%s, __dict__=%s",
|
|
51
|
+
self.__class__.__name__, self.pk, is_create, list(self.__dict__.keys()))
|
|
52
|
+
|
|
53
|
+
if is_create:
|
|
54
|
+
# Delegate to bulk_create which handles all hook logic
|
|
55
|
+
result = self.__class__.objects.bulk_create([self])
|
|
56
|
+
return result[0] if result else self
|
|
57
|
+
# Delegate to bulk_update which handles all hook logic
|
|
58
|
+
update_fields = kwargs.get("update_fields")
|
|
59
|
+
if update_fields is None:
|
|
60
|
+
# Update all non-auto fields
|
|
61
|
+
update_fields = [f.name for f in self.__class__._meta.fields if not f.auto_created and f.name != "id"]
|
|
62
|
+
|
|
63
|
+
logger.debug("💾 SAVE_UPDATE_FIELDS: fields=%s (count=%d)", update_fields, len(update_fields))
|
|
64
|
+
|
|
65
|
+
# Log FK field values before bulk_update
|
|
66
|
+
for field in self.__class__._meta.fields:
|
|
67
|
+
if field.get_internal_type() == 'ForeignKey' and field.name in update_fields:
|
|
68
|
+
fk_id_value = getattr(self, field.attname, 'NO_ATTR')
|
|
69
|
+
fk_obj_value = getattr(self, field.name, 'NO_ATTR')
|
|
70
|
+
logger.debug("💾 SAVE_FK_CHECK: field=%s, %s=%s, %s=%s (has_pk=%s)",
|
|
71
|
+
field.name, field.attname, fk_id_value, field.name, fk_obj_value,
|
|
72
|
+
hasattr(fk_obj_value, 'pk') if fk_obj_value != 'NO_ATTR' else 'N/A')
|
|
73
|
+
|
|
74
|
+
self.__class__.objects.bulk_update([self], update_fields)
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def delete(self, *args, bypass_hooks=False, **kwargs):
|
|
78
|
+
"""
|
|
79
|
+
Delete the model instance.
|
|
80
|
+
|
|
81
|
+
Delegates to bulk_delete which handles all hook logic
|
|
82
|
+
including MTI parent hooks.
|
|
83
|
+
"""
|
|
84
|
+
if bypass_hooks:
|
|
85
|
+
# Use super().delete() to call Django's default delete without our hook logic
|
|
86
|
+
return super().delete(*args, **kwargs)
|
|
87
|
+
|
|
88
|
+
# Delegate to bulk_delete (handles both MTI and non-MTI)
|
|
89
|
+
return self.__class__.objects.filter(pk=self.pk).delete()
|