Fixed #29582 -- Fixed a crash when using SearchVector with non text-fields.
The PostgreSQL concat() function handles nulls and non-text values better than the || operator.
This commit is contained in:
parent
71a739f3d7
commit
1a28dc3887
|
@ -1,6 +1,5 @@
|
||||||
from django.db.models import Field, FloatField
|
from django.db.models import Field, FloatField
|
||||||
from django.db.models.expressions import CombinedExpression, Func, Value
|
from django.db.models.expressions import CombinedExpression, Func, Value
|
||||||
from django.db.models.functions import Coalesce
|
|
||||||
from django.db.models.lookups import Lookup
|
from django.db.models.lookups import Lookup
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,15 +45,13 @@ class SearchVectorCombinable:
|
||||||
|
|
||||||
class SearchVector(SearchVectorCombinable, Func):
|
class SearchVector(SearchVectorCombinable, Func):
|
||||||
function = 'to_tsvector'
|
function = 'to_tsvector'
|
||||||
arg_joiner = " || ' ' || "
|
arg_joiner = ", ' ',"
|
||||||
|
template = '%(function)s(concat(%(expressions)s))'
|
||||||
output_field = SearchVectorField()
|
output_field = SearchVectorField()
|
||||||
config = None
|
config = None
|
||||||
|
|
||||||
def __init__(self, *expressions, **extra):
|
def __init__(self, *expressions, **extra):
|
||||||
super().__init__(*expressions, **extra)
|
super().__init__(*expressions, **extra)
|
||||||
self.source_expressions = [
|
|
||||||
Coalesce(expression, Value('')) for expression in self.source_expressions
|
|
||||||
]
|
|
||||||
self.config = self.extra.get('config', self.config)
|
self.config = self.extra.get('config', self.config)
|
||||||
weight = self.extra.get('weight')
|
weight = self.extra.get('weight')
|
||||||
if weight is not None and not hasattr(weight, 'resolve_expression'):
|
if weight is not None and not hasattr(weight, 'resolve_expression'):
|
||||||
|
@ -75,7 +72,7 @@ class SearchVector(SearchVectorCombinable, Func):
|
||||||
if template is None:
|
if template is None:
|
||||||
if self.config:
|
if self.config:
|
||||||
config_sql, config_params = compiler.compile(self.config)
|
config_sql, config_params = compiler.compile(self.config)
|
||||||
template = "%(function)s({}::regconfig, %(expressions)s)".format(config_sql.replace('%', '%%'))
|
template = "%(function)s({}::regconfig, concat(%(expressions)s))".format(config_sql.replace('%', '%%'))
|
||||||
else:
|
else:
|
||||||
template = self.template
|
template = self.template
|
||||||
sql, params = super().as_sql(compiler, connection, function=function, template=template)
|
sql, params = super().as_sql(compiler, connection, function=function, template=template)
|
||||||
|
|
|
@ -155,6 +155,12 @@ class MultipleFieldsTest(GrailTestData, PostgreSQLTestCase):
|
||||||
).filter(search='bedemir')
|
).filter(search='bedemir')
|
||||||
self.assertEqual(set(searched), {self.bedemir0, self.bedemir1, self.crowd, self.witch, self.duck})
|
self.assertEqual(set(searched), {self.bedemir0, self.bedemir1, self.crowd, self.witch, self.duck})
|
||||||
|
|
||||||
|
def test_search_with_non_text(self):
|
||||||
|
searched = Line.objects.annotate(
|
||||||
|
search=SearchVector('id'),
|
||||||
|
).filter(search=str(self.crowd.id))
|
||||||
|
self.assertSequenceEqual(searched, [self.crowd])
|
||||||
|
|
||||||
def test_config_query_explicit(self):
|
def test_config_query_explicit(self):
|
||||||
searched = Line.objects.annotate(
|
searched = Line.objects.annotate(
|
||||||
search=SearchVector('scene__setting', 'dialogue', config='french'),
|
search=SearchVector('scene__setting', 'dialogue', config='french'),
|
||||||
|
|
Loading…
Reference in New Issue