mirror of
https://github.com/django/django.git
synced 2025-11-17 02:24:22 +00:00
Add numeric conversion support to LazyObject
Enable int(), float(), and complex() on SimpleLazyObject to fix regression with nested subquery annotations.
This restores compatibility with querysets expecting numeric types and prevents TypeError when filtering with SimpleLazyObject.
Includes regression tests for numeric conversions and subquery usage.
Fixes regression described in #3543129822.
This commit is contained in:
parent
fa5e7e46d8
commit
02d1a8952e
3 changed files with 58 additions and 1 deletions
|
|
@ -324,6 +324,11 @@ class LazyObject:
|
|||
__str__ = new_method_proxy(str)
|
||||
__bool__ = new_method_proxy(bool)
|
||||
|
||||
# Numeric conversion support
|
||||
__int__ = new_method_proxy(int)
|
||||
__float__ = new_method_proxy(float)
|
||||
__complex__ = new_method_proxy(complex)
|
||||
|
||||
# Introspection support
|
||||
__dir__ = new_method_proxy(dir)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@ from operator import attrgetter
|
|||
|
||||
from django.core.exceptions import EmptyResultSet, FieldError
|
||||
from django.db import DEFAULT_DB_ALIAS, connection
|
||||
from django.db.models import Count, Exists, F, OuterRef, Q
|
||||
from django.db.models import Count, Exists, F, OuterRef, Q, Subquery
|
||||
from django.db.models.sql.constants import LOUTER
|
||||
from django.db.models.sql.where import NothingNode, WhereNode
|
||||
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
|
||||
from .models import (
|
||||
FK1, Annotation, Article, Author, BaseA, Book, CategoryItem,
|
||||
|
|
@ -2130,6 +2131,39 @@ class SubqueryTests(TestCase):
|
|||
).order_by('id').values_list('id', flat=True), [2, 4]
|
||||
)
|
||||
|
||||
def test_nested_subquery_with_simplelazyobject(self):
|
||||
"""
|
||||
Test that SimpleLazyObject works with nested subquery annotations.
|
||||
Regression test for the issue where using SimpleLazyObject in a filter
|
||||
with nested subquery annotation would fail with:
|
||||
TypeError: int() argument must be a string, a bytes-like object or a
|
||||
number, not 'SimpleLazyObject'
|
||||
"""
|
||||
# Create test data
|
||||
category = NamedCategory.objects.create(id=5, name="test")
|
||||
|
||||
# Create a nested subquery annotation
|
||||
# This simulates the scenario in the bug report where we have:
|
||||
# - An outer query on DumbCategory
|
||||
# - Annotated with a subquery on Tag
|
||||
# - That subquery is itself annotated with a subquery on another model
|
||||
# - And we filter using a SimpleLazyObject
|
||||
tag_subquery = Tag.objects.filter(
|
||||
category=OuterRef('pk')
|
||||
).values('category')
|
||||
|
||||
# Wrap the category id in a SimpleLazyObject
|
||||
# This is the key part - wrapping a numeric value (like a User.id)
|
||||
lazy_category_id = SimpleLazyObject(lambda: category.id)
|
||||
|
||||
# This should not raise a TypeError
|
||||
queryset = NamedCategory.objects.annotate(
|
||||
tag_category=Subquery(tag_subquery)
|
||||
).filter(tag_category=lazy_category_id)
|
||||
|
||||
# Force evaluation - this is where the bug would occur
|
||||
str(queryset.query)
|
||||
|
||||
|
||||
@skipUnlessDBFeature('allow_sliced_subqueries_with_in')
|
||||
class QuerySetBitwiseOperationTests(TestCase):
|
||||
|
|
|
|||
|
|
@ -91,6 +91,24 @@ class LazyObjectTestCase(TestCase):
|
|||
for t in [True, 1, (1,), {1: 2}, [1], object(), {1}]:
|
||||
self.assertTrue(t)
|
||||
|
||||
def test_int(self):
|
||||
obj = self.lazy_wrap(42)
|
||||
self.assertEqual(int(obj), 42)
|
||||
obj = self.lazy_wrap(3.14)
|
||||
self.assertEqual(int(obj), 3)
|
||||
|
||||
def test_float(self):
|
||||
obj = self.lazy_wrap(42)
|
||||
self.assertEqual(float(obj), 42.0)
|
||||
obj = self.lazy_wrap(3.14)
|
||||
self.assertEqual(float(obj), 3.14)
|
||||
|
||||
def test_complex(self):
|
||||
obj = self.lazy_wrap(42)
|
||||
self.assertEqual(complex(obj), 42+0j)
|
||||
obj = self.lazy_wrap(3.14)
|
||||
self.assertEqual(complex(obj), 3.14+0j)
|
||||
|
||||
def test_dir(self):
|
||||
obj = self.lazy_wrap('foo')
|
||||
self.assertEqual(dir(obj), dir('foo'))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue