django-bulk-hooks 0.1.100__tar.gz → 0.1.102__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.1.100 → django_bulk_hooks-0.1.102}/PKG-INFO +1 -164
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/README.md +1 -163
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/__init__.py +11 -23
- django_bulk_hooks-0.1.102/django_bulk_hooks/conditions.py +376 -0
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/engine.py +15 -26
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/handler.py +1 -13
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/manager.py +69 -31
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/models.py +14 -33
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/pyproject.toml +1 -1
- django_bulk_hooks-0.1.100/django_bulk_hooks/conditions.py +0 -358
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/LICENSE +0 -0
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/queryset.py +0 -0
- {django_bulk_hooks-0.1.100 → django_bulk_hooks-0.1.102}/django_bulk_hooks/registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: django-bulk-hooks
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.102
|
|
4
4
|
Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
|
|
5
5
|
Home-page: https://github.com/AugendLimited/django-bulk-hooks
|
|
6
6
|
License: MIT
|
|
@@ -226,166 +226,3 @@ class EfficientTransactionHandler:
|
|
|
226
226
|
```
|
|
227
227
|
|
|
228
228
|
This approach ensures your hooks are robust and won't fail due to missing related objects, while also being efficient with database queries.
|
|
229
|
-
|
|
230
|
-
## 🎯 Lambda Conditions and Anonymous Functions
|
|
231
|
-
|
|
232
|
-
`django-bulk-hooks` supports using anonymous functions (lambda functions) and custom callables as conditions, giving you maximum flexibility for complex filtering logic.
|
|
233
|
-
|
|
234
|
-
### Using LambdaCondition
|
|
235
|
-
|
|
236
|
-
The `LambdaCondition` class allows you to use lambda functions or any callable as a condition:
|
|
237
|
-
|
|
238
|
-
```python
|
|
239
|
-
from django_bulk_hooks import LambdaCondition
|
|
240
|
-
|
|
241
|
-
class ProductHandler:
|
|
242
|
-
# Simple lambda condition
|
|
243
|
-
@hook(Product, "after_create", condition=LambdaCondition(
|
|
244
|
-
lambda instance: instance.price > 100
|
|
245
|
-
))
|
|
246
|
-
def handle_expensive_products(self, new_records, old_records):
|
|
247
|
-
"""Handle products with price > 100"""
|
|
248
|
-
for product in new_records:
|
|
249
|
-
print(f"Expensive product: {product.name}")
|
|
250
|
-
|
|
251
|
-
# Lambda with multiple conditions
|
|
252
|
-
@hook(Product, "after_update", condition=LambdaCondition(
|
|
253
|
-
lambda instance: instance.price > 50 and instance.is_active and instance.stock_quantity > 0
|
|
254
|
-
))
|
|
255
|
-
def handle_available_expensive_products(self, new_records, old_records):
|
|
256
|
-
"""Handle active products with price > 50 and stock > 0"""
|
|
257
|
-
for product in new_records:
|
|
258
|
-
print(f"Available expensive product: {product.name}")
|
|
259
|
-
|
|
260
|
-
# Lambda comparing with original instance
|
|
261
|
-
@hook(Product, "after_update", condition=LambdaCondition(
|
|
262
|
-
lambda instance, original: original and instance.price > original.price * 1.5
|
|
263
|
-
))
|
|
264
|
-
def handle_significant_price_increases(self, new_records, old_records):
|
|
265
|
-
"""Handle products with >50% price increase"""
|
|
266
|
-
for new_product, old_product in zip(new_records, old_records):
|
|
267
|
-
if old_product:
|
|
268
|
-
increase = ((new_product.price - old_product.price) / old_product.price) * 100
|
|
269
|
-
print(f"Significant price increase: {new_product.name} +{increase:.1f}%")
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### Combining Lambda Conditions with Built-in Conditions
|
|
273
|
-
|
|
274
|
-
You can combine lambda conditions with built-in conditions using the `&` (AND) and `|` (OR) operators:
|
|
275
|
-
|
|
276
|
-
```python
|
|
277
|
-
from django_bulk_hooks.conditions import HasChanged, IsEqual
|
|
278
|
-
|
|
279
|
-
class AdvancedProductHandler:
|
|
280
|
-
# Combine lambda with built-in conditions
|
|
281
|
-
@hook(Product, "after_update", condition=(
|
|
282
|
-
HasChanged("price") &
|
|
283
|
-
LambdaCondition(lambda instance: instance.price > 100)
|
|
284
|
-
))
|
|
285
|
-
def handle_expensive_price_changes(self, new_records, old_records):
|
|
286
|
-
"""Handle when expensive products have price changes"""
|
|
287
|
-
for new_product, old_product in zip(new_records, old_records):
|
|
288
|
-
print(f"Expensive product price changed: {new_product.name}")
|
|
289
|
-
|
|
290
|
-
# Complex combined conditions
|
|
291
|
-
@hook(Order, "after_update", condition=(
|
|
292
|
-
LambdaCondition(lambda instance: instance.status == 'completed') &
|
|
293
|
-
LambdaCondition(lambda instance, original: original and instance.total_amount > original.total_amount)
|
|
294
|
-
))
|
|
295
|
-
def handle_completed_orders_with_increased_amount(self, new_records, old_records):
|
|
296
|
-
"""Handle completed orders that had amount increases"""
|
|
297
|
-
for new_order, old_order in zip(new_records, old_records):
|
|
298
|
-
if old_order:
|
|
299
|
-
increase = new_order.total_amount - old_order.total_amount
|
|
300
|
-
print(f"Completed order with amount increase: {new_order.customer_name} +${increase}")
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### Custom Condition Classes
|
|
304
|
-
|
|
305
|
-
For reusable logic, you can create custom condition classes:
|
|
306
|
-
|
|
307
|
-
```python
|
|
308
|
-
from django_bulk_hooks.conditions import HookCondition
|
|
309
|
-
|
|
310
|
-
class IsPremiumProduct(HookCondition):
|
|
311
|
-
def check(self, instance, original_instance=None):
|
|
312
|
-
return (
|
|
313
|
-
instance.price > 200 and
|
|
314
|
-
instance.rating >= 4.0 and
|
|
315
|
-
instance.is_active
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
def get_required_fields(self):
|
|
319
|
-
return {'price', 'rating', 'is_active'}
|
|
320
|
-
|
|
321
|
-
class ProductHandler:
|
|
322
|
-
@hook(Product, "after_create", condition=IsPremiumProduct())
|
|
323
|
-
def handle_premium_products(self, new_records, old_records):
|
|
324
|
-
"""Handle premium products"""
|
|
325
|
-
for product in new_records:
|
|
326
|
-
print(f"Premium product: {product.name}")
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Lambda Conditions with Required Fields
|
|
330
|
-
|
|
331
|
-
For optimization, you can specify which fields your lambda condition depends on:
|
|
332
|
-
|
|
333
|
-
```python
|
|
334
|
-
class OptimizedProductHandler:
|
|
335
|
-
@hook(Product, "after_update", condition=LambdaCondition(
|
|
336
|
-
lambda instance: instance.price > 100 and instance.category == 'electronics',
|
|
337
|
-
required_fields={'price', 'category'}
|
|
338
|
-
))
|
|
339
|
-
def handle_expensive_electronics(self, new_records, old_records):
|
|
340
|
-
"""Handle expensive electronics products"""
|
|
341
|
-
for product in new_records:
|
|
342
|
-
print(f"Expensive electronics: {product.name}")
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### Best Practices for Lambda Conditions
|
|
346
|
-
|
|
347
|
-
1. **Keep lambdas simple** - Complex logic should be moved to custom condition classes
|
|
348
|
-
2. **Handle None values** - Always check for None before performing operations
|
|
349
|
-
3. **Specify required fields** - This helps with query optimization
|
|
350
|
-
4. **Use descriptive names** - Make your lambda conditions self-documenting
|
|
351
|
-
5. **Test thoroughly** - Lambda conditions can be harder to debug than named functions
|
|
352
|
-
|
|
353
|
-
```python
|
|
354
|
-
# ✅ GOOD: Simple, clear lambda
|
|
355
|
-
condition = LambdaCondition(lambda instance: instance.price > 100)
|
|
356
|
-
|
|
357
|
-
# ✅ GOOD: Handles None values
|
|
358
|
-
condition = LambdaCondition(
|
|
359
|
-
lambda instance: instance.price is not None and instance.price > 100
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
# ❌ AVOID: Complex logic in lambda
|
|
363
|
-
condition = LambdaCondition(
|
|
364
|
-
lambda instance: (
|
|
365
|
-
instance.price > 100 and
|
|
366
|
-
instance.category in ['electronics', 'computers'] and
|
|
367
|
-
instance.stock_quantity > 0 and
|
|
368
|
-
instance.rating >= 4.0 and
|
|
369
|
-
instance.is_active and
|
|
370
|
-
instance.created_at > datetime.now() - timedelta(days=30)
|
|
371
|
-
)
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
# ✅ BETTER: Use custom condition class for complex logic
|
|
375
|
-
class IsRecentExpensiveElectronics(HookCondition):
|
|
376
|
-
def check(self, instance, original_instance=None):
|
|
377
|
-
return (
|
|
378
|
-
instance.price > 100 and
|
|
379
|
-
instance.category in ['electronics', 'computers'] and
|
|
380
|
-
instance.stock_quantity > 0 and
|
|
381
|
-
instance.rating >= 4.0 and
|
|
382
|
-
instance.is_active and
|
|
383
|
-
instance.created_at > datetime.now() - timedelta(days=30)
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
def get_required_fields(self):
|
|
387
|
-
return {'price', 'category', 'stock_quantity', 'rating', 'is_active', 'created_at'}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
## 🔧 Best Practices for Related Objects
|
|
391
|
-
|
|
@@ -206,166 +206,4 @@ class EfficientTransactionHandler:
|
|
|
206
206
|
self._batch_process_failed(transactions)
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
This approach ensures your hooks are robust and won't fail due to missing related objects, while also being efficient with database queries.
|
|
210
|
-
|
|
211
|
-
## 🎯 Lambda Conditions and Anonymous Functions
|
|
212
|
-
|
|
213
|
-
`django-bulk-hooks` supports using anonymous functions (lambda functions) and custom callables as conditions, giving you maximum flexibility for complex filtering logic.
|
|
214
|
-
|
|
215
|
-
### Using LambdaCondition
|
|
216
|
-
|
|
217
|
-
The `LambdaCondition` class allows you to use lambda functions or any callable as a condition:
|
|
218
|
-
|
|
219
|
-
```python
|
|
220
|
-
from django_bulk_hooks import LambdaCondition
|
|
221
|
-
|
|
222
|
-
class ProductHandler:
|
|
223
|
-
# Simple lambda condition
|
|
224
|
-
@hook(Product, "after_create", condition=LambdaCondition(
|
|
225
|
-
lambda instance: instance.price > 100
|
|
226
|
-
))
|
|
227
|
-
def handle_expensive_products(self, new_records, old_records):
|
|
228
|
-
"""Handle products with price > 100"""
|
|
229
|
-
for product in new_records:
|
|
230
|
-
print(f"Expensive product: {product.name}")
|
|
231
|
-
|
|
232
|
-
# Lambda with multiple conditions
|
|
233
|
-
@hook(Product, "after_update", condition=LambdaCondition(
|
|
234
|
-
lambda instance: instance.price > 50 and instance.is_active and instance.stock_quantity > 0
|
|
235
|
-
))
|
|
236
|
-
def handle_available_expensive_products(self, new_records, old_records):
|
|
237
|
-
"""Handle active products with price > 50 and stock > 0"""
|
|
238
|
-
for product in new_records:
|
|
239
|
-
print(f"Available expensive product: {product.name}")
|
|
240
|
-
|
|
241
|
-
# Lambda comparing with original instance
|
|
242
|
-
@hook(Product, "after_update", condition=LambdaCondition(
|
|
243
|
-
lambda instance, original: original and instance.price > original.price * 1.5
|
|
244
|
-
))
|
|
245
|
-
def handle_significant_price_increases(self, new_records, old_records):
|
|
246
|
-
"""Handle products with >50% price increase"""
|
|
247
|
-
for new_product, old_product in zip(new_records, old_records):
|
|
248
|
-
if old_product:
|
|
249
|
-
increase = ((new_product.price - old_product.price) / old_product.price) * 100
|
|
250
|
-
print(f"Significant price increase: {new_product.name} +{increase:.1f}%")
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Combining Lambda Conditions with Built-in Conditions
|
|
254
|
-
|
|
255
|
-
You can combine lambda conditions with built-in conditions using the `&` (AND) and `|` (OR) operators:
|
|
256
|
-
|
|
257
|
-
```python
|
|
258
|
-
from django_bulk_hooks.conditions import HasChanged, IsEqual
|
|
259
|
-
|
|
260
|
-
class AdvancedProductHandler:
|
|
261
|
-
# Combine lambda with built-in conditions
|
|
262
|
-
@hook(Product, "after_update", condition=(
|
|
263
|
-
HasChanged("price") &
|
|
264
|
-
LambdaCondition(lambda instance: instance.price > 100)
|
|
265
|
-
))
|
|
266
|
-
def handle_expensive_price_changes(self, new_records, old_records):
|
|
267
|
-
"""Handle when expensive products have price changes"""
|
|
268
|
-
for new_product, old_product in zip(new_records, old_records):
|
|
269
|
-
print(f"Expensive product price changed: {new_product.name}")
|
|
270
|
-
|
|
271
|
-
# Complex combined conditions
|
|
272
|
-
@hook(Order, "after_update", condition=(
|
|
273
|
-
LambdaCondition(lambda instance: instance.status == 'completed') &
|
|
274
|
-
LambdaCondition(lambda instance, original: original and instance.total_amount > original.total_amount)
|
|
275
|
-
))
|
|
276
|
-
def handle_completed_orders_with_increased_amount(self, new_records, old_records):
|
|
277
|
-
"""Handle completed orders that had amount increases"""
|
|
278
|
-
for new_order, old_order in zip(new_records, old_records):
|
|
279
|
-
if old_order:
|
|
280
|
-
increase = new_order.total_amount - old_order.total_amount
|
|
281
|
-
print(f"Completed order with amount increase: {new_order.customer_name} +${increase}")
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### Custom Condition Classes
|
|
285
|
-
|
|
286
|
-
For reusable logic, you can create custom condition classes:
|
|
287
|
-
|
|
288
|
-
```python
|
|
289
|
-
from django_bulk_hooks.conditions import HookCondition
|
|
290
|
-
|
|
291
|
-
class IsPremiumProduct(HookCondition):
|
|
292
|
-
def check(self, instance, original_instance=None):
|
|
293
|
-
return (
|
|
294
|
-
instance.price > 200 and
|
|
295
|
-
instance.rating >= 4.0 and
|
|
296
|
-
instance.is_active
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
def get_required_fields(self):
|
|
300
|
-
return {'price', 'rating', 'is_active'}
|
|
301
|
-
|
|
302
|
-
class ProductHandler:
|
|
303
|
-
@hook(Product, "after_create", condition=IsPremiumProduct())
|
|
304
|
-
def handle_premium_products(self, new_records, old_records):
|
|
305
|
-
"""Handle premium products"""
|
|
306
|
-
for product in new_records:
|
|
307
|
-
print(f"Premium product: {product.name}")
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Lambda Conditions with Required Fields
|
|
311
|
-
|
|
312
|
-
For optimization, you can specify which fields your lambda condition depends on:
|
|
313
|
-
|
|
314
|
-
```python
|
|
315
|
-
class OptimizedProductHandler:
|
|
316
|
-
@hook(Product, "after_update", condition=LambdaCondition(
|
|
317
|
-
lambda instance: instance.price > 100 and instance.category == 'electronics',
|
|
318
|
-
required_fields={'price', 'category'}
|
|
319
|
-
))
|
|
320
|
-
def handle_expensive_electronics(self, new_records, old_records):
|
|
321
|
-
"""Handle expensive electronics products"""
|
|
322
|
-
for product in new_records:
|
|
323
|
-
print(f"Expensive electronics: {product.name}")
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Best Practices for Lambda Conditions
|
|
327
|
-
|
|
328
|
-
1. **Keep lambdas simple** - Complex logic should be moved to custom condition classes
|
|
329
|
-
2. **Handle None values** - Always check for None before performing operations
|
|
330
|
-
3. **Specify required fields** - This helps with query optimization
|
|
331
|
-
4. **Use descriptive names** - Make your lambda conditions self-documenting
|
|
332
|
-
5. **Test thoroughly** - Lambda conditions can be harder to debug than named functions
|
|
333
|
-
|
|
334
|
-
```python
|
|
335
|
-
# ✅ GOOD: Simple, clear lambda
|
|
336
|
-
condition = LambdaCondition(lambda instance: instance.price > 100)
|
|
337
|
-
|
|
338
|
-
# ✅ GOOD: Handles None values
|
|
339
|
-
condition = LambdaCondition(
|
|
340
|
-
lambda instance: instance.price is not None and instance.price > 100
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
# ❌ AVOID: Complex logic in lambda
|
|
344
|
-
condition = LambdaCondition(
|
|
345
|
-
lambda instance: (
|
|
346
|
-
instance.price > 100 and
|
|
347
|
-
instance.category in ['electronics', 'computers'] and
|
|
348
|
-
instance.stock_quantity > 0 and
|
|
349
|
-
instance.rating >= 4.0 and
|
|
350
|
-
instance.is_active and
|
|
351
|
-
instance.created_at > datetime.now() - timedelta(days=30)
|
|
352
|
-
)
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
# ✅ BETTER: Use custom condition class for complex logic
|
|
356
|
-
class IsRecentExpensiveElectronics(HookCondition):
|
|
357
|
-
def check(self, instance, original_instance=None):
|
|
358
|
-
return (
|
|
359
|
-
instance.price > 100 and
|
|
360
|
-
instance.category in ['electronics', 'computers'] and
|
|
361
|
-
instance.stock_quantity > 0 and
|
|
362
|
-
instance.rating >= 4.0 and
|
|
363
|
-
instance.is_active and
|
|
364
|
-
instance.created_at > datetime.now() - timedelta(days=30)
|
|
365
|
-
)
|
|
366
|
-
|
|
367
|
-
def get_required_fields(self):
|
|
368
|
-
return {'price', 'category', 'stock_quantity', 'rating', 'is_active', 'created_at'}
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
## 🔧 Best Practices for Related Objects
|
|
209
|
+
This approach ensures your hooks are robust and won't fail due to missing related objects, while also being efficient with database queries.
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
from django_bulk_hooks.conditions import (
|
|
2
|
-
ChangesTo,
|
|
3
|
-
HasChanged,
|
|
4
|
-
IsBlank,
|
|
5
|
-
IsEqual,
|
|
6
|
-
IsGreaterThan,
|
|
7
|
-
IsGreaterThanOrEqual,
|
|
8
|
-
IsLessThan,
|
|
9
|
-
IsLessThanOrEqual,
|
|
10
|
-
IsNotEqual,
|
|
11
|
-
LambdaCondition,
|
|
12
|
-
WasEqual,
|
|
13
|
-
is_field_set,
|
|
14
|
-
safe_get_related_attr,
|
|
15
|
-
safe_get_related_object,
|
|
16
|
-
)
|
|
17
1
|
from django_bulk_hooks.constants import (
|
|
18
2
|
AFTER_CREATE,
|
|
19
3
|
AFTER_DELETE,
|
|
@@ -25,10 +9,20 @@ from django_bulk_hooks.constants import (
|
|
|
25
9
|
VALIDATE_DELETE,
|
|
26
10
|
VALIDATE_UPDATE,
|
|
27
11
|
)
|
|
12
|
+
from django_bulk_hooks.conditions import (
|
|
13
|
+
ChangesTo,
|
|
14
|
+
HasChanged,
|
|
15
|
+
IsEqual,
|
|
16
|
+
IsNotEqual,
|
|
17
|
+
WasEqual,
|
|
18
|
+
safe_get_related_object,
|
|
19
|
+
safe_get_related_attr,
|
|
20
|
+
is_field_set,
|
|
21
|
+
)
|
|
28
22
|
from django_bulk_hooks.decorators import hook, select_related
|
|
29
|
-
from django_bulk_hooks.enums import Priority
|
|
30
23
|
from django_bulk_hooks.handler import HookHandler
|
|
31
24
|
from django_bulk_hooks.models import HookModelMixin
|
|
25
|
+
from django_bulk_hooks.enums import Priority
|
|
32
26
|
|
|
33
27
|
__all__ = [
|
|
34
28
|
"HookHandler",
|
|
@@ -53,10 +47,4 @@ __all__ = [
|
|
|
53
47
|
"IsEqual",
|
|
54
48
|
"IsNotEqual",
|
|
55
49
|
"WasEqual",
|
|
56
|
-
"IsBlank",
|
|
57
|
-
"IsGreaterThan",
|
|
58
|
-
"IsLessThan",
|
|
59
|
-
"IsGreaterThanOrEqual",
|
|
60
|
-
"IsLessThanOrEqual",
|
|
61
|
-
"LambdaCondition",
|
|
62
50
|
]
|