1
0
mirror of https://github.com/django/django.git synced 2025-04-05 05:56:42 +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:
William 2022-11-25 15:37:06 +02:00
parent e580b891cb
commit 3def262723
4 changed files with 46 additions and 4 deletions

View File

@ -2048,6 +2048,9 @@ class RawQuerySet:
model_init_names = [f.attname for f in model_init_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):
"""Same as QuerySet.prefetch_related()"""
clone = self._clone()

View File

@ -82,6 +82,11 @@ JoinInfo = namedtuple(
class RawQuery:
"""A single raw SQL query."""
has_select_fields = True
contains_aggregate = []
contains_over_clause = False
subquery = False
def __init__(self, sql, using, params=()):
self.params = params
self.sql = sql
@ -98,7 +103,9 @@ class RawQuery:
return self.clone(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):
if self.cursor is None:
@ -151,6 +158,17 @@ class RawQuery:
self.cursor = connection.cursor()
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"))
@ -1243,7 +1261,7 @@ class Query(BaseExpression):
)
elif hasattr(value, "_meta"):
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:
self.check_query_object_type(v, opts, field)

View File

@ -355,6 +355,9 @@ Miscellaneous
* The undocumented ``negated`` parameter of the
: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:
Features deprecated in 4.2

View File

@ -3,6 +3,7 @@ from decimal import Decimal
from django.core.exceptions import FieldDoesNotExist
from django.db.models.query import RawQuerySet
from django.db.models.sql.query import RawQuery
from django.test import TestCase, skipUnlessDBFeature
from .models import (
@ -62,6 +63,12 @@ class RawQueryTests(TestCase):
paperback=True,
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.c2 = Coffee.objects.create(brand="starbucks")
cls.r1 = Reviewer.objects.create()
@ -308,7 +315,7 @@ class RawQueryTests(TestCase):
("book_count", 3),
("book_count", 0),
("book_count", 1),
("book_count", 0),
("book_count", 1),
)
authors = Author.objects.order_by("pk")
self.assertSuccessfulRawQuery(Author, query, authors, expected_annotations)
@ -413,7 +420,18 @@ class RawQueryTests(TestCase):
)
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(
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
)