django-bulk-hooks 0.1.78__py3-none-any.whl → 0.1.79__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.
Potentially problematic release.
This version of django-bulk-hooks might be problematic. Click here for more details.
- django_bulk_hooks/__init__.py +2 -0
- django_bulk_hooks/conditions.py +29 -1
- django_bulk_hooks/engine.py +1 -28
- django_bulk_hooks/handler.py +1 -1
- django_bulk_hooks/registry.py +2 -3
- {django_bulk_hooks-0.1.78.dist-info → django_bulk_hooks-0.1.79.dist-info}/METADATA +3 -106
- django_bulk_hooks-0.1.79.dist-info/RECORD +16 -0
- django_bulk_hooks/priority.py +0 -16
- django_bulk_hooks-0.1.78.dist-info/RECORD +0 -17
- {django_bulk_hooks-0.1.78.dist-info → django_bulk_hooks-0.1.79.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.78.dist-info → django_bulk_hooks-0.1.79.dist-info}/WHEEL +0 -0
django_bulk_hooks/__init__.py
CHANGED
|
@@ -12,6 +12,7 @@ from django_bulk_hooks.constants import (
|
|
|
12
12
|
from django_bulk_hooks.engine import safe_get_related_object, safe_get_related_attr
|
|
13
13
|
from django_bulk_hooks.handler import HookHandler
|
|
14
14
|
from django_bulk_hooks.models import HookModelMixin
|
|
15
|
+
from django_bulk_hooks.enums import Priority
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
17
18
|
"HookHandler",
|
|
@@ -27,4 +28,5 @@ __all__ = [
|
|
|
27
28
|
"VALIDATE_DELETE",
|
|
28
29
|
"safe_get_related_object",
|
|
29
30
|
"safe_get_related_attr",
|
|
31
|
+
"Priority",
|
|
30
32
|
]
|
django_bulk_hooks/conditions.py
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
|
-
from
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def safe_get_related_object(instance, field_name):
|
|
5
|
+
"""
|
|
6
|
+
Safely get a related object without raising RelatedObjectDoesNotExist.
|
|
7
|
+
Returns None if the foreign key field is None or the related object doesn't exist.
|
|
8
|
+
"""
|
|
9
|
+
if not hasattr(instance, field_name):
|
|
10
|
+
return None
|
|
11
|
+
|
|
12
|
+
# Get the foreign key field
|
|
13
|
+
try:
|
|
14
|
+
field = instance._meta.get_field(field_name)
|
|
15
|
+
if not field.is_relation or field.many_to_many or field.one_to_many:
|
|
16
|
+
return getattr(instance, field_name, None)
|
|
17
|
+
except models.FieldDoesNotExist:
|
|
18
|
+
return getattr(instance, field_name, None)
|
|
19
|
+
|
|
20
|
+
# Check if the foreign key field is None
|
|
21
|
+
fk_field_name = f"{field_name}_id"
|
|
22
|
+
if hasattr(instance, fk_field_name) and getattr(instance, fk_field_name) is None:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
# Try to get the related object, but catch RelatedObjectDoesNotExist
|
|
26
|
+
try:
|
|
27
|
+
return getattr(instance, field_name)
|
|
28
|
+
except field.related_model.RelatedObjectDoesNotExist:
|
|
29
|
+
return None
|
|
2
30
|
|
|
3
31
|
|
|
4
32
|
def resolve_dotted_attr(instance, dotted_path):
|
django_bulk_hooks/engine.py
CHANGED
|
@@ -3,38 +3,11 @@ import logging
|
|
|
3
3
|
from django.core.exceptions import ValidationError
|
|
4
4
|
from django.db import models
|
|
5
5
|
from django_bulk_hooks.registry import get_hooks
|
|
6
|
+
from django_bulk_hooks.conditions import safe_get_related_object
|
|
6
7
|
|
|
7
8
|
logger = logging.getLogger(__name__)
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
def safe_get_related_object(instance, field_name):
|
|
11
|
-
"""
|
|
12
|
-
Safely get a related object without raising RelatedObjectDoesNotExist.
|
|
13
|
-
Returns None if the foreign key field is None or the related object doesn't exist.
|
|
14
|
-
"""
|
|
15
|
-
if not hasattr(instance, field_name):
|
|
16
|
-
return None
|
|
17
|
-
|
|
18
|
-
# Get the foreign key field
|
|
19
|
-
try:
|
|
20
|
-
field = instance._meta.get_field(field_name)
|
|
21
|
-
if not field.is_relation or field.many_to_many or field.one_to_many:
|
|
22
|
-
return getattr(instance, field_name, None)
|
|
23
|
-
except models.FieldDoesNotExist:
|
|
24
|
-
return getattr(instance, field_name, None)
|
|
25
|
-
|
|
26
|
-
# Check if the foreign key field is None
|
|
27
|
-
fk_field_name = f"{field_name}_id"
|
|
28
|
-
if hasattr(instance, fk_field_name) and getattr(instance, fk_field_name) is None:
|
|
29
|
-
return None
|
|
30
|
-
|
|
31
|
-
# Try to get the related object, but catch RelatedObjectDoesNotExist
|
|
32
|
-
try:
|
|
33
|
-
return getattr(instance, field_name)
|
|
34
|
-
except field.related_model.RelatedObjectDoesNotExist:
|
|
35
|
-
return None
|
|
36
|
-
|
|
37
|
-
|
|
38
11
|
def safe_get_related_attr(instance, field_name, attr_name=None):
|
|
39
12
|
"""
|
|
40
13
|
Safely get a related object or its attribute without raising RelatedObjectDoesNotExist.
|
django_bulk_hooks/handler.py
CHANGED
django_bulk_hooks/registry.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from typing import Union
|
|
3
3
|
|
|
4
|
-
from django_bulk_hooks.
|
|
4
|
+
from django_bulk_hooks.enums import Priority
|
|
5
5
|
|
|
6
6
|
_hooks: dict[tuple[type, str], list[tuple[type, str, Callable, int]]] = {}
|
|
7
7
|
|
|
@@ -17,8 +17,7 @@ def register_hook(
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def get_hooks(model, event):
|
|
20
|
-
|
|
21
|
-
return hooks
|
|
20
|
+
return _hooks.get((model, event), [])
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
def list_all_hooks():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: django-bulk-hooks
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.79
|
|
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
|
|
@@ -58,7 +58,7 @@ from django_bulk_hooks import hook, AFTER_UPDATE, Hook
|
|
|
58
58
|
from django_bulk_hooks.conditions import WhenFieldHasChanged
|
|
59
59
|
from .models import Account
|
|
60
60
|
|
|
61
|
-
class AccountHooks(
|
|
61
|
+
class AccountHooks(HookHandler):
|
|
62
62
|
@hook(AFTER_UPDATE, model=Account, condition=WhenFieldHasChanged("balance"))
|
|
63
63
|
def log_balance_change(self, new_records, old_records):
|
|
64
64
|
print("Accounts updated:", [a.pk for a in new_records])
|
|
@@ -74,99 +74,10 @@ class AccountHooks(Hook):
|
|
|
74
74
|
print("Accounts deleted:", [a.pk for a in old_records])
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
## 🛠 Supported Hook Events
|
|
78
|
-
|
|
79
|
-
- `BEFORE_CREATE`, `AFTER_CREATE`
|
|
80
|
-
- `BEFORE_UPDATE`, `AFTER_UPDATE`
|
|
81
|
-
- `BEFORE_DELETE`, `AFTER_DELETE`
|
|
82
|
-
|
|
83
|
-
## 🔄 Lifecycle Events
|
|
84
|
-
|
|
85
|
-
### Individual Model Operations
|
|
86
|
-
|
|
87
|
-
The `HookModelMixin` automatically triggers hooks for individual model operations:
|
|
88
|
-
|
|
89
|
-
```python
|
|
90
|
-
# These will trigger BEFORE_CREATE and AFTER_CREATE hooks
|
|
91
|
-
account = Account.objects.create(balance=100.00)
|
|
92
|
-
account.save() # for new instances
|
|
93
|
-
|
|
94
|
-
# These will trigger BEFORE_UPDATE and AFTER_UPDATE hooks
|
|
95
|
-
account.balance = 200.00
|
|
96
|
-
account.save() # for existing instances
|
|
97
|
-
|
|
98
|
-
# This will trigger BEFORE_DELETE and AFTER_DELETE hooks
|
|
99
|
-
account.delete()
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Bulk Operations
|
|
103
|
-
|
|
104
|
-
Bulk operations also trigger the same hooks:
|
|
105
|
-
|
|
106
|
-
```python
|
|
107
|
-
# Bulk create - triggers BEFORE_CREATE and AFTER_CREATE hooks
|
|
108
|
-
accounts = [
|
|
109
|
-
Account(balance=100.00),
|
|
110
|
-
Account(balance=200.00),
|
|
111
|
-
]
|
|
112
|
-
Account.objects.bulk_create(accounts)
|
|
113
|
-
|
|
114
|
-
# Bulk update - triggers BEFORE_UPDATE and AFTER_UPDATE hooks
|
|
115
|
-
for account in accounts:
|
|
116
|
-
account.balance *= 1.1
|
|
117
|
-
Account.objects.bulk_update(accounts, ['balance'])
|
|
118
|
-
|
|
119
|
-
# Bulk delete - triggers BEFORE_DELETE and AFTER_DELETE hooks
|
|
120
|
-
Account.objects.bulk_delete(accounts)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Queryset Operations
|
|
124
|
-
|
|
125
|
-
Queryset operations are also supported:
|
|
126
|
-
|
|
127
|
-
```python
|
|
128
|
-
# Queryset update - triggers BEFORE_UPDATE and AFTER_UPDATE hooks
|
|
129
|
-
Account.objects.update(balance=0.00)
|
|
130
|
-
|
|
131
|
-
# Queryset delete - triggers BEFORE_DELETE and AFTER_DELETE hooks
|
|
132
|
-
Account.objects.delete()
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## 🧠 Why?
|
|
136
|
-
|
|
137
|
-
Django's `bulk_` methods bypass signals and `save()`. This package fills that gap with:
|
|
138
|
-
|
|
139
|
-
- Hooks that behave consistently across creates/updates/deletes
|
|
140
|
-
- **NEW**: Individual model lifecycle hooks that work with `save()` and `delete()`
|
|
141
|
-
- Scalable performance via chunking (default 200)
|
|
142
|
-
- Support for `@hook` decorators and centralized hook classes
|
|
143
|
-
- **NEW**: Automatic hook triggering for admin operations and other Django features
|
|
144
|
-
|
|
145
|
-
## 📦 Usage Examples
|
|
146
|
-
|
|
147
|
-
### Individual Model Operations
|
|
148
|
-
|
|
149
|
-
```python
|
|
150
|
-
# These automatically trigger hooks
|
|
151
|
-
account = Account.objects.create(balance=100.00)
|
|
152
|
-
account.balance = 200.00
|
|
153
|
-
account.save()
|
|
154
|
-
account.delete()
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Bulk Operations
|
|
158
|
-
|
|
159
|
-
```python
|
|
160
|
-
# These also trigger hooks
|
|
161
|
-
Account.objects.bulk_create(accounts)
|
|
162
|
-
Account.objects.bulk_update(accounts, ['balance'])
|
|
163
|
-
Account.objects.bulk_delete(accounts)
|
|
164
|
-
```
|
|
165
|
-
|
|
166
77
|
### Advanced Hook Usage
|
|
167
78
|
|
|
168
79
|
```python
|
|
169
|
-
class AdvancedAccountHooks(
|
|
80
|
+
class AdvancedAccountHooks(HookHandler):
|
|
170
81
|
@hook(BEFORE_UPDATE, model=Account, condition=WhenFieldHasChanged("balance"))
|
|
171
82
|
def validate_balance_change(self, new_records, old_records):
|
|
172
83
|
for new_account, old_account in zip(new_records, old_records):
|
|
@@ -179,17 +90,3 @@ class AdvancedAccountHooks(Hook):
|
|
|
179
90
|
# Send welcome email logic here
|
|
180
91
|
pass
|
|
181
92
|
```
|
|
182
|
-
|
|
183
|
-
## 🧩 Integration with Queryable Properties
|
|
184
|
-
|
|
185
|
-
You can extend from `BulkHookManager` to support formula fields or property querying.
|
|
186
|
-
|
|
187
|
-
```python
|
|
188
|
-
class MyManager(BulkHookManager, QueryablePropertiesManager):
|
|
189
|
-
pass
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## 📝 License
|
|
193
|
-
|
|
194
|
-
MIT © 2024 Augend / Konrad Beck
|
|
195
|
-
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
django_bulk_hooks/__init__.py,sha256=JU6GMrqIPDm1n8zc78iTd1I1pzubmdkBJwROMaecZPM,805
|
|
2
|
+
django_bulk_hooks/conditions.py,sha256=o31qTnSfEkAKMVnFPF0JdkuvMFWb92Lc-o-WQ2CoWAk,8107
|
|
3
|
+
django_bulk_hooks/constants.py,sha256=3x1H1fSUUNo0DZONN7GUVDuySZctTR-jtByBHmAIX5w,303
|
|
4
|
+
django_bulk_hooks/context.py,sha256=HVDT73uSzvgrOR6mdXTvsBm3hLOgBU8ant_mB7VlFuM,380
|
|
5
|
+
django_bulk_hooks/decorators.py,sha256=zstmb27dKcOHu3Atg7cauewCTzPvUmq03mzVKJRi56o,7230
|
|
6
|
+
django_bulk_hooks/engine.py,sha256=ru6oxoXEj4eC8JM30hzLj-jcD-_Mcg-SaPqexxR87c0,2183
|
|
7
|
+
django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
|
|
8
|
+
django_bulk_hooks/handler.py,sha256=Qpg_zT6SsQiTlhduvzXxPdG6uynjyR2fBjj-R6HZiXI,4861
|
|
9
|
+
django_bulk_hooks/manager.py,sha256=-V128ACxPAz82ua4jQRFUkjAKtKW4MN5ppz0bHcv5s4,7138
|
|
10
|
+
django_bulk_hooks/models.py,sha256=7RG7GrOdHXFjGVPV4FPRZVNMIHHW-hMCi6hn9LH_hVI,3331
|
|
11
|
+
django_bulk_hooks/queryset.py,sha256=7lLqhZ-XOYsZ1I3Loxi4Nhz79M8HlTYE413AW8nyeDI,1330
|
|
12
|
+
django_bulk_hooks/registry.py,sha256=Vh78exKYcdZhM27120kQm-iXGOjd_kf9ZUYBZ8eQ2V0,683
|
|
13
|
+
django_bulk_hooks-0.1.79.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
14
|
+
django_bulk_hooks-0.1.79.dist-info/METADATA,sha256=MfSByYpJLtek9y5Vr8GjUt635pK0McIEZRCE2W0O0mA,3388
|
|
15
|
+
django_bulk_hooks-0.1.79.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
16
|
+
django_bulk_hooks-0.1.79.dist-info/RECORD,,
|
django_bulk_hooks/priority.py
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
from enum import IntEnum
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Priority(IntEnum):
|
|
5
|
-
"""
|
|
6
|
-
Named priorities for django-bulk-hooks hooks.
|
|
7
|
-
|
|
8
|
-
Lower values run earlier (higher priority).
|
|
9
|
-
Hooks are sorted in ascending order.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
HIGHEST = 0 # runs first
|
|
13
|
-
HIGH = 25 # runs early
|
|
14
|
-
NORMAL = 50 # default ordering
|
|
15
|
-
LOW = 75 # runs later
|
|
16
|
-
LOWEST = 100 # runs last
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
django_bulk_hooks/__init__.py,sha256=_Hu0OVtOtAMV4SJXAf60WanGDGNSmx4iekVFEnbiP7o,742
|
|
2
|
-
django_bulk_hooks/conditions.py,sha256=yPrWj1nA1ptpQFG5zhHmRwt4rswhnkKSWM4ZyicDF58,7094
|
|
3
|
-
django_bulk_hooks/constants.py,sha256=3x1H1fSUUNo0DZONN7GUVDuySZctTR-jtByBHmAIX5w,303
|
|
4
|
-
django_bulk_hooks/context.py,sha256=HVDT73uSzvgrOR6mdXTvsBm3hLOgBU8ant_mB7VlFuM,380
|
|
5
|
-
django_bulk_hooks/decorators.py,sha256=zstmb27dKcOHu3Atg7cauewCTzPvUmq03mzVKJRi56o,7230
|
|
6
|
-
django_bulk_hooks/engine.py,sha256=5zj7R2Pot6T17RCq9BZ8ajc7vgRJJYAUODA3qzwSAe0,3162
|
|
7
|
-
django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
|
|
8
|
-
django_bulk_hooks/handler.py,sha256=xZt8iNdYF-ACz-MnKMY0co6scWINU5V5wC1lyDn844k,4854
|
|
9
|
-
django_bulk_hooks/manager.py,sha256=-V128ACxPAz82ua4jQRFUkjAKtKW4MN5ppz0bHcv5s4,7138
|
|
10
|
-
django_bulk_hooks/models.py,sha256=7RG7GrOdHXFjGVPV4FPRZVNMIHHW-hMCi6hn9LH_hVI,3331
|
|
11
|
-
django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
|
|
12
|
-
django_bulk_hooks/queryset.py,sha256=7lLqhZ-XOYsZ1I3Loxi4Nhz79M8HlTYE413AW8nyeDI,1330
|
|
13
|
-
django_bulk_hooks/registry.py,sha256=-mQBizJ06nz_tajZBinViKx_uP2Tbc1tIpTEMv7lwKA,705
|
|
14
|
-
django_bulk_hooks-0.1.78.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
-
django_bulk_hooks-0.1.78.dist-info/METADATA,sha256=hS-l3fJebt6fVD4UaMpFrDzIyqKidkkoQ_8H8KlifxM,5930
|
|
16
|
-
django_bulk_hooks-0.1.78.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
17
|
-
django_bulk_hooks-0.1.78.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|