django-bulk-hooks 0.2.62__tar.gz → 0.2.63__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.2.62 → django_bulk_hooks-0.2.63}/PKG-INFO +1 -1
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/dispatcher.py +95 -1
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/pyproject.toml +1 -1
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/LICENSE +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/README.md +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/changeset.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/conditions.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/context.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/factory.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/helpers.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/manager.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/__init__.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/analyzer.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/bulk_executor.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/coordinator.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/field_utils.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/mti_handler.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/mti_plans.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/record_classifier.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/queryset.py +0 -0
- {django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/registry.py +0 -0
|
@@ -122,7 +122,13 @@ class HookDispatcher:
|
|
|
122
122
|
condition: Optional condition to filter records
|
|
123
123
|
changeset: ChangeSet with all record changes
|
|
124
124
|
"""
|
|
125
|
-
#
|
|
125
|
+
# NEW: Preload relationships needed for condition evaluation
|
|
126
|
+
if condition:
|
|
127
|
+
condition_relationships = self._extract_condition_relationships(condition, changeset.model_cls)
|
|
128
|
+
if condition_relationships:
|
|
129
|
+
self._preload_condition_relationships(changeset, condition_relationships)
|
|
130
|
+
|
|
131
|
+
# Filter records based on condition (now safe - relationships are preloaded)
|
|
126
132
|
if condition:
|
|
127
133
|
filtered_changes = [change for change in changeset.changes if condition.check(change.new_record, change.old_record)]
|
|
128
134
|
|
|
@@ -204,6 +210,94 @@ class HookDispatcher:
|
|
|
204
210
|
)
|
|
205
211
|
raise
|
|
206
212
|
|
|
213
|
+
def _extract_condition_relationships(self, condition, model_cls):
|
|
214
|
+
"""
|
|
215
|
+
Extract relationship paths that a condition might access.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
condition: HookCondition instance
|
|
219
|
+
model_cls: The model class
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
set: Set of relationship field names to preload
|
|
223
|
+
"""
|
|
224
|
+
relationships = set()
|
|
225
|
+
|
|
226
|
+
# Guard against Mock objects and non-condition objects
|
|
227
|
+
if not hasattr(condition, 'check') or hasattr(condition, '_mock_name'):
|
|
228
|
+
return relationships
|
|
229
|
+
|
|
230
|
+
# Handle different condition types
|
|
231
|
+
if hasattr(condition, 'field'):
|
|
232
|
+
# Extract relationships from field path (e.g., "status__value" -> "status")
|
|
233
|
+
field_path = condition.field
|
|
234
|
+
if isinstance(field_path, str):
|
|
235
|
+
if '__' in field_path:
|
|
236
|
+
# Take the first part before __ (the relationship to preload)
|
|
237
|
+
rel_field = field_path.split('__')[0]
|
|
238
|
+
relationships.add(rel_field)
|
|
239
|
+
elif self._is_relationship_field(model_cls, field_path):
|
|
240
|
+
relationships.add(field_path)
|
|
241
|
+
|
|
242
|
+
# Handle composite conditions (AndCondition, OrCondition)
|
|
243
|
+
if hasattr(condition, 'cond1') and hasattr(condition, 'cond2'):
|
|
244
|
+
relationships.update(self._extract_condition_relationships(condition.cond1, model_cls))
|
|
245
|
+
relationships.update(self._extract_condition_relationships(condition.cond2, model_cls))
|
|
246
|
+
|
|
247
|
+
# Handle NotCondition
|
|
248
|
+
if hasattr(condition, 'cond'):
|
|
249
|
+
relationships.update(self._extract_condition_relationships(condition.cond, model_cls))
|
|
250
|
+
|
|
251
|
+
return relationships
|
|
252
|
+
|
|
253
|
+
def _is_relationship_field(self, model_cls, field_name):
|
|
254
|
+
"""Check if a field is a relationship field."""
|
|
255
|
+
try:
|
|
256
|
+
field = model_cls._meta.get_field(field_name)
|
|
257
|
+
return field.is_relation and not field.many_to_many
|
|
258
|
+
except:
|
|
259
|
+
return False
|
|
260
|
+
|
|
261
|
+
def _preload_condition_relationships(self, changeset, relationships):
|
|
262
|
+
"""
|
|
263
|
+
Preload relationships needed for condition evaluation.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
changeset: ChangeSet with records
|
|
267
|
+
relationships: Set of relationship field names to preload
|
|
268
|
+
"""
|
|
269
|
+
if not relationships or not changeset.new_records:
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
# Use Django's select_related to preload relationships
|
|
273
|
+
relationship_list = list(relationships)
|
|
274
|
+
|
|
275
|
+
# Preload for new_records
|
|
276
|
+
if changeset.new_records:
|
|
277
|
+
# Use select_related on the queryset
|
|
278
|
+
ids = [obj.pk for obj in changeset.new_records if obj.pk is not None]
|
|
279
|
+
if ids:
|
|
280
|
+
preloaded = changeset.model_cls.objects.filter(pk__in=ids).select_related(*relationship_list).in_bulk()
|
|
281
|
+
# Update the objects in changeset with preloaded relationships
|
|
282
|
+
for obj in changeset.new_records:
|
|
283
|
+
if obj.pk and obj.pk in preloaded:
|
|
284
|
+
preloaded_obj = preloaded[obj.pk]
|
|
285
|
+
for rel in relationship_list:
|
|
286
|
+
if hasattr(preloaded_obj, rel):
|
|
287
|
+
setattr(obj, rel, getattr(preloaded_obj, rel))
|
|
288
|
+
|
|
289
|
+
# Also handle unsaved objects by preloading their FK targets
|
|
290
|
+
for obj in changeset.new_records:
|
|
291
|
+
if obj.pk is None: # Unsaved object
|
|
292
|
+
for rel in relationship_list:
|
|
293
|
+
if hasattr(obj, f'{rel}_id'):
|
|
294
|
+
rel_id = getattr(obj, f'{rel}_id')
|
|
295
|
+
if rel_id:
|
|
296
|
+
# Load the related object
|
|
297
|
+
rel_model = getattr(changeset.model_cls._meta.get_field(rel).remote_field, 'model')
|
|
298
|
+
rel_obj = rel_model.objects.get(pk=rel_id)
|
|
299
|
+
setattr(obj, rel, rel_obj)
|
|
300
|
+
|
|
207
301
|
|
|
208
302
|
# Global dispatcher instance
|
|
209
303
|
_dispatcher: HookDispatcher | None = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/__init__.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/analyzer.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/bulk_executor.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/coordinator.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/field_utils.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/mti_handler.py
RENAMED
|
File without changes
|
{django_bulk_hooks-0.2.62 → django_bulk_hooks-0.2.63}/django_bulk_hooks/operations/mti_plans.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|