summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Mispelon <bmispelon@gmail.com>2020-03-04 13:33:12 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-03-16 10:27:23 +0100
commit3baf92cf8230ad3a932986170fd07c8feae7ff2f (patch)
treed40a4ccf7e8d693615e908fb40f0612e1495635a
parent924c01ba095f7df9529a65c176a892a5c1624ec5 (diff)
Fixed #31340 -- Allowed query expressions in SearchQuery.value and __search lookup.
-rw-r--r--django/contrib/postgres/search.py5
-rw-r--r--docs/ref/contrib/postgres/search.txt7
-rw-r--r--docs/releases/3.1.txt5
-rw-r--r--tests/postgres_tests/migrations/0002_create_test_models.py11
-rw-r--r--tests/postgres_tests/models.py5
-rw-r--r--tests/postgres_tests/test_search.py14
6 files changed, 43 insertions, 4 deletions
diff --git a/django/contrib/postgres/search.py b/django/contrib/postgres/search.py
index 90b6823575..2b2ae0c321 100644
--- a/django/contrib/postgres/search.py
+++ b/django/contrib/postgres/search.py
@@ -11,7 +11,7 @@ class SearchVectorExact(Lookup):
lookup_name = 'exact'
def process_rhs(self, qn, connection):
- if not hasattr(self.rhs, 'resolve_expression'):
+ if not isinstance(self.rhs, (SearchQuery, CombinedSearchQuery)):
config = getattr(self.lhs, 'config', None)
self.rhs = SearchQuery(self.rhs, config=config)
rhs, rhs_params = super().process_rhs(qn, connection)
@@ -170,7 +170,8 @@ class SearchQuery(SearchQueryCombinable, Func):
self.function = self.SEARCH_TYPES.get(search_type)
if self.function is None:
raise ValueError("Unknown search_type argument '%s'." % search_type)
- value = Value(value)
+ if not hasattr(value, 'resolve_expression'):
+ value = Value(value)
expressions = (value,)
self.config = SearchConfig.from_parameter(config)
if self.config is not None:
diff --git a/docs/ref/contrib/postgres/search.txt b/docs/ref/contrib/postgres/search.txt
index 949d95929e..65d54cfd8d 100644
--- a/docs/ref/contrib/postgres/search.txt
+++ b/docs/ref/contrib/postgres/search.txt
@@ -35,6 +35,10 @@ query and the vector.
To use the ``search`` lookup, ``'django.contrib.postgres'`` must be in your
:setting:`INSTALLED_APPS`.
+.. versionchanged:: 3.1
+
+ Support for query expressions was added.
+
``SearchVector``
================
@@ -108,7 +112,8 @@ See :ref:`postgresql-fts-search-configuration` for an explanation of the
.. versionchanged:: 3.1
- Support for ``'websearch'`` search type was added.
+ Support for ``'websearch'`` search type and query expressions in
+ ``SearchQuery.value`` were added.
``SearchRank``
==============
diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt
index 278752db90..de66d7805f 100644
--- a/docs/releases/3.1.txt
+++ b/docs/releases/3.1.txt
@@ -113,9 +113,14 @@ Minor features
* :class:`~django.contrib.postgres.search.SearchQuery` now supports
``'websearch'`` search type on PostgreSQL 11+.
+* :class:`SearchQuery.value <django.contrib.postgres.search.SearchQuery>` now
+ supports query expressions.
+
* The new :class:`~django.contrib.postgres.search.SearchHeadline` class allows
highlighting search results.
+* :lookup:`search` lookup now supports query expressions.
+
:mod:`django.contrib.redirects`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/postgres_tests/migrations/0002_create_test_models.py b/tests/postgres_tests/migrations/0002_create_test_models.py
index 12d94e348a..ee1463e1eb 100644
--- a/tests/postgres_tests/migrations/0002_create_test_models.py
+++ b/tests/postgres_tests/migrations/0002_create_test_models.py
@@ -186,6 +186,17 @@ class Migration(migrations.Migration):
bases=None,
),
migrations.CreateModel(
+ name='LineSavedSearch',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('line', models.ForeignKey('postgres_tests.Line', on_delete=models.CASCADE)),
+ ('query', models.CharField(max_length=100)),
+ ],
+ options={
+ 'required_db_vendor': 'postgresql',
+ },
+ ),
+ migrations.CreateModel(
name='AggregateTestModel',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
diff --git a/tests/postgres_tests/models.py b/tests/postgres_tests/models.py
index 8528c59da1..e803c989e0 100644
--- a/tests/postgres_tests/models.py
+++ b/tests/postgres_tests/models.py
@@ -139,6 +139,11 @@ class Line(PostgreSQLModel):
return self.dialogue or ''
+class LineSavedSearch(PostgreSQLModel):
+ line = models.ForeignKey('Line', models.CASCADE)
+ query = models.CharField(max_length=100)
+
+
class RangesModel(PostgreSQLModel):
ints = IntegerRangeField(blank=True, null=True)
bigints = BigIntegerRangeField(blank=True, null=True)
diff --git a/tests/postgres_tests/test_search.py b/tests/postgres_tests/test_search.py
index 0e836e896b..b40d672920 100644
--- a/tests/postgres_tests/test_search.py
+++ b/tests/postgres_tests/test_search.py
@@ -10,7 +10,7 @@ from django.db.models import F
from django.test import modify_settings, skipUnlessDBFeature
from . import PostgreSQLSimpleTestCase, PostgreSQLTestCase
-from .models import Character, Line, Scene
+from .models import Character, Line, LineSavedSearch, Scene
try:
from django.contrib.postgres.search import (
@@ -110,6 +110,18 @@ class SimpleSearchTest(GrailTestData, PostgreSQLTestCase):
)
self.assertSequenceEqual(searched, [self.verse2])
+ def test_search_with_F_expression(self):
+ # Non-matching query.
+ LineSavedSearch.objects.create(line=self.verse1, query='hearts')
+ # Matching query.
+ match = LineSavedSearch.objects.create(line=self.verse1, query='elbows')
+ for query_expression in [F('query'), SearchQuery(F('query'))]:
+ with self.subTest(query_expression):
+ searched = LineSavedSearch.objects.filter(
+ line__dialogue__search=query_expression,
+ )
+ self.assertSequenceEqual(searched, [match])
+
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.postgres'})
class SearchVectorFieldTest(GrailTestData, PostgreSQLTestCase):