mirror of
https://github.com/django/django.git
synced 2025-06-05 03:29:12 +00:00
Fixed #21604 -- Embed raw queries as subqueries
Added functionallity so RawQueries can be used as subquries when using __in filter
This commit is contained in:
parent
e580b891cb
commit
3def262723
@ -2048,6 +2048,9 @@ class RawQuerySet:
|
|||||||
model_init_names = [f.attname for f in model_init_fields]
|
model_init_names = [f.attname for f in model_init_fields]
|
||||||
return model_init_names, model_init_order, annotation_fields
|
return model_init_names, model_init_order, annotation_fields
|
||||||
|
|
||||||
|
def resolve_expression(self, *args, **kwargs):
|
||||||
|
return self.query.resolve_expression(self, *args, **kwargs)
|
||||||
|
|
||||||
def prefetch_related(self, *lookups):
|
def prefetch_related(self, *lookups):
|
||||||
"""Same as QuerySet.prefetch_related()"""
|
"""Same as QuerySet.prefetch_related()"""
|
||||||
clone = self._clone()
|
clone = self._clone()
|
||||||
|
@ -82,6 +82,11 @@ JoinInfo = namedtuple(
|
|||||||
class RawQuery:
|
class RawQuery:
|
||||||
"""A single raw SQL query."""
|
"""A single raw SQL query."""
|
||||||
|
|
||||||
|
has_select_fields = True
|
||||||
|
contains_aggregate = []
|
||||||
|
contains_over_clause = False
|
||||||
|
subquery = False
|
||||||
|
|
||||||
def __init__(self, sql, using, params=()):
|
def __init__(self, sql, using, params=()):
|
||||||
self.params = params
|
self.params = params
|
||||||
self.sql = sql
|
self.sql = sql
|
||||||
@ -98,7 +103,9 @@ class RawQuery:
|
|||||||
return self.clone(using)
|
return self.clone(using)
|
||||||
|
|
||||||
def clone(self, using):
|
def clone(self, using):
|
||||||
return RawQuery(self.sql, using, params=self.params)
|
query = RawQuery(self.sql, using, params=self.params)
|
||||||
|
query.subquery = self.subquery
|
||||||
|
return query
|
||||||
|
|
||||||
def get_columns(self):
|
def get_columns(self):
|
||||||
if self.cursor is None:
|
if self.cursor is None:
|
||||||
@ -151,6 +158,17 @@ class RawQuery:
|
|||||||
self.cursor = connection.cursor()
|
self.cursor = connection.cursor()
|
||||||
self.cursor.execute(self.sql, params)
|
self.cursor.execute(self.sql, params)
|
||||||
|
|
||||||
|
def as_sql(self, compiler, connection):
|
||||||
|
sql = self.sql
|
||||||
|
if self.subquery:
|
||||||
|
sql = "(%s)" % sql
|
||||||
|
return sql, self.params
|
||||||
|
|
||||||
|
def resolve_expression(self, query, *args, **kwargs):
|
||||||
|
clone = self.clone(self.using)
|
||||||
|
clone.subquery = True
|
||||||
|
return clone
|
||||||
|
|
||||||
|
|
||||||
ExplainInfo = namedtuple("ExplainInfo", ("format", "options"))
|
ExplainInfo = namedtuple("ExplainInfo", ("format", "options"))
|
||||||
|
|
||||||
@ -1243,7 +1261,7 @@ class Query(BaseExpression):
|
|||||||
)
|
)
|
||||||
elif hasattr(value, "_meta"):
|
elif hasattr(value, "_meta"):
|
||||||
self.check_query_object_type(value, opts, field)
|
self.check_query_object_type(value, opts, field)
|
||||||
elif hasattr(value, "__iter__"):
|
elif hasattr(value, "__iter__") and not getattr(value, "subquery", False):
|
||||||
for v in value:
|
for v in value:
|
||||||
self.check_query_object_type(v, opts, field)
|
self.check_query_object_type(v, opts, field)
|
||||||
|
|
||||||
|
@ -355,6 +355,9 @@ Miscellaneous
|
|||||||
* The undocumented ``negated`` parameter of the
|
* The undocumented ``negated`` parameter of the
|
||||||
:class:`~django.db.models.Exists` expression is removed.
|
:class:`~django.db.models.Exists` expression is removed.
|
||||||
|
|
||||||
|
* Add functionality so :class:`~django.db.models.sql.RawQuery`
|
||||||
|
can be used as a subquery.
|
||||||
|
|
||||||
.. _deprecated-features-4.2:
|
.. _deprecated-features-4.2:
|
||||||
|
|
||||||
Features deprecated in 4.2
|
Features deprecated in 4.2
|
||||||
|
@ -3,6 +3,7 @@ from decimal import Decimal
|
|||||||
|
|
||||||
from django.core.exceptions import FieldDoesNotExist
|
from django.core.exceptions import FieldDoesNotExist
|
||||||
from django.db.models.query import RawQuerySet
|
from django.db.models.query import RawQuerySet
|
||||||
|
from django.db.models.sql.query import RawQuery
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, skipUnlessDBFeature
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
@ -62,6 +63,12 @@ class RawQueryTests(TestCase):
|
|||||||
paperback=True,
|
paperback=True,
|
||||||
opening_line="It was the day my grandmother exploded.",
|
opening_line="It was the day my grandmother exploded.",
|
||||||
)
|
)
|
||||||
|
cls.b5 = Book.objects.create(
|
||||||
|
title="Some other awesome book",
|
||||||
|
author=cls.a4,
|
||||||
|
paperback=True,
|
||||||
|
opening_line="Story of the book that was left behind",
|
||||||
|
)
|
||||||
cls.c1 = Coffee.objects.create(brand="dunkin doughnuts")
|
cls.c1 = Coffee.objects.create(brand="dunkin doughnuts")
|
||||||
cls.c2 = Coffee.objects.create(brand="starbucks")
|
cls.c2 = Coffee.objects.create(brand="starbucks")
|
||||||
cls.r1 = Reviewer.objects.create()
|
cls.r1 = Reviewer.objects.create()
|
||||||
@ -308,7 +315,7 @@ class RawQueryTests(TestCase):
|
|||||||
("book_count", 3),
|
("book_count", 3),
|
||||||
("book_count", 0),
|
("book_count", 0),
|
||||||
("book_count", 1),
|
("book_count", 1),
|
||||||
("book_count", 0),
|
("book_count", 1),
|
||||||
)
|
)
|
||||||
authors = Author.objects.order_by("pk")
|
authors = Author.objects.order_by("pk")
|
||||||
self.assertSuccessfulRawQuery(Author, query, authors, expected_annotations)
|
self.assertSuccessfulRawQuery(Author, query, authors, expected_annotations)
|
||||||
@ -413,7 +420,18 @@ class RawQueryTests(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_len(self):
|
def test_len(self):
|
||||||
self.assertEqual(len(Book.objects.raw("SELECT * FROM raw_query_book")), 4)
|
self.assertEqual(len(Book.objects.raw("SELECT * FROM raw_query_book")), 5)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(Book.objects.raw("SELECT * FROM raw_query_book WHERE id = 0")), 0
|
len(Book.objects.raw("SELECT * FROM raw_query_book WHERE id = 0")), 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_as_subquery(self):
|
||||||
|
author_id_sub_query = RawQuery(
|
||||||
|
"SELECT id FROM raw_query_author WHERE last_name LIKE %s",
|
||||||
|
using="default",
|
||||||
|
params=["Smith"],
|
||||||
|
)
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
self.assertEqual(
|
||||||
|
len(Book.objects.filter(author_id__in=author_id_sub_query)), 4
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user