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.
@@ -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
- objects = BulkHookManager()
12
-
13
- class Meta:
14
- abstract = True
15
-
16
- def clean(self, bypass_hooks=False):
17
- """
18
- Override clean() to hook validation hooks.
19
- This ensures that when Django calls clean() (like in admin forms),
20
- it hooks the VALIDATE_* hooks for validation only.
21
- """
22
- super().clean()
23
-
24
- # If bypass_hooks is True, skip validation hooks
25
- if bypass_hooks:
26
- return
27
-
28
- # Delegate to coordinator (consistent with save/delete)
29
- is_create = self.pk is None
30
- self.__class__.objects.get_queryset().coordinator.clean(
31
- [self], is_create=is_create,
32
- )
33
-
34
- def save(self, *args, bypass_hooks=False, **kwargs):
35
- """
36
- Save the model instance.
37
-
38
- Delegates to bulk_create/bulk_update which handle all hook logic
39
- including MTI parent hooks.
40
- """
41
- if bypass_hooks:
42
- # Use super().save() to call Django's default save without our hook logic
43
- return super().save(*args, **kwargs)
44
-
45
- is_create = self.pk is None
46
-
47
- if is_create:
48
- # Delegate to bulk_create which handles all hook logic
49
- result = self.__class__.objects.bulk_create([self])
50
- return result[0] if result else self
51
- # Delegate to bulk_update which handles all hook logic
52
- update_fields = kwargs.get("update_fields")
53
- if update_fields is None:
54
- # Update all non-auto fields
55
- update_fields = [
56
- f.name
57
- for f in self.__class__._meta.fields
58
- if not f.auto_created and f.name != "id"
59
- ]
60
- self.__class__.objects.bulk_update([self], update_fields)
61
- return self
62
-
63
- def delete(self, *args, bypass_hooks=False, **kwargs):
64
- """
65
- Delete the model instance.
66
-
67
- Delegates to bulk_delete which handles all hook logic
68
- including MTI parent hooks.
69
- """
70
- if bypass_hooks:
71
- # Use super().delete() to call Django's default delete without our hook logic
72
- return super().delete(*args, **kwargs)
73
-
74
- # Delegate to bulk_delete (handles both MTI and non-MTI)
75
- return self.__class__.objects.filter(pk=self.pk).delete()
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()