django-bulk-hooks 0.1.239__py3-none-any.whl → 0.1.241__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/queryset.py +134 -10
- {django_bulk_hooks-0.1.239.dist-info → django_bulk_hooks-0.1.241.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.239.dist-info → django_bulk_hooks-0.1.241.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.239.dist-info → django_bulk_hooks-0.1.241.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.239.dist-info → django_bulk_hooks-0.1.241.dist-info}/WHEEL +0 -0
django_bulk_hooks/queryset.py
CHANGED
|
@@ -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,13 +71,29 @@ 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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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}")
|
|
78
92
|
|
|
79
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
|
+
|
|
80
97
|
if has_subquery:
|
|
81
98
|
logger.debug(f"Detected Subquery in update: {[k for k, v in kwargs.items() if isinstance(v, Subquery)]}")
|
|
82
99
|
else:
|
|
@@ -84,6 +101,8 @@ class HookQuerySetMixin:
|
|
|
84
101
|
for k, v in kwargs.items():
|
|
85
102
|
if hasattr(v, 'query') and hasattr(v, 'resolve_expression'):
|
|
86
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]}")
|
|
87
106
|
|
|
88
107
|
# Apply field updates to instances
|
|
89
108
|
# If a per-object value map exists (from bulk_update), prefer it over kwargs
|
|
@@ -145,11 +164,24 @@ class HookQuerySetMixin:
|
|
|
145
164
|
output_field = field_obj
|
|
146
165
|
target_name = field_name
|
|
147
166
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
# Special handling for Subquery values in CASE statements
|
|
168
|
+
if isinstance(value, Subquery):
|
|
169
|
+
logger.debug(f"Creating When statement with Subquery for {field_name}")
|
|
170
|
+
# Ensure the Subquery has proper output_field
|
|
171
|
+
if not hasattr(value, 'output_field') or value.output_field is None:
|
|
172
|
+
value.output_field = output_field
|
|
173
|
+
logger.debug(f"Set output_field for Subquery in When statement to {output_field}")
|
|
174
|
+
when_statements.append(
|
|
175
|
+
When(
|
|
176
|
+
pk=obj_pk, then=value
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
else:
|
|
180
|
+
when_statements.append(
|
|
181
|
+
When(
|
|
182
|
+
pk=obj_pk, then=Value(value, output_field=output_field)
|
|
183
|
+
)
|
|
151
184
|
)
|
|
152
|
-
)
|
|
153
185
|
|
|
154
186
|
if when_statements:
|
|
155
187
|
case_statements[target_name] = Case(
|
|
@@ -158,11 +190,103 @@ class HookQuerySetMixin:
|
|
|
158
190
|
|
|
159
191
|
# Merge extra CASE updates into kwargs for DB update
|
|
160
192
|
if case_statements:
|
|
193
|
+
logger.debug(f"Adding case statements to kwargs: {list(case_statements.keys())}")
|
|
194
|
+
for field_name, case_stmt in case_statements.items():
|
|
195
|
+
logger.debug(f"Case statement for {field_name}: {type(case_stmt).__name__}")
|
|
196
|
+
# Check if the case statement contains Subquery objects
|
|
197
|
+
if hasattr(case_stmt, 'get_source_expressions'):
|
|
198
|
+
source_exprs = case_stmt.get_source_expressions()
|
|
199
|
+
for expr in source_exprs:
|
|
200
|
+
if isinstance(expr, Subquery):
|
|
201
|
+
logger.debug(f"Case statement for {field_name} contains Subquery")
|
|
202
|
+
elif hasattr(expr, 'get_source_expressions'):
|
|
203
|
+
# Check nested expressions (like Value objects)
|
|
204
|
+
nested_exprs = expr.get_source_expressions()
|
|
205
|
+
for nested_expr in nested_exprs:
|
|
206
|
+
if isinstance(nested_expr, Subquery):
|
|
207
|
+
logger.debug(f"Case statement for {field_name} contains nested Subquery")
|
|
208
|
+
|
|
161
209
|
kwargs = {**kwargs, **case_statements}
|
|
162
210
|
|
|
163
211
|
# Use Django's built-in update logic directly
|
|
164
212
|
# Call the base QuerySet implementation to avoid recursion
|
|
165
|
-
|
|
213
|
+
|
|
214
|
+
# Additional safety check: ensure Subquery objects are properly handled
|
|
215
|
+
# This prevents the "cannot adapt type 'Subquery'" error
|
|
216
|
+
safe_kwargs = {}
|
|
217
|
+
logger.debug(f"Processing {len(kwargs)} kwargs for safety check")
|
|
218
|
+
|
|
219
|
+
for key, value in kwargs.items():
|
|
220
|
+
logger.debug(f"Processing key '{key}' with value type {type(value).__name__}")
|
|
221
|
+
|
|
222
|
+
if isinstance(value, Subquery):
|
|
223
|
+
logger.debug(f"Found Subquery for field {key}")
|
|
224
|
+
# Ensure Subquery has proper output_field
|
|
225
|
+
if not hasattr(value, 'output_field') or value.output_field is None:
|
|
226
|
+
logger.warning(f"Subquery for field {key} missing output_field, attempting to infer")
|
|
227
|
+
# Try to infer from the model field
|
|
228
|
+
try:
|
|
229
|
+
field = model_cls._meta.get_field(key)
|
|
230
|
+
logger.debug(f"Inferred field type: {type(field).__name__}")
|
|
231
|
+
value = value.resolve_expression(None, None)
|
|
232
|
+
value.output_field = field
|
|
233
|
+
logger.debug(f"Set output_field to {field}")
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.error(f"Failed to infer output_field for Subquery on {key}: {e}")
|
|
236
|
+
raise
|
|
237
|
+
else:
|
|
238
|
+
logger.debug(f"Subquery for field {key} already has output_field: {value.output_field}")
|
|
239
|
+
safe_kwargs[key] = value
|
|
240
|
+
elif hasattr(value, 'get_source_expressions') and hasattr(value, 'resolve_expression'):
|
|
241
|
+
# Handle Case statements and other complex expressions
|
|
242
|
+
logger.debug(f"Found complex expression for field {key}: {type(value).__name__}")
|
|
243
|
+
|
|
244
|
+
# Check if this expression contains any Subquery objects
|
|
245
|
+
source_expressions = value.get_source_expressions()
|
|
246
|
+
has_nested_subquery = False
|
|
247
|
+
|
|
248
|
+
for expr in source_expressions:
|
|
249
|
+
if isinstance(expr, Subquery):
|
|
250
|
+
has_nested_subquery = True
|
|
251
|
+
logger.debug(f"Found nested Subquery in {type(value).__name__}")
|
|
252
|
+
# Ensure the nested Subquery has proper output_field
|
|
253
|
+
if not hasattr(expr, 'output_field') or expr.output_field is None:
|
|
254
|
+
try:
|
|
255
|
+
field = model_cls._meta.get_field(key)
|
|
256
|
+
expr.output_field = field
|
|
257
|
+
logger.debug(f"Set output_field for nested Subquery to {field}")
|
|
258
|
+
except Exception as e:
|
|
259
|
+
logger.error(f"Failed to set output_field for nested Subquery: {e}")
|
|
260
|
+
raise
|
|
261
|
+
|
|
262
|
+
if has_nested_subquery:
|
|
263
|
+
logger.debug(f"Expression contains Subquery, ensuring proper output_field")
|
|
264
|
+
# Try to resolve the expression to ensure it's properly formatted
|
|
265
|
+
try:
|
|
266
|
+
resolved_value = value.resolve_expression(None, None)
|
|
267
|
+
safe_kwargs[key] = resolved_value
|
|
268
|
+
logger.debug(f"Successfully resolved expression for {key}")
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.error(f"Failed to resolve expression for {key}: {e}")
|
|
271
|
+
raise
|
|
272
|
+
else:
|
|
273
|
+
safe_kwargs[key] = value
|
|
274
|
+
else:
|
|
275
|
+
logger.debug(f"Non-Subquery value for field {key}: {type(value).__name__}")
|
|
276
|
+
safe_kwargs[key] = value
|
|
277
|
+
|
|
278
|
+
logger.debug(f"Safe kwargs keys: {list(safe_kwargs.keys())}")
|
|
279
|
+
logger.debug(f"Safe kwargs types: {[(k, type(v).__name__) for k, v in safe_kwargs.items()]}")
|
|
280
|
+
|
|
281
|
+
logger.debug(f"Calling super().update() with {len(safe_kwargs)} kwargs")
|
|
282
|
+
try:
|
|
283
|
+
update_count = super().update(**safe_kwargs)
|
|
284
|
+
logger.debug(f"Super update successful, count: {update_count}")
|
|
285
|
+
except Exception as e:
|
|
286
|
+
logger.error(f"Super update failed: {e}")
|
|
287
|
+
logger.error(f"Exception type: {type(e).__name__}")
|
|
288
|
+
logger.error(f"Safe kwargs that caused failure: {safe_kwargs}")
|
|
289
|
+
raise
|
|
166
290
|
|
|
167
291
|
# If we used Subquery objects, refresh the instances to get computed values
|
|
168
292
|
if has_subquery and instances:
|
|
@@ -9,9 +9,9 @@ django_bulk_hooks/handler.py,sha256=Bx-W6yyiciKMyy-BRxUt3CmRPCrX9_LhQgU-5LaJTjg,
|
|
|
9
9
|
django_bulk_hooks/manager.py,sha256=nfWiwU5-yAoxdnQsUMohxtyCpkV0MBv6X3wmipr9eQY,3697
|
|
10
10
|
django_bulk_hooks/models.py,sha256=exnXYVKEVbYAXhChCP8VdWTnKCnm9DiTcokEIBee1I0,4350
|
|
11
11
|
django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
|
|
12
|
-
django_bulk_hooks/queryset.py,sha256=
|
|
12
|
+
django_bulk_hooks/queryset.py,sha256=kUmV4izYquYsvtcR3PH8TkY3PBm-Kt8c8B4HO4ck0uo,46280
|
|
13
13
|
django_bulk_hooks/registry.py,sha256=GRUTGVQEO2sdkC9OaZ9Q3U7mM-3Ix83uTyvrlTtpatw,1317
|
|
14
|
-
django_bulk_hooks-0.1.
|
|
15
|
-
django_bulk_hooks-0.1.
|
|
16
|
-
django_bulk_hooks-0.1.
|
|
17
|
-
django_bulk_hooks-0.1.
|
|
14
|
+
django_bulk_hooks-0.1.241.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
+
django_bulk_hooks-0.1.241.dist-info/METADATA,sha256=DUraAu1YHa04565ngESG8QnmmUwrcvrD1-4oqoOnBhY,9061
|
|
16
|
+
django_bulk_hooks-0.1.241.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
17
|
+
django_bulk_hooks-0.1.241.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|