django-bulk-hooks 0.1.238__py3-none-any.whl → 0.1.240__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.

@@ -1,4 +1,4 @@
1
- from django_bulk_hooks.handler import Hook
2
- from django_bulk_hooks.manager import BulkHookManager
3
-
4
- __all__ = ["BulkHookManager", "Hook"]
1
+ from django_bulk_hooks.handler import Hook as HookClass
2
+ from django_bulk_hooks.manager import BulkHookManager
3
+
4
+ __all__ = ["BulkHookManager", "HookClass"]
@@ -2,6 +2,7 @@ import inspect
2
2
  from functools import wraps
3
3
 
4
4
  from django.core.exceptions import FieldDoesNotExist
5
+
5
6
  from django_bulk_hooks.enums import DEFAULT_PRIORITY
6
7
  from django_bulk_hooks.registry import register_hook
7
8
 
@@ -61,13 +62,35 @@ def select_related(*related_fields):
61
62
  continue
62
63
  # if any related field is not already cached on the instance,
63
64
  # mark it for fetching
64
- if any(field not in obj._state.fields_cache for field in related_fields):
65
+ if any(
66
+ field not in obj._state.fields_cache for field in related_fields
67
+ ):
65
68
  ids_to_fetch.append(obj.pk)
66
69
 
67
70
  fetched = {}
68
71
  if ids_to_fetch:
69
- # Use the base manager to avoid recursion
70
- fetched = model_cls._base_manager.select_related(*related_fields).in_bulk(ids_to_fetch)
72
+ # Validate fields before passing to select_related
73
+ validated_fields = []
74
+ for field in related_fields:
75
+ if "." in field:
76
+ raise ValueError(
77
+ f"@select_related does not support nested fields like '{field}'"
78
+ )
79
+ try:
80
+ f = model_cls._meta.get_field(field)
81
+ if not (
82
+ f.is_relation and not f.many_to_many and not f.one_to_many
83
+ ):
84
+ continue
85
+ validated_fields.append(field)
86
+ except FieldDoesNotExist:
87
+ continue
88
+
89
+ if validated_fields:
90
+ # Use the base manager to avoid recursion
91
+ fetched = model_cls._base_manager.select_related(
92
+ *validated_fields
93
+ ).in_bulk(ids_to_fetch)
71
94
 
72
95
  for obj in new_records:
73
96
  preloaded = fetched.get(obj.pk)
@@ -78,9 +101,8 @@ def select_related(*related_fields):
78
101
  # don't override values that were explicitly set or already loaded
79
102
  continue
80
103
  if "." in field:
81
- raise ValueError(
82
- f"@preload_related does not support nested fields like '{field}'"
83
- )
104
+ # This should have been caught earlier, but just in case
105
+ continue
84
106
 
85
107
  try:
86
108
  f = model_cls._meta.get_field(field)
@@ -108,30 +130,32 @@ def select_related(*related_fields):
108
130
  def bulk_hook(model_cls, event, when=None, priority=None):
109
131
  """
110
132
  Decorator to register a bulk hook for a model.
111
-
133
+
112
134
  Args:
113
135
  model_cls: The model class to hook into
114
136
  event: The event to hook into (e.g., BEFORE_UPDATE, AFTER_UPDATE)
115
137
  when: Optional condition for when the hook should run
116
138
  priority: Optional priority for hook execution order
117
139
  """
140
+
118
141
  def decorator(func):
119
142
  # Create a simple handler class for the function
120
143
  class FunctionHandler:
121
144
  def __init__(self):
122
145
  self.func = func
123
-
146
+
124
147
  def handle(self, new_instances, original_instances):
125
148
  return self.func(new_instances, original_instances)
126
-
149
+
127
150
  # Register the hook using the registry
128
151
  register_hook(
129
152
  model=model_cls,
130
153
  event=event,
131
154
  handler_cls=FunctionHandler,
132
- method_name='handle',
155
+ method_name="handle",
133
156
  condition=when,
134
157
  priority=priority or DEFAULT_PRIORITY,
135
158
  )
136
159
  return func
160
+
137
161
  return decorator
@@ -1,167 +1,188 @@
1
- import logging
2
- import threading
3
- from collections import deque
4
-
5
- from django.db import transaction
6
-
7
- from django_bulk_hooks.registry import get_hooks, register_hook
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- # Thread-local hook context and hook state
13
- class HookVars(threading.local):
14
- def __init__(self):
15
- self.new = None
16
- self.old = None
17
- self.event = None
18
- self.model = None
19
- self.depth = 0
20
-
21
-
22
- hook_vars = HookVars()
23
-
24
- # Hook queue per thread
25
- _hook_context = threading.local()
26
-
27
-
28
- def get_hook_queue():
29
- if not hasattr(_hook_context, "queue"):
30
- _hook_context.queue = deque()
31
- return _hook_context.queue
32
-
33
-
34
- class HookContextState:
35
- @property
36
- def is_before(self):
37
- return hook_vars.event.startswith("before_") if hook_vars.event else False
38
-
39
- @property
40
- def is_after(self):
41
- return hook_vars.event.startswith("after_") if hook_vars.event else False
42
-
43
- @property
44
- def is_create(self):
45
- return "create" in hook_vars.event if hook_vars.event else False
46
-
47
- @property
48
- def is_update(self):
49
- return "update" in hook_vars.event if hook_vars.event else False
50
-
51
- @property
52
- def new(self):
53
- return hook_vars.new
54
-
55
- @property
56
- def old(self):
57
- return hook_vars.old
58
-
59
- @property
60
- def model(self):
61
- return hook_vars.model
62
-
63
-
64
- Hook = HookContextState()
65
-
66
-
67
- class HookMeta(type):
68
- _registered = set()
69
-
70
- def __new__(mcs, name, bases, namespace):
71
- cls = super().__new__(mcs, name, bases, namespace)
72
- for method_name, method in namespace.items():
73
- if hasattr(method, "hooks_hooks"):
74
- for model_cls, event, condition, priority in method.hooks_hooks:
75
- key = (model_cls, event, cls, method_name)
76
- if key not in HookMeta._registered:
77
- register_hook(
78
- model=model_cls,
79
- event=event,
80
- handler_cls=cls,
81
- method_name=method_name,
82
- condition=condition,
83
- priority=priority,
84
- )
85
- HookMeta._registered.add(key)
86
- return cls
87
-
88
-
89
- class Hook(metaclass=HookMeta):
90
- @classmethod
91
- def handle(
92
- cls,
93
- event: str,
94
- model: type,
95
- *,
96
- new_records: list = None,
97
- old_records: list = None,
98
- **kwargs,
99
- ) -> None:
100
- queue = get_hook_queue()
101
- queue.append((cls, event, model, new_records, old_records, kwargs))
102
-
103
- if len(queue) > 1:
104
- return # nested call, will be processed by outermost
105
-
106
- # only outermost handle will process the queue
107
- while queue:
108
- cls_, event_, model_, new_, old_, kw_ = queue.popleft()
109
- cls_._process(event_, model_, new_, old_, **kw_)
110
-
111
- @classmethod
112
- def _process(
113
- cls,
114
- event,
115
- model,
116
- new_records,
117
- old_records,
118
- **kwargs,
119
- ):
120
- hook_vars.depth += 1
121
- hook_vars.new = new_records
122
- hook_vars.old = old_records
123
- hook_vars.event = event
124
- hook_vars.model = model
125
-
126
- hooks = sorted(get_hooks(model, event), key=lambda x: x[3])
127
-
128
- def _execute():
129
- new_local = new_records or []
130
- old_local = old_records or []
131
- if len(old_local) < len(new_local):
132
- old_local += [None] * (len(new_local) - len(old_local))
133
-
134
- for handler_cls, method_name, condition, priority in hooks:
135
- if condition is not None:
136
- checks = [
137
- condition.check(n, o) for n, o in zip(new_local, old_local)
138
- ]
139
- if not any(checks):
140
- continue
141
-
142
- handler = handler_cls()
143
- method = getattr(handler, method_name)
144
-
145
- try:
146
- method(
147
- new_records=new_local,
148
- old_records=old_local,
149
- **kwargs,
150
- )
151
- except Exception:
152
- logger.exception(
153
- "Error in hook %s.%s", handler_cls.__name__, method_name
154
- )
155
-
156
- conn = transaction.get_connection()
157
- try:
158
- if conn.in_atomic_block and event.startswith("after_"):
159
- transaction.on_commit(_execute)
160
- else:
161
- _execute()
162
- finally:
163
- hook_vars.new = None
164
- hook_vars.old = None
165
- hook_vars.event = None
166
- hook_vars.model = None
167
- hook_vars.depth -= 1
1
+ import logging
2
+ import threading
3
+ from collections import deque
4
+
5
+ from django.db import transaction
6
+
7
+ from django_bulk_hooks.registry import get_hooks, register_hook
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ # Thread-local hook context and hook state
13
+ class HookVars(threading.local):
14
+ def __init__(self):
15
+ self.new = None
16
+ self.old = None
17
+ self.event = None
18
+ self.model = None
19
+ self.depth = 0
20
+
21
+
22
+ hook_vars = HookVars()
23
+
24
+ # Hook queue per thread
25
+ _hook_context = threading.local()
26
+
27
+
28
+ def get_hook_queue():
29
+ if not hasattr(_hook_context, "queue"):
30
+ _hook_context.queue = deque()
31
+ return _hook_context.queue
32
+
33
+
34
+ class HookContextState:
35
+ @property
36
+ def is_before(self):
37
+ return hook_vars.event.startswith("before_") if hook_vars.event else False
38
+
39
+ @property
40
+ def is_after(self):
41
+ return hook_vars.event.startswith("after_") if hook_vars.event else False
42
+
43
+ @property
44
+ def is_create(self):
45
+ return "create" in hook_vars.event if hook_vars.event else False
46
+
47
+ @property
48
+ def is_update(self):
49
+ return "update" in hook_vars.event if hook_vars.event else False
50
+
51
+ @property
52
+ def new(self):
53
+ return hook_vars.new
54
+
55
+ @property
56
+ def old(self):
57
+ return hook_vars.old
58
+
59
+ @property
60
+ def model(self):
61
+ return hook_vars.model
62
+
63
+
64
+ HookContext = HookContextState()
65
+
66
+
67
+ class HookMeta(type):
68
+ _registered = set()
69
+
70
+ def __new__(mcs, name, bases, namespace):
71
+ cls = super().__new__(mcs, name, bases, namespace)
72
+ for method_name, method in namespace.items():
73
+ if hasattr(method, "hooks_hooks"):
74
+ for model_cls, event, condition, priority in method.hooks_hooks:
75
+ key = (model_cls, event, cls, method_name)
76
+ if key not in HookMeta._registered:
77
+ register_hook(
78
+ model=model_cls,
79
+ event=event,
80
+ handler_cls=cls,
81
+ method_name=method_name,
82
+ condition=condition,
83
+ priority=priority,
84
+ )
85
+ HookMeta._registered.add(key)
86
+ return cls
87
+
88
+
89
+ class Hook(metaclass=HookMeta):
90
+ @classmethod
91
+ def handle(
92
+ cls,
93
+ event: str,
94
+ model: type,
95
+ *,
96
+ new_records: list = None,
97
+ old_records: list = None,
98
+ **kwargs,
99
+ ) -> None:
100
+ queue = get_hook_queue()
101
+ queue.append((cls, event, model, new_records, old_records, kwargs))
102
+ logger.debug(f"Added item to queue: {event}, depth: {hook_vars.depth}")
103
+
104
+ # If we're already processing hooks (depth > 0), don't process the queue
105
+ # The outermost call will process the entire queue
106
+ if hook_vars.depth > 0:
107
+ logger.debug(f"Depth > 0, returning without processing queue")
108
+ return
109
+
110
+ # Process the entire queue
111
+ logger.debug(f"Processing queue with {len(queue)} items")
112
+ while queue:
113
+ item = queue.popleft()
114
+ if len(item) == 6:
115
+ cls_, event_, model_, new_, old_, kw_ = item
116
+ logger.debug(f"Processing queue item: {event_}")
117
+ # Call _process on the Hook class, not the calling class
118
+ Hook._process(event_, model_, new_, old_, **kw_)
119
+ else:
120
+ logger.warning(f"Invalid queue item format: {item}")
121
+ continue
122
+
123
+ @classmethod
124
+ def _process(
125
+ cls,
126
+ event,
127
+ model,
128
+ new_records,
129
+ old_records,
130
+ **kwargs,
131
+ ):
132
+ hook_vars.depth += 1
133
+ hook_vars.new = new_records
134
+ hook_vars.old = old_records
135
+ hook_vars.event = event
136
+ hook_vars.model = model
137
+
138
+ hooks = sorted(get_hooks(model, event), key=lambda x: x[3])
139
+ logger.debug(f"Found {len(hooks)} hooks for {event}")
140
+
141
+ def _execute():
142
+ logger.debug(f"Executing {len(hooks)} hooks for {event}")
143
+ new_local = new_records or []
144
+ old_local = old_records or []
145
+ if len(old_local) < len(new_local):
146
+ old_local += [None] * (len(new_local) - len(old_local))
147
+
148
+ for handler_cls, method_name, condition, priority in hooks:
149
+ logger.debug(f"Processing hook {handler_cls.__name__}.{method_name}")
150
+ if condition is not None:
151
+ checks = [
152
+ condition.check(n, o) for n, o in zip(new_local, old_local)
153
+ ]
154
+ if not any(checks):
155
+ logger.debug(f"Condition failed for {handler_cls.__name__}.{method_name}")
156
+ continue
157
+
158
+ handler = handler_cls()
159
+ method = getattr(handler, method_name)
160
+ logger.debug(f"Executing {handler_cls.__name__}.{method_name}")
161
+
162
+ try:
163
+ method(
164
+ new_records=new_local,
165
+ old_records=old_local,
166
+ **kwargs,
167
+ )
168
+ logger.debug(f"Successfully executed {handler_cls.__name__}.{method_name}")
169
+ except Exception:
170
+ logger.exception(
171
+ "Error in hook %s.%s", handler_cls.__name__, method_name
172
+ )
173
+
174
+ conn = transaction.get_connection()
175
+ logger.debug(f"Transaction in_atomic_block: {conn.in_atomic_block}, event: {event}")
176
+ try:
177
+ if conn.in_atomic_block and event.startswith("after_"):
178
+ logger.debug(f"Deferring {event} to on_commit")
179
+ transaction.on_commit(_execute)
180
+ else:
181
+ logger.debug(f"Executing {event} immediately")
182
+ _execute()
183
+ finally:
184
+ hook_vars.new = None
185
+ hook_vars.old = None
186
+ hook_vars.event = None
187
+ hook_vars.model = None
188
+ hook_vars.depth -= 1
@@ -55,6 +55,7 @@ class HookQuerySetMixin:
55
55
 
56
56
  @transaction.atomic
57
57
  def update(self, **kwargs):
58
+ logger.debug(f"Entering update method with {len(kwargs)} kwargs")
58
59
  instances = list(self)
59
60
  if not instances:
60
61
  return 0
@@ -70,10 +71,38 @@ class HookQuerySetMixin:
70
71
  originals = [original_map.get(obj.pk) for obj in instances]
71
72
 
72
73
  # Check if any of the update values are Subquery objects
73
- has_subquery = any(
74
- hasattr(value, "query") and hasattr(value, "resolve_expression")
75
- for value in kwargs.values()
76
- )
74
+ try:
75
+ from django.db.models import Subquery
76
+ logger.debug(f"Successfully imported Subquery from django.db.models")
77
+ except ImportError as e:
78
+ logger.error(f"Failed to import Subquery: {e}")
79
+ raise
80
+
81
+ logger.debug(f"Checking for Subquery objects in {len(kwargs)} kwargs")
82
+
83
+ subquery_detected = []
84
+ for key, value in kwargs.items():
85
+ is_subquery = isinstance(value, Subquery)
86
+ logger.debug(f"Key '{key}': type={type(value).__name__}, is_subquery={is_subquery}")
87
+ if is_subquery:
88
+ subquery_detected.append(key)
89
+
90
+ has_subquery = len(subquery_detected) > 0
91
+ logger.debug(f"Subquery detection result: {has_subquery}, detected keys: {subquery_detected}")
92
+
93
+ # Debug logging for Subquery detection
94
+ logger.debug(f"Update kwargs: {list(kwargs.keys())}")
95
+ logger.debug(f"Update kwargs types: {[(k, type(v).__name__) for k, v in kwargs.items()]}")
96
+
97
+ if has_subquery:
98
+ logger.debug(f"Detected Subquery in update: {[k for k, v in kwargs.items() if isinstance(v, Subquery)]}")
99
+ else:
100
+ # Check if we missed any Subquery objects
101
+ for k, v in kwargs.items():
102
+ if hasattr(v, 'query') and hasattr(v, 'resolve_expression'):
103
+ logger.warning(f"Potential Subquery-like object detected but not recognized: {k}={type(v).__name__}")
104
+ logger.warning(f"Object attributes: query={hasattr(v, 'query')}, resolve_expression={hasattr(v, 'resolve_expression')}")
105
+ logger.warning(f"Object dir: {[attr for attr in dir(v) if not attr.startswith('_')][:10]}")
77
106
 
78
107
  # Apply field updates to instances
79
108
  # If a per-object value map exists (from bulk_update), prefer it over kwargs
@@ -152,7 +181,49 @@ class HookQuerySetMixin:
152
181
 
153
182
  # Use Django's built-in update logic directly
154
183
  # Call the base QuerySet implementation to avoid recursion
155
- update_count = super().update(**kwargs)
184
+
185
+ # Additional safety check: ensure Subquery objects are properly handled
186
+ # This prevents the "cannot adapt type 'Subquery'" error
187
+ safe_kwargs = {}
188
+ logger.debug(f"Processing {len(kwargs)} kwargs for safety check")
189
+
190
+ for key, value in kwargs.items():
191
+ logger.debug(f"Processing key '{key}' with value type {type(value).__name__}")
192
+
193
+ if isinstance(value, Subquery):
194
+ logger.debug(f"Found Subquery for field {key}")
195
+ # Ensure Subquery has proper output_field
196
+ if not hasattr(value, 'output_field') or value.output_field is None:
197
+ logger.warning(f"Subquery for field {key} missing output_field, attempting to infer")
198
+ # Try to infer from the model field
199
+ try:
200
+ field = model_cls._meta.get_field(key)
201
+ logger.debug(f"Inferred field type: {type(field).__name__}")
202
+ value = value.resolve_expression(None, None)
203
+ value.output_field = field
204
+ logger.debug(f"Set output_field to {field}")
205
+ except Exception as e:
206
+ logger.error(f"Failed to infer output_field for Subquery on {key}: {e}")
207
+ raise
208
+ else:
209
+ logger.debug(f"Subquery for field {key} already has output_field: {value.output_field}")
210
+ safe_kwargs[key] = value
211
+ else:
212
+ logger.debug(f"Non-Subquery value for field {key}: {type(value).__name__}")
213
+ safe_kwargs[key] = value
214
+
215
+ logger.debug(f"Safe kwargs keys: {list(safe_kwargs.keys())}")
216
+ logger.debug(f"Safe kwargs types: {[(k, type(v).__name__) for k, v in safe_kwargs.items()]}")
217
+
218
+ logger.debug(f"Calling super().update() with {len(safe_kwargs)} kwargs")
219
+ try:
220
+ update_count = super().update(**safe_kwargs)
221
+ logger.debug(f"Super update successful, count: {update_count}")
222
+ except Exception as e:
223
+ logger.error(f"Super update failed: {e}")
224
+ logger.error(f"Exception type: {type(e).__name__}")
225
+ logger.error(f"Safe kwargs that caused failure: {safe_kwargs}")
226
+ raise
156
227
 
157
228
  # If we used Subquery objects, refresh the instances to get computed values
158
229
  if has_subquery and instances:
@@ -175,8 +246,15 @@ class HookQuerySetMixin:
175
246
  getattr(refreshed_instance, field.name),
176
247
  )
177
248
 
178
- # Run AFTER_UPDATE hooks only for standalone updates
179
- if not current_bypass_hooks:
249
+ # Run AFTER_UPDATE hooks for standalone updates or subquery operations
250
+ # For subquery operations, we need to run hooks even if we're in a bulk context
251
+ # because subqueries bypass the normal object-level update flow
252
+ should_run_hooks = (
253
+ not current_bypass_hooks or
254
+ has_subquery # Always run hooks for subquery operations
255
+ )
256
+
257
+ if should_run_hooks:
180
258
  logger.debug("update: running AFTER_UPDATE")
181
259
  engine.run(model_cls, AFTER_UPDATE, instances, originals, ctx=ctx)
182
260
  else:
@@ -837,6 +915,49 @@ class HookQuerySetMixin:
837
915
 
838
916
  return total_updated
839
917
 
918
+ @transaction.atomic
919
+ def bulk_delete(self, objs, bypass_hooks=False, bypass_validation=False, **kwargs):
920
+ """
921
+ Bulk delete objects in the database.
922
+ """
923
+ model_cls = self.model
924
+
925
+ if not objs:
926
+ return 0
927
+
928
+ if any(not isinstance(obj, model_cls) for obj in objs):
929
+ raise TypeError(
930
+ f"bulk_delete expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
931
+ )
932
+
933
+ logger.debug(
934
+ f"bulk_delete {model_cls.__name__} bypass_hooks={bypass_hooks} objs={len(objs)}"
935
+ )
936
+
937
+ # Fire hooks before DB ops
938
+ if not bypass_hooks:
939
+ ctx = HookContext(model_cls, bypass_hooks=False)
940
+ if not bypass_validation:
941
+ engine.run(model_cls, VALIDATE_DELETE, objs, ctx=ctx)
942
+ engine.run(model_cls, BEFORE_DELETE, objs, ctx=ctx)
943
+ else:
944
+ ctx = HookContext(model_cls, bypass_hooks=True)
945
+ logger.debug("bulk_delete bypassed hooks")
946
+
947
+ # Use Django's standard delete() method on the queryset
948
+ pks = [obj.pk for obj in objs if obj.pk is not None]
949
+ if pks:
950
+ # Use the base manager to avoid recursion
951
+ result = self.model._base_manager.filter(pk__in=pks).delete()[0]
952
+ else:
953
+ result = 0
954
+
955
+ # Fire AFTER_DELETE hooks
956
+ if not bypass_hooks:
957
+ engine.run(model_cls, AFTER_DELETE, objs, ctx=ctx)
958
+
959
+ return result
960
+
840
961
 
841
962
  class HookQuerySet(HookQuerySetMixin, models.QuerySet):
842
963
  """
@@ -15,7 +15,7 @@ def register_hook(
15
15
  key = (model, event)
16
16
  hooks = _hooks.setdefault(key, [])
17
17
  hooks.append((handler_cls, method_name, condition, priority))
18
- # keep sorted by priority
18
+ # Sort by priority (lower values first)
19
19
  hooks.sort(key=lambda x: x[3])
20
20
  logger.debug(f"Registered {handler_cls.__name__}.{method_name} for {model.__name__}.{event}")
21
21
 
@@ -29,6 +29,13 @@ def get_hooks(model, event):
29
29
  return hooks
30
30
 
31
31
 
32
+ def clear_hooks():
33
+ """Clear all registered hooks. Useful for testing."""
34
+ global _hooks
35
+ _hooks.clear()
36
+ logger.debug("Cleared all registered hooks")
37
+
38
+
32
39
  def list_all_hooks():
33
40
  """Debug function to list all registered hooks"""
34
41
  return _hooks
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.238
3
+ Version: 0.1.240
4
4
  Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
5
- Home-page: https://github.com/AugendLimited/django-bulk-hooks
6
5
  License: MIT
7
6
  Keywords: django,bulk,hooks
8
7
  Author: Konrad Beck
@@ -14,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: 3.13
16
15
  Requires-Dist: Django (>=4.0)
16
+ Project-URL: Homepage, https://github.com/AugendLimited/django-bulk-hooks
17
17
  Project-URL: Repository, https://github.com/AugendLimited/django-bulk-hooks
18
18
  Description-Content-Type: text/markdown
19
19
 
@@ -0,0 +1,17 @@
1
+ django_bulk_hooks/__init__.py,sha256=hsbKduccFEcsV4KIw8CbxCUDOtLZwToCc-XP3sqNy-8,154
2
+ django_bulk_hooks/conditions.py,sha256=V_f3Di2uCVUjoyfiU4BQCHmI4uUIRSRroApDcXlvnso,6349
3
+ django_bulk_hooks/constants.py,sha256=3x1H1fSUUNo0DZONN7GUVDuySZctTR-jtByBHmAIX5w,303
4
+ django_bulk_hooks/context.py,sha256=jlLsqGZbj__J0-iBUp1D6jTrlDEiX3qIo0XlywW4D9I,2244
5
+ django_bulk_hooks/decorators.py,sha256=tBHjegw1qZgpJkKng1q7gMpd2UpSY2nH9f7oD1cWhr0,5735
6
+ django_bulk_hooks/engine.py,sha256=t_kvgex6_iZEFc5LK-srBTZPe-1bdlYdip5LfWOc6lc,2411
7
+ django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
8
+ django_bulk_hooks/handler.py,sha256=Bx-W6yyiciKMyy-BRxUt3CmRPCrX9_LhQgU-5LaJTjg,6019
9
+ django_bulk_hooks/manager.py,sha256=nfWiwU5-yAoxdnQsUMohxtyCpkV0MBv6X3wmipr9eQY,3697
10
+ django_bulk_hooks/models.py,sha256=exnXYVKEVbYAXhChCP8VdWTnKCnm9DiTcokEIBee1I0,4350
11
+ django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
12
+ django_bulk_hooks/queryset.py,sha256=bi8jE8yvl2ih0M3LbzW8TZvl7JFKbgysILRXIpci6KM,42055
13
+ django_bulk_hooks/registry.py,sha256=GRUTGVQEO2sdkC9OaZ9Q3U7mM-3Ix83uTyvrlTtpatw,1317
14
+ django_bulk_hooks-0.1.240.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
+ django_bulk_hooks-0.1.240.dist-info/METADATA,sha256=hkqwdtVcH2Te0py2ryH-RXgAJpX93e0IDFteueUBYtQ,9061
16
+ django_bulk_hooks-0.1.240.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
+ django_bulk_hooks-0.1.240.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.1
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,17 +0,0 @@
1
- django_bulk_hooks/__init__.py,sha256=uUgpnb9AWjIAcWNpCMqBcOewSnpJjJYH6cjPbQkzoNU,140
2
- django_bulk_hooks/conditions.py,sha256=V_f3Di2uCVUjoyfiU4BQCHmI4uUIRSRroApDcXlvnso,6349
3
- django_bulk_hooks/constants.py,sha256=3x1H1fSUUNo0DZONN7GUVDuySZctTR-jtByBHmAIX5w,303
4
- django_bulk_hooks/context.py,sha256=jlLsqGZbj__J0-iBUp1D6jTrlDEiX3qIo0XlywW4D9I,2244
5
- django_bulk_hooks/decorators.py,sha256=WD7Jn7QAvY8F4wOsYlIpjoM9-FdHXSKB7hH9ot-lkYQ,4896
6
- django_bulk_hooks/engine.py,sha256=t_kvgex6_iZEFc5LK-srBTZPe-1bdlYdip5LfWOc6lc,2411
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=nfWiwU5-yAoxdnQsUMohxtyCpkV0MBv6X3wmipr9eQY,3697
10
- django_bulk_hooks/models.py,sha256=exnXYVKEVbYAXhChCP8VdWTnKCnm9DiTcokEIBee1I0,4350
11
- django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
12
- django_bulk_hooks/queryset.py,sha256=OCKx6sXV1LG-aPOJ4gISjnn4zZzWE-R_NMGbzZF91HY,36156
13
- django_bulk_hooks/registry.py,sha256=8UuhniiH5ChSeOKV1UUbqTEiIu25bZXvcHmkaRbxmME,1131
14
- django_bulk_hooks-0.1.238.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.238.dist-info/METADATA,sha256=DHtPWOMH6HjrqO3jx7Ez1JegBVENAxPJISuexntKE-U,9049
16
- django_bulk_hooks-0.1.238.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
- django_bulk_hooks-0.1.238.dist-info/RECORD,,