Fixed #26056 -- Added QuerySet.values()/values_list() support for ArrayField's __overlap lookup.

Thanks Mads Jensen and kosz85 and the initial patch.
This commit is contained in:
Ben Cail 2022-10-26 09:58:08 -04:00 committed by Mariusz Felisiak
parent 81b1c167bf
commit fbde929b19
4 changed files with 35 additions and 1 deletions

View File

@ -1,5 +1,6 @@
from django.db.models import Transform from django.db.models import Transform
from django.db.models.lookups import PostgresOperatorLookup from django.db.models.lookups import PostgresOperatorLookup
from django.db.models.sql.query import Query
from .search import SearchVector, SearchVectorExact, SearchVectorField from .search import SearchVector, SearchVectorExact, SearchVectorField
@ -18,6 +19,13 @@ class Overlap(PostgresOperatorLookup):
lookup_name = "overlap" lookup_name = "overlap"
postgres_operator = "&&" postgres_operator = "&&"
def get_prep_lookup(self):
from .expressions import ArraySubquery
if isinstance(self.rhs, Query):
self.rhs = ArraySubquery(self.rhs)
return super().get_prep_lookup()
class HasKey(PostgresOperatorLookup): class HasKey(PostgresOperatorLookup):
lookup_name = "has_key" lookup_name = "has_key"

View File

@ -170,7 +170,7 @@ Returns objects where the data shares any results with the values passed. Uses
the SQL operator ``&&``. For example:: the SQL operator ``&&``. For example::
>>> Post.objects.create(name='First post', tags=['thoughts', 'django']) >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
>>> Post.objects.create(name='Second post', tags=['thoughts']) >>> Post.objects.create(name='Second post', tags=['thoughts', 'tutorial'])
>>> Post.objects.create(name='Third post', tags=['tutorial', 'django']) >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])
>>> Post.objects.filter(tags__overlap=['thoughts']) >>> Post.objects.filter(tags__overlap=['thoughts'])
@ -179,6 +179,14 @@ the SQL operator ``&&``. For example::
>>> Post.objects.filter(tags__overlap=['thoughts', 'tutorial']) >>> Post.objects.filter(tags__overlap=['thoughts', 'tutorial'])
<QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]> <QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>
>>> Post.objects.filter(tags__overlap=Post.objects.values_list('tags'))
<QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>
.. versionchanged:: 4.2
Support for ``QuerySet.values()`` and ``values_list()`` as a right-hand
side was added.
.. fieldlookup:: arrayfield.len .. fieldlookup:: arrayfield.len
``len`` ``len``

View File

@ -102,6 +102,9 @@ Minor features
<django.contrib.postgres.search.TrigramStrictWordDistance>` expressions allow <django.contrib.postgres.search.TrigramStrictWordDistance>` expressions allow
using trigram strict word similarity. using trigram strict word similarity.
* The :lookup:`arrayfield.overlap` lookup now supports ``QuerySet.values()``
and ``values_list()`` as a right-hand side.
:mod:`django.contrib.redirects` :mod:`django.contrib.redirects`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -414,6 +414,21 @@ class TestQuerying(PostgreSQLTestCase):
[obj_1, obj_2], [obj_1, obj_2],
) )
def test_overlap_values(self):
qs = NullableIntegerArrayModel.objects.filter(order__lt=3)
self.assertCountEqual(
NullableIntegerArrayModel.objects.filter(
field__overlap=qs.values_list("field"),
),
self.objs[:3],
)
self.assertCountEqual(
NullableIntegerArrayModel.objects.filter(
field__overlap=qs.values("field"),
),
self.objs[:3],
)
def test_lookups_autofield_array(self): def test_lookups_autofield_array(self):
qs = ( qs = (
NullableIntegerArrayModel.objects.filter( NullableIntegerArrayModel.objects.filter(