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

@@ -5,8 +5,9 @@ These are pure data structures returned by MTIHandler to be executed by BulkExec
5
5
  This separates planning (logic) from execution (database operations).
6
6
  """
7
7
 
8
- from dataclasses import dataclass, field
9
- from typing import Dict, List, Any
8
+ from dataclasses import dataclass
9
+ from dataclasses import field
10
+ from typing import Any
10
11
 
11
12
 
12
13
  @dataclass
@@ -23,11 +24,11 @@ class ParentLevel:
23
24
  update_fields: Fields to update on conflict (if update_conflicts=True)
24
25
  """
25
26
  model_class: Any
26
- objects: List[Any]
27
- original_object_map: Dict[int, int] = field(default_factory=dict)
27
+ objects: list[Any]
28
+ original_object_map: dict[int, int] = field(default_factory=dict)
28
29
  update_conflicts: bool = False
29
- unique_fields: List[str] = field(default_factory=list)
30
- update_fields: List[str] = field(default_factory=list)
30
+ unique_fields: list[str] = field(default_factory=list)
31
+ update_fields: list[str] = field(default_factory=list)
31
32
 
32
33
 
33
34
  @dataclass
@@ -50,16 +51,16 @@ class MTICreatePlan:
50
51
  unique_fields: Fields used for conflict detection
51
52
  update_fields: Fields to update on conflict
52
53
  """
53
- inheritance_chain: List[Any]
54
- parent_levels: List[ParentLevel]
55
- child_objects: List[Any]
54
+ inheritance_chain: list[Any]
55
+ parent_levels: list[ParentLevel]
56
+ child_objects: list[Any]
56
57
  child_model: Any
57
- original_objects: List[Any]
58
+ original_objects: list[Any]
58
59
  batch_size: int = None
59
60
  existing_record_ids: set = field(default_factory=set)
60
61
  update_conflicts: bool = False
61
- unique_fields: List[str] = field(default_factory=list)
62
- update_fields: List[str] = field(default_factory=list)
62
+ unique_fields: list[str] = field(default_factory=list)
63
+ update_fields: list[str] = field(default_factory=list)
63
64
 
64
65
 
65
66
  @dataclass
@@ -73,7 +74,7 @@ class ModelFieldGroup:
73
74
  filter_field: Field to use for filtering (e.g., 'pk' or parent link attname)
74
75
  """
75
76
  model_class: Any
76
- fields: List[str]
77
+ fields: list[str]
77
78
  filter_field: str = "pk"
78
79
 
79
80
 
@@ -88,8 +89,8 @@ class MTIUpdatePlan:
88
89
  objects: Objects to update
89
90
  batch_size: Batch size for operations
90
91
  """
91
- inheritance_chain: List[Any]
92
- field_groups: List[ModelFieldGroup]
93
- objects: List[Any]
92
+ inheritance_chain: list[Any]
93
+ field_groups: list[ModelFieldGroup]
94
+ objects: list[Any]
94
95
  batch_size: int = None
95
96
 
@@ -8,6 +8,7 @@ Separates data access concerns from business logic.
8
8
  """
9
9
 
10
10
  import logging
11
+
11
12
  from django.db.models import Q
12
13
 
13
14
  logger = logging.getLogger(__name__)
@@ -48,11 +49,11 @@ class RecordClassifier:
48
49
  """
49
50
  if not unique_fields or not objs:
50
51
  return set(), {}
51
-
52
+
52
53
  # Build a query to find existing records
53
54
  queries = []
54
55
  obj_to_unique_values = {}
55
-
56
+
56
57
  for obj in objs:
57
58
  # Build lookup dict for this object's unique fields
58
59
  lookup = {}
@@ -67,36 +68,36 @@ class RecordClassifier:
67
68
  if lookup:
68
69
  queries.append(Q(**lookup))
69
70
  obj_to_unique_values[id(obj)] = tuple(lookup.values())
70
-
71
+
71
72
  if not queries:
72
73
  return set(), {}
73
-
74
+
74
75
  # Query for existing records
75
76
  combined_query = queries[0]
76
77
  for q in queries[1:]:
77
78
  combined_query |= q
78
-
79
+
79
80
  existing_records = list(
80
- self.model_cls.objects.filter(combined_query).values('pk', *unique_fields)
81
+ self.model_cls.objects.filter(combined_query).values("pk", *unique_fields),
81
82
  )
82
-
83
+
83
84
  # Map existing records back to original objects
84
85
  existing_record_ids = set()
85
86
  existing_pks_map = {}
86
-
87
+
87
88
  for record in existing_records:
88
89
  record_values = tuple(record[field] for field in unique_fields)
89
90
  # Find which object(s) match these values
90
91
  for obj_id, obj_values in obj_to_unique_values.items():
91
92
  if obj_values == record_values:
92
93
  existing_record_ids.add(obj_id)
93
- existing_pks_map[obj_id] = record['pk']
94
-
94
+ existing_pks_map[obj_id] = record["pk"]
95
+
95
96
  logger.info(
96
97
  f"Classified {len(existing_record_ids)} existing and "
97
- f"{len(objs) - len(existing_record_ids)} new records for upsert"
98
+ f"{len(objs) - len(existing_record_ids)} new records for upsert",
98
99
  )
99
-
100
+
100
101
  return existing_record_ids, existing_pks_map
101
102
 
102
103
  def fetch_by_pks(self, pks, select_related=None, prefetch_related=None):
@@ -113,15 +114,15 @@ class RecordClassifier:
113
114
  """
114
115
  if not pks:
115
116
  return {}
116
-
117
+
117
118
  queryset = self.model_cls._base_manager.filter(pk__in=pks)
118
-
119
+
119
120
  if select_related:
120
121
  queryset = queryset.select_related(*select_related)
121
-
122
+
122
123
  if prefetch_related:
123
124
  queryset = queryset.prefetch_related(*prefetch_related)
124
-
125
+
125
126
  return {obj.pk: obj for obj in queryset}
126
127
 
127
128
  def fetch_by_unique_constraint(self, field_values_map):
@@ -141,7 +142,7 @@ class RecordClassifier:
141
142
  except self.model_cls.MultipleObjectsReturned:
142
143
  logger.warning(
143
144
  f"Multiple {self.model_cls.__name__} records found for "
144
- f"unique constraint {field_values_map}"
145
+ f"unique constraint {field_values_map}",
145
146
  )
146
147
  return self.model_cls.objects.filter(**field_values_map).first()
147
148
 
@@ -157,11 +158,11 @@ class RecordClassifier:
157
158
  """
158
159
  if not pks:
159
160
  return set()
160
-
161
+
161
162
  existing_pks = self.model_cls.objects.filter(
162
- pk__in=pks
163
- ).values_list('pk', flat=True)
164
-
163
+ pk__in=pks,
164
+ ).values_list("pk", flat=True)
165
+
165
166
  return set(existing_pks)
166
167
 
167
168
  def count_by_unique_fields(self, objs, unique_fields):
@@ -7,7 +7,9 @@ complex coordination required for bulk operations with hooks.
7
7
  """
8
8
 
9
9
  import logging
10
- from django.db import models, transaction
10
+
11
+ from django.db import models
12
+ from django.db import transaction
11
13
 
12
14
  logger = logging.getLogger(__name__)
13
15
 
@@ -98,7 +100,7 @@ class HookQuerySet(models.QuerySet):
98
100
  fields = self.coordinator.analyzer.detect_changed_fields(objs)
99
101
  if not fields:
100
102
  logger.debug(
101
- f"bulk_update: No fields changed for {len(objs)} {self.model.__name__} objects"
103
+ f"bulk_update: No fields changed for {len(objs)} {self.model.__name__} objects",
102
104
  )
103
105
  return 0
104
106
 
@@ -133,7 +135,7 @@ class HookQuerySet(models.QuerySet):
133
135
 
134
136
  @transaction.atomic
135
137
  def bulk_delete(
136
- self, objs, bypass_hooks=False, bypass_validation=False, **kwargs
138
+ self, objs, bypass_hooks=False, bypass_validation=False, **kwargs,
137
139
  ):
138
140
  """
139
141
  Delete multiple objects with hook support.
@@ -8,14 +8,13 @@ deterministic priority ordering.
8
8
  import logging
9
9
  import threading
10
10
  from collections.abc import Callable
11
- from typing import Dict, List, Optional, Tuple, Type, Union
12
11
 
13
12
  from django_bulk_hooks.enums import Priority
14
13
 
15
14
  logger = logging.getLogger(__name__)
16
15
 
17
16
  # Type alias for hook info tuple
18
- HookInfo = Tuple[Type, str, Optional[Callable], int]
17
+ HookInfo = tuple[type, str, Callable | None, int]
19
18
 
20
19
 
21
20
  class HookRegistry:
@@ -30,17 +29,17 @@ class HookRegistry:
30
29
 
31
30
  def __init__(self):
32
31
  """Initialize an empty registry with thread-safe storage."""
33
- self._hooks: Dict[Tuple[Type, str], List[HookInfo]] = {}
32
+ self._hooks: dict[tuple[type, str], list[HookInfo]] = {}
34
33
  self._lock = threading.RLock()
35
34
 
36
35
  def register(
37
36
  self,
38
- model: Type,
37
+ model: type,
39
38
  event: str,
40
- handler_cls: Type,
39
+ handler_cls: type,
41
40
  method_name: str,
42
- condition: Optional[Callable],
43
- priority: Union[int, Priority],
41
+ condition: Callable | None,
42
+ priority: int | Priority,
44
43
  ) -> None:
45
44
  """
46
45
  Register a hook handler for a model and event.
@@ -64,16 +63,14 @@ class HookRegistry:
64
63
  # Sort by priority (lower values first)
65
64
  hooks.sort(key=lambda x: x[3])
66
65
  logger.debug(
67
- f"Registered {handler_cls.__name__}.{method_name} "
68
- f"for {model.__name__}.{event} (priority={priority})"
66
+ f"Registered {handler_cls.__name__}.{method_name} for {model.__name__}.{event} (priority={priority})",
69
67
  )
70
68
  else:
71
69
  logger.debug(
72
- f"Hook {handler_cls.__name__}.{method_name} "
73
- f"already registered for {model.__name__}.{event}"
70
+ f"Hook {handler_cls.__name__}.{method_name} already registered for {model.__name__}.{event}",
74
71
  )
75
72
 
76
- def get_hooks(self, model: Type, event: str) -> List[HookInfo]:
73
+ def get_hooks(self, model: type, event: str) -> list[HookInfo]:
77
74
  """
78
75
  Get all hooks for a model and event.
79
76
 
@@ -97,13 +94,17 @@ class HookRegistry:
97
94
  "before_create",
98
95
  ]:
99
96
  logger.debug(
100
- f"get_hooks {model.__name__}.{event} found {len(hooks)} hooks"
97
+ f"get_hooks {model.__name__}.{event} found {len(hooks)} hooks",
101
98
  )
102
99
 
103
100
  return hooks
104
101
 
105
102
  def unregister(
106
- self, model: Type, event: str, handler_cls: Type, method_name: str
103
+ self,
104
+ model: type,
105
+ event: str,
106
+ handler_cls: type,
107
+ method_name: str,
107
108
  ) -> None:
108
109
  """
109
110
  Unregister a specific hook handler.
@@ -124,9 +125,7 @@ class HookRegistry:
124
125
  hooks = self._hooks[key]
125
126
  # Filter out the specific hook
126
127
  self._hooks[key] = [
127
- (h_cls, m_name, cond, pri)
128
- for h_cls, m_name, cond, pri in hooks
129
- if not (h_cls == handler_cls and m_name == method_name)
128
+ (h_cls, m_name, cond, pri) for h_cls, m_name, cond, pri in hooks if not (h_cls == handler_cls and m_name == method_name)
130
129
  ]
131
130
 
132
131
  # Clean up empty hook lists
@@ -134,8 +133,7 @@ class HookRegistry:
134
133
  del self._hooks[key]
135
134
 
136
135
  logger.debug(
137
- f"Unregistered {handler_cls.__name__}.{method_name} "
138
- f"for {model.__name__}.{event}"
136
+ f"Unregistered {handler_cls.__name__}.{method_name} for {model.__name__}.{event}",
139
137
  )
140
138
 
141
139
  def clear(self) -> None:
@@ -155,7 +153,7 @@ class HookRegistry:
155
153
 
156
154
  logger.debug("Cleared all registered hooks")
157
155
 
158
- def list_all(self) -> Dict[Tuple[Type, str], List[HookInfo]]:
156
+ def list_all(self) -> dict[tuple[type, str], list[HookInfo]]:
159
157
  """
160
158
  Get all registered hooks for debugging.
161
159
 
@@ -166,17 +164,19 @@ class HookRegistry:
166
164
  return dict(self._hooks)
167
165
 
168
166
  @property
169
- def hooks(self) -> Dict[Tuple[Type, str], List[HookInfo]]:
167
+ def hooks(self) -> dict[tuple[type, str], list[HookInfo]]:
170
168
  """
171
169
  Expose internal hooks dictionary for testing purposes.
172
-
170
+
173
171
  This property provides direct access to the internal hooks storage
174
172
  to allow tests to clear the registry state between test runs.
175
173
  """
176
174
  return self._hooks
177
175
 
178
176
  def count_hooks(
179
- self, model: Optional[Type] = None, event: Optional[str] = None
177
+ self,
178
+ model: type | None = None,
179
+ event: str | None = None,
180
180
  ) -> int:
181
181
  """
182
182
  Count registered hooks, optionally filtered by model and/or event.
@@ -192,27 +192,19 @@ class HookRegistry:
192
192
  if model is None and event is None:
193
193
  # Count all hooks
194
194
  return sum(len(hooks) for hooks in self._hooks.values())
195
- elif model is not None and event is not None:
195
+ if model is not None and event is not None:
196
196
  # Count hooks for specific model and event
197
197
  return len(self._hooks.get((model, event), []))
198
- elif model is not None:
198
+ if model is not None:
199
199
  # Count all hooks for a model
200
- return sum(
201
- len(hooks)
202
- for (m, _), hooks in self._hooks.items()
203
- if m == model
204
- )
205
- else: # event is not None
206
- # Count all hooks for an event
207
- return sum(
208
- len(hooks)
209
- for (_, e), hooks in self._hooks.items()
210
- if e == event
211
- )
200
+ return sum(len(hooks) for (m, _), hooks in self._hooks.items() if m == model)
201
+ # event is not None
202
+ # Count all hooks for an event
203
+ return sum(len(hooks) for (_, e), hooks in self._hooks.items() if e == event)
212
204
 
213
205
 
214
206
  # Global singleton registry
215
- _registry: Optional[HookRegistry] = None
207
+ _registry: HookRegistry | None = None
216
208
  _registry_lock = threading.Lock()
217
209
 
218
210
 
@@ -239,12 +231,12 @@ def get_registry() -> HookRegistry:
239
231
 
240
232
  # Backward-compatible module-level functions
241
233
  def register_hook(
242
- model: Type,
234
+ model: type,
243
235
  event: str,
244
- handler_cls: Type,
236
+ handler_cls: type,
245
237
  method_name: str,
246
- condition: Optional[Callable],
247
- priority: Union[int, Priority],
238
+ condition: Callable | None,
239
+ priority: int | Priority,
248
240
  ) -> None:
249
241
  """
250
242
  Register a hook handler (backward-compatible function).
@@ -255,7 +247,7 @@ def register_hook(
255
247
  registry.register(model, event, handler_cls, method_name, condition, priority)
256
248
 
257
249
 
258
- def get_hooks(model: Type, event: str) -> List[HookInfo]:
250
+ def get_hooks(model: type, event: str) -> list[HookInfo]:
259
251
  """
260
252
  Get hooks for a model and event (backward-compatible function).
261
253
 
@@ -266,7 +258,10 @@ def get_hooks(model: Type, event: str) -> List[HookInfo]:
266
258
 
267
259
 
268
260
  def unregister_hook(
269
- model: Type, event: str, handler_cls: Type, method_name: str
261
+ model: type,
262
+ event: str,
263
+ handler_cls: type,
264
+ method_name: str,
270
265
  ) -> None:
271
266
  """
272
267
  Unregister a hook handler (backward-compatible function).
@@ -288,7 +283,7 @@ def clear_hooks() -> None:
288
283
  registry.clear()
289
284
 
290
285
 
291
- def list_all_hooks() -> Dict[Tuple[Type, str], List[HookInfo]]:
286
+ def list_all_hooks() -> dict[tuple[type, str], list[HookInfo]]:
292
287
  """
293
288
  List all registered hooks (backward-compatible function).
294
289
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.2.16
3
+ Version: 0.2.19
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
@@ -0,0 +1,26 @@
1
+ django_bulk_hooks/__init__.py,sha256=ujBvX-GY4Pg4ACBh7RXm_MOmi2eAowf5s7pG2SXWdpo,2276
2
+ django_bulk_hooks/changeset.py,sha256=wU6wckJEQ_hG5UZq_9g_kWODoKwEj99JrtacVUQ9BxA,7445
3
+ django_bulk_hooks/conditions.py,sha256=v2DMFmWI7bppBQw5qdbO5CmQRN_QtUwnBjcyKBJLLbw,8030
4
+ django_bulk_hooks/constants.py,sha256=PxpEETaO6gdENcTPoXS586lerGKVP3nmjpDvOkmhYxI,509
5
+ django_bulk_hooks/context.py,sha256=mqaC5-yESDTA5ruI7fuXlt8qSgKuOFp0mjq7h1-4HdQ,1926
6
+ django_bulk_hooks/decorators.py,sha256=P7cvzFgORJRW-YQHNAxNXqQOP9OywBmA7Rz9kiJoxUk,12237
7
+ django_bulk_hooks/dispatcher.py,sha256=fOWh1gtukdegMgku7qdc-Hix6G6ZCKMetONFiNKIBWI,8842
8
+ django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
9
+ django_bulk_hooks/factory.py,sha256=OgrOmLbIzhrSKTDx06oGMAVsEb0NoVOmW5IdLsMz_Qs,19938
10
+ django_bulk_hooks/handler.py,sha256=i0M4mdx3vgXIb8mA1S5ZBW_8ezECd8yTZaj9QNUm8P8,4738
11
+ django_bulk_hooks/helpers.py,sha256=Nw8eXryLUUquW7AgiuKp0PQT3Pq6HAHsdP-xAtqhmjA,3216
12
+ django_bulk_hooks/manager.py,sha256=3mFzB0ZzHHeXWdKGObZD_H0NlskHJc8uYBF69KKdAXU,4068
13
+ django_bulk_hooks/models.py,sha256=4Vvi2LiGP0g4j08a5liqBROfsO8Wd_ermBoyjKwfrPU,2512
14
+ django_bulk_hooks/operations/__init__.py,sha256=BtJYjmRhe_sScivLsniDaZmBkm0ZLvcmzXFKL7QY2Xg,550
15
+ django_bulk_hooks/operations/analyzer.py,sha256=8xNdsoeOCHsIIP7ztIuFHDewBR2dDSS6auOrSU8zmqE,9007
16
+ django_bulk_hooks/operations/bulk_executor.py,sha256=QmfcEUJAVHaMPzYe4yDwQ0i-9FvxDaTWy17mcmn2-Yw,22371
17
+ django_bulk_hooks/operations/coordinator.py,sha256=iSi-x3fq6_3UygWGeQY8Fq_gZWIqq_8N7WddPhVeygk,23526
18
+ django_bulk_hooks/operations/mti_handler.py,sha256=G-pxkzIqHqXGshRGksqmsN1J3rlzePUZrSv4wm7D3cQ,19162
19
+ django_bulk_hooks/operations/mti_plans.py,sha256=YP7LcV9Z8UqNS_x74OswF9_5swqruRTdAu6z-J_R6C0,3377
20
+ django_bulk_hooks/operations/record_classifier.py,sha256=KzUoAhfoqzFVrOabNZAby9Akb54h-fAQZmb8O-fIx_0,6221
21
+ django_bulk_hooks/queryset.py,sha256=rvJgQLwtSJztwc68nkJ6xwCsnbXQvkvS6_dbGGj8TFo,5886
22
+ django_bulk_hooks/registry.py,sha256=QyeA2OqNdMAMaLjFU9UF0YGhKiPKbZkmFQpLgof7uNs,9038
23
+ django_bulk_hooks-0.2.19.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
24
+ django_bulk_hooks-0.2.19.dist-info/METADATA,sha256=P0V_xhydlyp6toChYm6BHfAXAfcxmhIAVZX7jmDRMJ4,9265
25
+ django_bulk_hooks-0.2.19.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
26
+ django_bulk_hooks-0.2.19.dist-info/RECORD,,
@@ -1,26 +0,0 @@
1
- django_bulk_hooks/__init__.py,sha256=4QlWY5rqR9o2ddrNB-ypM4s1GtNJ1ZvL2ABhybaPpio,1823
2
- django_bulk_hooks/changeset.py,sha256=WALeiWDcjOBNdCKeidVKOPKAySKj9ZOvUJ-kWaVZYhM,7444
3
- django_bulk_hooks/conditions.py,sha256=qtGjToKXC8FPUPK31Mib-GMzc9GSdrH90M2pT3CIsh8,8111
4
- django_bulk_hooks/constants.py,sha256=PxpEETaO6gdENcTPoXS586lerGKVP3nmjpDvOkmhYxI,509
5
- django_bulk_hooks/context.py,sha256=mqaC5-yESDTA5ruI7fuXlt8qSgKuOFp0mjq7h1-4HdQ,1926
6
- django_bulk_hooks/decorators.py,sha256=uZhMGbFUxMz8fNnWBltB1D5Ez1d6vRNvoSFzqOTdGDo,12331
7
- django_bulk_hooks/dispatcher.py,sha256=ExEtApzwvprWIlbZifPUY17hJHeRUmeHe59BikDgs_g,8872
8
- django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
9
- django_bulk_hooks/factory.py,sha256=JmjQiJPfAnytXrO6r6qOadX5yX0-sfpbZ9V8nwX3MAg,20013
10
- django_bulk_hooks/handler.py,sha256=2-k0GPWGSQ6acfvV0qJgDH8aa0z51DqdpX5vSJ6Uawk,4759
11
- django_bulk_hooks/helpers.py,sha256=Yopvl588VbKOi2kHEsQcEcI5jw5jiNA2MuF6Ce1VP0c,3174
12
- django_bulk_hooks/manager.py,sha256=3mFzB0ZzHHeXWdKGObZD_H0NlskHJc8uYBF69KKdAXU,4068
13
- django_bulk_hooks/models.py,sha256=62tn5wL55EjJVOsZofMluhEJB8bH7CzBvH0vd214_RY,2570
14
- django_bulk_hooks/operations/__init__.py,sha256=5L5NnwiFw8Yn5WO6-38eGdCYBkA0URpwyDcAdeYfc5w,550
15
- django_bulk_hooks/operations/analyzer.py,sha256=s6FM53ho1raPdKU-VjjW0SWymXyrJe0I_Wu8XsXFdSY,9065
16
- django_bulk_hooks/operations/bulk_executor.py,sha256=MqfcwFNvAxpe1pfFfPEq70z8Ft1IxW1fmieDHeWbbbI,22767
17
- django_bulk_hooks/operations/coordinator.py,sha256=T9Y1Y5BAichETCG23mwC0eQ7VdZde-kUBKWKJVk3L1M,23717
18
- django_bulk_hooks/operations/mti_handler.py,sha256=y7tHDe2sPVg4bU0y4ITguMUdERwYXvfvjgSD-uvDBEM,19591
19
- django_bulk_hooks/operations/mti_plans.py,sha256=W2NjG0W9alY2ZxplzHgQv3TvWWmam-8pZuI0YU7HUqA,3365
20
- django_bulk_hooks/operations/record_classifier.py,sha256=ny_9Z0ATSX-X6I51ufuQUzgGKM0j3IcPs9SMOcgy5oc,6335
21
- django_bulk_hooks/queryset.py,sha256=ody4MXrRREL27Ts2ey1UpS0tb5Dxnw-6kN3unxPQ3zY,5860
22
- django_bulk_hooks/registry.py,sha256=fYNY4UzM4evNVozndeVeyD-yL1h0Lf8iykqJ3WqnMc8,9349
23
- django_bulk_hooks-0.2.16.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
24
- django_bulk_hooks-0.2.16.dist-info/METADATA,sha256=oDkpbXEOpjzhwtAlHboZDSSi_E-HrZ95D6_IoW85ufE,9265
25
- django_bulk_hooks-0.2.16.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
26
- django_bulk_hooks-0.2.16.dist-info/RECORD,,