Include NULL value for negative search on nested Many2One and Reference
This unify the search behavior on relational fields with NULL value started with c14f51b77b30. Closes #13585 (grafted from 87456218dcc9b075658f9e2338b120fce9b9a43d) --HG-- branch : 7.0
This commit is contained in:
1 parent
82d30e56ef
commit
1dcbb0255c
5 files changed
+54
-5
No files matched your search
@@ -37,6 +37,10 @@ by this pattern::
|
||||
|
||||
The number of *dots* in a clause is not limited.
|
||||
|
||||
.. note::
|
||||
Negative operators match records for which any of the field before the
|
||||
last dot is empty.
|
||||
|
||||
.. warning::
|
||||
For :class:`trytond.model.fields.Reference`, an extra ending clause is
|
||||
needed to define the target model to join, for example::
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from sql import As, Column, Expression, Literal, Query, With
|
||||
from sql import As, Column, Expression, Literal, Null, Query, With
|
||||
from sql.aggregate import Max
|
||||
from sql.conditionals import Coalesce
|
||||
from sql.operators import Or
|
||||
@@ -266,7 +266,7 @@ class Many2One(Field):
|
||||
target_domain = [(target_name,) + tuple(domain[1:])]
|
||||
if Target.estimated_count() < _subquery_threshold:
|
||||
query = Target.search(target_domain, order=[], query=True)
|
||||
return column.in_(query)
|
||||
expression = column.in_(query)
|
||||
else:
|
||||
rule_domain = Rule.domain_get(Target.__name__, mode='read')
|
||||
if rule_domain:
|
||||
@@ -279,7 +279,9 @@ class Many2One(Field):
|
||||
target_table, _ = target_tables[None]
|
||||
_, expression = Target.search_domain(
|
||||
target_domain, tables=target_tables)
|
||||
return expression
|
||||
if operator.startswith('not') or operator == '!=':
|
||||
expression |= column == Null
|
||||
return expression
|
||||
|
||||
@order_method
|
||||
def convert_order(self, name, tables, Model):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import warnings
|
||||
|
||||
from sql import Cast, Expression, Literal, Query
|
||||
from sql import Cast, Expression, Literal, Null, Query
|
||||
from sql.functions import Position, Substring
|
||||
|
||||
from trytond.pool import Pool
|
||||
@@ -214,9 +214,12 @@ class Reference(SelectionMixin, Field):
|
||||
if 'active' in Target._fields:
|
||||
target_domain.append(('active', 'in', [True, False]))
|
||||
query = Target.search(target_domain, order=[], query=True)
|
||||
return (
|
||||
expression = (
|
||||
self.sql_id(column, Model).in_(query)
|
||||
& column.like(target + ',%'))
|
||||
if operator.startswith('not') or operator == '!=':
|
||||
expression |= column == Null
|
||||
return expression
|
||||
|
||||
def definition(self, model, language):
|
||||
encoder = PYSONEncoder()
|
||||
|
||||
@@ -200,6 +200,24 @@ class FieldMany2OneTestCase(unittest.TestCase):
|
||||
query = self._test_search_join()
|
||||
self.assertNotIsInstance(query.from_[0], Join)
|
||||
|
||||
@with_transaction()
|
||||
def test_search_nested_null(self):
|
||||
"Test search by null many2one"
|
||||
pool = Pool()
|
||||
Many2One = pool.get('test.many2one')
|
||||
|
||||
record, = Many2One.create([{'many2one': None}])
|
||||
|
||||
result = Many2One.search([
|
||||
('many2one.value', '=', 1),
|
||||
])
|
||||
self.assertListEqual(result, [])
|
||||
|
||||
result = Many2One.search([
|
||||
('many2one.value', '!=', 1),
|
||||
])
|
||||
self.assertListEqual(result, [record])
|
||||
|
||||
@with_transaction()
|
||||
def test_context_attribute(self):
|
||||
"Test context on many2one attribute"
|
||||
|
||||
@@ -352,6 +352,28 @@ class FieldReferenceTestCase(unittest.TestCase):
|
||||
|
||||
self.assertListEqual(references, [reference])
|
||||
|
||||
@with_transaction()
|
||||
def test_search_join_null(self):
|
||||
"Test search by null reference join"
|
||||
pool = Pool()
|
||||
Target = pool.get('test.reference.target')
|
||||
Reference = pool.get('test.reference')
|
||||
reference, = Reference.create([{
|
||||
'reference': None,
|
||||
}])
|
||||
|
||||
result = Reference.search([
|
||||
('reference.name', '=', "Target", Target.__name__),
|
||||
])
|
||||
|
||||
self.assertListEqual(result, [])
|
||||
|
||||
result = Reference.search([
|
||||
('reference.name', '!=', "Target", Target.__name__),
|
||||
])
|
||||
|
||||
self.assertListEqual(result, [reference])
|
||||
|
||||
@with_transaction()
|
||||
def test_write_string(self):
|
||||
"Test write reference string"
|
||||
|
||||
Reference in new issue
Block a user