[IMP] minor cosmetic changes to improve readability and ease reviewing process

bzr revid: odo@openerp.com-20110921224423-h5hjnqrqgrp5hhau
This commit is contained in:
Olivier Dony 2011-09-22 00:44:23 +02:00
parent 6e4a93cd22
commit 6652943cd2
2 changed files with 104 additions and 85 deletions

View File

@ -202,8 +202,6 @@
if x.website: if x.website:
with_website.append(x.id) with_website.append(x.id)
with_website.sort() with_website.sort()
print "with_parent", with_parent
print "without_parent", without_parent
# We treat null values differently than in SQL. For instance in SQL: # We treat null values differently than in SQL. For instance in SQL:
# SELECT id FROM res_partner WHERE parent_id NOT IN (0) # SELECT id FROM res_partner WHERE parent_id NOT IN (0)
@ -367,22 +365,31 @@
# Search the company via its one2many (the one2many must point back at the company). # Search the company via its one2many (the one2many must point back at the company).
company = self.browse(cr, uid, ref('ymltest_company3')) company = self.browse(cr, uid, ref('ymltest_company3'))
max_currency_id = max(self.pool.get('res.currency').search(cr, uid, [])) max_currency_id = max(self.pool.get('res.currency').search(cr, uid, []))
currency_ids = self.pool.get('res.currency').search(cr, uid, [('name', 'not like', 'probably_unexisting_name')]) currency_ids1 = self.pool.get('res.currency').search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
currency_ids = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [max_currency_id + 1003])]) currency_ids2 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [max_currency_id + 1003])])
currency_ids = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [])]) currency_ids3 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [])])
assert currency_ids1 == currency_ids2 == currency_ids3, 'All 3 results should have be the same: all currencies'
default_company = self.browse(cr, uid, 1) default_company = self.browse(cr, uid, 1)
# one2many towards same model # one2many towards same model
res_1 = self.search(cr, uid, [('child_ids', 'in', [x.id for x in company.child_ids])]) res_1 = self.search(cr, uid, [('child_ids', 'in', [x.id for x in company.child_ids])]) # any company having a child of company3 as child
res_2 = self.search(cr, uid, [('child_ids', 'in', [company.child_ids[0].id])]) res_2 = self.search(cr, uid, [('child_ids', 'in', [company.child_ids[0].id])]) # any company having the first child of company3 as child
# one2many towards another model # one2many towards another model
res_3 = self.search(cr, uid, [('currency_ids', 'in', [x.id for x in default_company.currency_ids])]) res_3 = self.search(cr, uid, [('currency_ids', 'in', [x.id for x in default_company.currency_ids])]) # companies having a currency of main company
res_4 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].id])]) res_4 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].id])]) # companies having first currency of main company
res_5 = self.search(cr, uid, [('currency_ids', 'in', default_company.currency_ids[0].id)]) res_5 = self.search(cr, uid, [('currency_ids', 'in', default_company.currency_ids[0].id)]) # companies having first currency of main company
# res_6 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].name])]) # TODO # res_6 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].name])]) # TODO
res_7 = self.search(cr, uid, [('currency_ids', '=', default_company.currency_ids[0].name)]) res_7 = self.search(cr, uid, [('currency_ids', '=', default_company.currency_ids[0].name)])
res_8 = self.search(cr, uid, [('currency_ids', 'like', default_company.currency_ids[0].name)]) res_8 = self.search(cr, uid, [('currency_ids', 'like', default_company.currency_ids[0].name)])
res_9 = self.search(cr, uid, [('currency_ids', 'like', 'probably_unexisting_name')]) res_9 = self.search(cr, uid, [('currency_ids', 'like', 'probably_unexisting_name')])
# self.search(cr, uid, [('currency_ids', 'unexisting_op', 'probably_unexisting_name')]) # TODO expected exception # self.search(cr, uid, [('currency_ids', 'unexisting_op', 'probably_unexisting_name')]) # TODO expected exception
assert res_1 == [ref('ymltest_company3')]
assert res_2 == [ref('ymltest_company3')]
assert res_3 == [1]
assert res_4 == [1]
assert res_5 == [1]
assert res_7 == [1]
assert res_8 == [1]
assert res_9 == []
# get the companies referenced by some currency (this is normally the main company) # get the companies referenced by some currency (this is normally the main company)
res_10 = self.search(cr, uid, [('currency_ids', 'not like', 'probably_unexisting_name')]) res_10 = self.search(cr, uid, [('currency_ids', 'not like', 'probably_unexisting_name')])
@ -393,15 +400,7 @@
res_11.sort() res_11.sort()
res_12.sort() res_12.sort()
res_13.sort() res_13.sort()
assert res_1 == [ref('ymltest_company3')]
assert res_2 == [ref('ymltest_company3')]
assert res_3 == [1]
assert res_4 == [1]
assert res_5 == [1]
assert res_7 == [1]
assert res_8 == [1]
assert res_9 == []
print ">>> 10:", res_10
print ">>> 11:", res_11 print ">>> 11:", res_11
#assert res_10 == res_11 #assert res_10 == res_11
assert res_10 == res_12 assert res_10 == res_12

View File

@ -75,10 +75,16 @@ domain is not a valid second-level operand.
Unaccent - Accent-insensitive search Unaccent - Accent-insensitive search
OpenERP will use the SQL function 'unaccent' when available for the 'ilike' and OpenERP will use the SQL function 'unaccent' when available for the 'ilike' and
'not ilike' operators. Normally the 'unaccent' function is obtained from the 'not ilike' operators, and enabled in the configuration.
PostgreSQL 'unaccent' contrib module[0]. The steps to install the module might Normally the 'unaccent' function is obtained from the PostgreSQL 'unaccent'
differ on specific PostgreSQL versions. We give here some instruction for contrib module[0].
PostgreSQL 9.x on a Ubuntu system.
..todo: The following explanation should be moved in some external installation
guide
The steps to install the module might differ on specific PostgreSQL versions.
We give here some instruction for PostgreSQL 9.x on a Ubuntu system.
Ubuntu doesn't come yet with PostgreSQL 9.x, so an alternive package source Ubuntu doesn't come yet with PostgreSQL 9.x, so an alternive package source
is used. We use Martin Pitt's PPA available at ppa:pitti/postgresql[1]. See is used. We use Martin Pitt's PPA available at ppa:pitti/postgresql[1]. See
@ -127,6 +133,7 @@ import openerp.modules
NOT_OPERATOR = '!' NOT_OPERATOR = '!'
OR_OPERATOR = '|' OR_OPERATOR = '|'
AND_OPERATOR = '&' AND_OPERATOR = '&'
DOMAIN_OPERATORS = (NOT_OPERATOR, OR_OPERATOR, AND_OPERATOR)
# List of available term operators. It is also possible to use the '<>' # List of available term operators. It is also possible to use the '<>'
# operator, which is strictly the same as '!='; the later should be prefered # operator, which is strictly the same as '!='; the later should be prefered
@ -135,13 +142,15 @@ AND_OPERATOR = '&'
# only one representation). # only one representation).
# An internal (i.e. not available to the user) 'inselect' operator is also # An internal (i.e. not available to the user) 'inselect' operator is also
# used. In this case its right operand has the form (subselect, params). # used. In this case its right operand has the form (subselect, params).
OPS = ('=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in', 'child_of') TERM_OPERATORS = ('=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike',
'like', 'not like', 'ilike', 'not ilike', 'in', 'not in',
'child_of')
# A subset of the above operators, with a 'negative' semantic. When the # A subset of the above operators, with a 'negative' semantic. When the
# expressions 'in NEGATIVE_OPS' or 'not in NEGATIVE_OPS' are used in the code # expressions 'in NEGATIVE_TERM_OPERATORS' or 'not in NEGATIVE_TERM_OPERATORS' are used in the code
# below, this doesn't necessarily mean that any of those NEGATIVE_OPS is # below, this doesn't necessarily mean that any of those NEGATIVE_TERM_OPERATORS is
# legal in the processed term. # legal in the processed term.
NEGATIVE_OPS = ('!=', 'not like', 'not ilike', 'not in') NEGATIVE_TERM_OPERATORS = ('!=', 'not like', 'not ilike', 'not in')
TRUE_LEAF = (1, '=', 1) TRUE_LEAF = (1, '=', 1)
FALSE_LEAF = (0, '=', 1) FALSE_LEAF = (0, '=', 1)
@ -164,7 +173,7 @@ def normalize(domain):
op_arity = {NOT_OPERATOR: 1, AND_OPERATOR: 2, OR_OPERATOR: 2} op_arity = {NOT_OPERATOR: 1, AND_OPERATOR: 2, OR_OPERATOR: 2}
for token in domain: for token in domain:
if expected == 0: # more than expected, like in [A, B] if expected == 0: # more than expected, like in [A, B]
result[0:0] = ['&'] # put an extra '&' in front result[0:0] = [AND_OPERATOR] # put an extra '&' in front
expected = 1 expected = 1
result.append(token) result.append(token)
if isinstance(token, (list, tuple)): # domain term if isinstance(token, (list, tuple)): # domain term
@ -205,16 +214,16 @@ def combine(operator, unit, zero, domains):
return result return result
def AND(domains): def AND(domains):
""" AND([D1,D2,...]) returns a domain representing D1 and D2 and ... """ """AND([D1,D2,...]) returns a domain representing D1 and D2 and ... """
return combine(AND_OPERATOR, TRUE_DOMAIN, FALSE_DOMAIN, domains) return combine(AND_OPERATOR, TRUE_DOMAIN, FALSE_DOMAIN, domains)
def OR(domains): def OR(domains):
""" OR([D1,D2,...]) returns a domain representing D1 or D2 or ... """ """OR([D1,D2,...]) returns a domain representing D1 or D2 or ... """
return combine(OR_OPERATOR, FALSE_DOMAIN, TRUE_DOMAIN, domains) return combine(OR_OPERATOR, FALSE_DOMAIN, TRUE_DOMAIN, domains)
def is_operator(element): def is_operator(element):
""" Test whether an object is a valid domain operator. """ """Test whether an object is a valid domain operator. """
return isinstance(element, (str, unicode)) and element in [AND_OPERATOR, OR_OPERATOR, NOT_OPERATOR] return isinstance(element, basestring) and element in DOMAIN_OPERATORS
# TODO change the share wizard to use this function. # TODO change the share wizard to use this function.
def is_leaf(element, internal=False): def is_leaf(element, internal=False):
@ -222,12 +231,11 @@ def is_leaf(element, internal=False):
:param internal: allow or not the 'inselect' internal operator in the term. :param internal: allow or not the 'inselect' internal operator in the term.
This normally should be always left to False. This normally should be always left to False.
""" """
INTERNAL_OPS = OPS + ('inselect',) INTERNAL_OPS = TERM_OPERATORS + ('inselect',)
return (isinstance(element, tuple) or isinstance(element, list)) \ return (isinstance(element, tuple) or isinstance(element, list)) \
and len(element) == 3 \ and len(element) == 3 \
and (((not internal) and element[1] in OPS + ('<>',)) \ and (((not internal) and element[1] in TERM_OPERATORS + ('<>',)) \
or (internal and element[1] in INTERNAL_OPS + ('<>',))) or (internal and element[1] in INTERNAL_OPS + ('<>',)))
def normalize_leaf(left, operator, right): def normalize_leaf(left, operator, right):
@ -247,7 +255,7 @@ def normalize_leaf(left, operator, right):
return left, operator, right return left, operator, right
def distribute_not(domain): def distribute_not(domain):
""" Distribute the '!' operator on a normalized domain. """ Distribute any '!' domain operators found inside a normalized domain.
Because we don't use SQL semantic for processing a 'left not in right' Because we don't use SQL semantic for processing a 'left not in right'
query (i.e. our 'not in' is not simply translated to a SQL 'not in'), query (i.e. our 'not in' is not simply translated to a SQL 'not in'),
@ -256,11 +264,17 @@ def distribute_not(domain):
the result with 'not (...)', as it would result in a 'not in' at the SQL the result with 'not (...)', as it would result in a 'not in' at the SQL
level. level.
This function is thus responsible for pushing the '!' operator inside the This function is thus responsible for pushing any '!' domain operators
terms. inside the terms themselves. For example::
['!','&',('user_id','=',4),('partner_id','in',[1,2])]
will be turned into:
['|',('user_id','!=',4),('partner_id','not in',[1,2])]
""" """
def negate(leaf): def negate(leaf):
"""Negates and returns a single domain leaf term,
using the opposite operator if possible"""
left, operator, right = leaf left, operator, right = leaf
mapping = { mapping = {
'<': '>=', '<': '>=',
@ -279,44 +293,50 @@ def distribute_not(domain):
if operator in mapping: if operator in mapping:
operator = mapping[operator] operator = mapping[operator]
return [(left, operator, right)] return [(left, operator, right)]
return ['!', (left, operator, right)] return [NOT_OPERATOR, (left, operator, right)]
def distribute(domain): def distribute_negate(domain):
"""Negate the domain ``subtree`` rooted at domain[0],
leaving the rest of the domain intact, and return
(negated_subtree, untouched_domain_rest)
"""
if is_leaf(domain[0]): if is_leaf(domain[0]):
return negate(domain[0]), domain[1:] return negate(domain[0]), domain[1:]
if domain[0] == '&': if domain[0] == AND_OPERATOR:
done1, todo1 = distribute(domain[1:]) done1, todo1 = distribute_negate(domain[1:])
done2, todo2 = distribute(todo1) done2, todo2 = distribute_negate(todo1)
return ['|'] + done1 + done2, todo2 return [OR_OPERATOR] + done1 + done2, todo2
if domain[0] == '|': if domain[0] == OR_OPERATOR:
done1, todo1 = distribute(domain[1:]) done1, todo1 = distribute_negate(domain[1:])
done2, todo2 = distribute(todo1) done2, todo2 = distribute_negate(todo1)
return ['&'] + done1 + done2, todo2 return [AND_OPERATOR] + done1 + done2, todo2
if not domain: if not domain:
return [] return []
if domain[0] != '!': if domain[0] != NOT_OPERATOR:
return [domain[0]] + distribute_not(domain[1:]) return [domain[0]] + distribute_not(domain[1:])
if domain[0] == '!': if domain[0] == NOT_OPERATOR:
done, todo = distribute(domain[1:]) done, todo = distribute_negate(domain[1:])
return done + distribute_not(todo) return done + distribute_not(todo)
def select_from_where(cr, s, f, w, ids, op): def select_from_where(cr, select_field, from_table, where_field, where_ids, where_operator):
# todo: merge into parent query as sub-query # todo: merge into parent query as sub-query
res = [] res = []
if ids: if where_ids:
if op in ['<','>','>=','<=']: if where_operator in ['<','>','>=','<=']:
cr.execute('SELECT "%s" FROM "%s" WHERE "%s" %s %%s' % \ cr.execute('SELECT "%s" FROM "%s" WHERE "%s" %s %%s' % \
(s, f, w, op), (ids[0],)) # TODO shouldn't this be min/max(ids) ? (select_field, from_table, where_field, where_operator),
(where_ids[0],)) # TODO shouldn't this be min/max(where_ids) ?
res = [r[0] for r in cr.fetchall()] res = [r[0] for r in cr.fetchall()]
else: # TODO op is supposed to be 'in'? It is called with child_of... else: # TODO where_operator is supposed to be 'in'? It is called with child_of...
for i in range(0, len(ids), cr.IN_MAX): for i in range(0, len(where_ids), cr.IN_MAX):
subids = ids[i:i+cr.IN_MAX] subids = where_ids[i:i+cr.IN_MAX]
cr.execute('SELECT "%s" FROM "%s" WHERE "%s" IN %%s' % \ cr.execute('SELECT "%s" FROM "%s" WHERE "%s" IN %%s' % \
(s, f, w), (tuple(subids),)) (select_field, from_table, where_field), (tuple(subids),))
res.extend([r[0] for r in cr.fetchall()]) res.extend([r[0] for r in cr.fetchall()])
return res return res
def select_distinct_from_where_not_null(cr, s, f): def select_distinct_from_where_not_null(cr, select_field, from_table):
cr.execute('SELECT distinct("%s") FROM "%s" where "%s" is not null' % (s, f, s)) cr.execute('SELECT distinct("%s") FROM "%s" where "%s" is not null' % \
(select_field, from_table, select_field))
return [r[0] for r in cr.fetchall()] return [r[0] for r in cr.fetchall()]
class expression(object): class expression(object):
@ -342,34 +362,35 @@ class expression(object):
return self.__exp[:] return self.__exp[:]
def parse(self, cr, uid, exp, table, context): def parse(self, cr, uid, exp, table, context):
""" transform the leafs of the expression """ """ transform the leaves of the expression """
self.__exp = exp self.__exp = exp
self.__main_table = table self.__main_table = table
self.__all_tables.add(table) self.__all_tables.add(table)
def child_of_domain(left, right, table, parent=None, prefix=''): def child_of_domain(left, ids, left_model, parent=None, prefix=''):
ids = right """Returns a domain implementing the child_of operator for [(left,child_of,ids)],
if table._parent_store and (not table.pool._init): either as a range using the parent_left/right tree lookup fields (when available),
# TODO: Improve where joins are implemented for many with '.', replace by: or as an expanded [(left,in,child_ids)]"""
# doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)] if left_model._parent_store and (not left_model.pool._init):
# TODO: Improve where joins are implemented for many with '.', replace by:
# doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)]
doms = [] doms = []
for o in table.browse(cr, uid, ids, context=context): for o in left_model.browse(cr, uid, ids, context=context):
if doms: if doms:
doms.insert(0, OR_OPERATOR) doms.insert(0, OR_OPERATOR)
doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)] doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)]
if prefix: if prefix:
return [(left, 'in', table.search(cr, uid, doms, context=context))] return [(left, 'in', left_model.search(cr, uid, doms, context=context))]
return doms return doms
else: else:
def rg(ids, table, parent): def recursive_children(ids, model, parent_field):
if not ids: if not ids:
return [] return []
ids2 = table.search(cr, uid, [(parent, 'in', ids)], context=context) ids2 = model.search(cr, uid, [(parent_field, 'in', ids)], context=context)
return ids + rg(ids2, table, parent) return ids + recursive_children(ids2, model, parent_field)
return [(left, 'in', rg(ids, table, parent or table._parent_name))] return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))]
# TODO rename this function as it is not strictly for 'child_of', but also for 'in'... def to_ids(value, field_obj):
def child_of_right_to_ids(value, field_obj):
""" Normalize a single id, or a string, or a list of ids to a list of ids. """ Normalize a single id, or a string, or a list of ids to a list of ids.
""" """
if isinstance(value, basestring): if isinstance(value, basestring):
@ -417,7 +438,7 @@ class expression(object):
if not field: if not field:
if left == 'id' and operator == 'child_of': if left == 'id' and operator == 'child_of':
ids2 = child_of_right_to_ids(right, table) ids2 = to_ids(right, table)
dom = child_of_domain(left, ids2, working_table) dom = child_of_domain(left, ids2, working_table)
self.__exp = self.__exp[:i] + dom + self.__exp[i+1:] self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
continue continue
@ -458,11 +479,10 @@ class expression(object):
elif field._type == 'one2many': elif field._type == 'one2many':
# Applying recursivity on field(one2many) # Applying recursivity on field(one2many)
if operator == 'child_of': if operator == 'child_of':
ids2 = to_ids(right, field_obj)
if field._obj != working_table._name: if field._obj != working_table._name:
ids2 = child_of_right_to_ids(right, field_obj)
dom = child_of_domain(left, ids2, field_obj, prefix=field._obj) dom = child_of_domain(left, ids2, field_obj, prefix=field._obj)
else: else:
ids2 = child_of_right_to_ids(right, field_obj)
dom = child_of_domain('id', ids2, working_table, parent=left) dom = child_of_domain('id', ids2, working_table, parent=left)
self.__exp = self.__exp[:i] + dom + self.__exp[i+1:] self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
@ -486,11 +506,11 @@ class expression(object):
self.__exp[i] = FALSE_LEAF self.__exp[i] = FALSE_LEAF
else: else:
call_null = False call_null = False
o2m_op = 'not in' if operator in NEGATIVE_OPS else 'in' o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
self.__exp[i] = ('id', o2m_op, select_from_where(cr, field._fields_id, field_obj._table, 'id', ids2, operator)) self.__exp[i] = ('id', o2m_op, select_from_where(cr, field._fields_id, field_obj._table, 'id', ids2, operator))
if call_null: if call_null:
o2m_op = 'in' if operator in NEGATIVE_OPS else 'not in' o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
self.__exp[i] = ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, field_obj._table)) self.__exp[i] = ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, field_obj._table))
elif field._type == 'many2many': elif field._type == 'many2many':
@ -501,7 +521,7 @@ class expression(object):
return ids return ids
return select_from_where(cr, field._id1, field._rel, field._id2, ids, operator) return select_from_where(cr, field._id1, field._rel, field._id2, ids, operator)
ids2 = child_of_right_to_ids(right, field_obj) ids2 = to_ids(right, field_obj)
dom = child_of_domain('id', ids2, field_obj) dom = child_of_domain('id', ids2, field_obj)
ids2 = field_obj.search(cr, uid, dom, context=context) ids2 = field_obj.search(cr, uid, dom, context=context)
self.__exp[i] = ('id', 'in', _rec_convert(ids2)) self.__exp[i] = ('id', 'in', _rec_convert(ids2))
@ -526,16 +546,16 @@ class expression(object):
operator = 'in' # operator changed because ids are directly related to main object operator = 'in' # operator changed because ids are directly related to main object
else: else:
call_null_m2m = False call_null_m2m = False
m2m_op = 'not in' if operator in NEGATIVE_OPS else 'in' m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
self.__exp[i] = ('id', m2m_op, select_from_where(cr, field._id1, field._rel, field._id2, res_ids, operator) or [0]) self.__exp[i] = ('id', m2m_op, select_from_where(cr, field._id1, field._rel, field._id2, res_ids, operator) or [0])
if call_null_m2m: if call_null_m2m:
m2m_op = 'in' if operator in NEGATIVE_OPS else 'not in' m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
self.__exp[i] = ('id', m2m_op, select_distinct_from_where_not_null(cr, field._id1, field._rel)) self.__exp[i] = ('id', m2m_op, select_distinct_from_where_not_null(cr, field._id1, field._rel))
elif field._type == 'many2one': elif field._type == 'many2one':
if operator == 'child_of': if operator == 'child_of':
ids2 = child_of_right_to_ids(right, field_obj) ids2 = to_ids(right, field_obj)
if field._obj != working_table._name: if field._obj != working_table._name:
dom = child_of_domain(left, ids2, field_obj, prefix=field._obj) dom = child_of_domain(left, ids2, field_obj, prefix=field._obj)
else: else:
@ -558,7 +578,7 @@ class expression(object):
elif isinstance(right, list) and operator in ['!=','=']: #for domain (FIELD,'=',['value1','value2']) elif isinstance(right, list) and operator in ['!=','=']: #for domain (FIELD,'=',['value1','value2'])
operator = dict_op[operator] operator = dict_op[operator]
res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None, context=c)] res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None, context=c)]
if operator in NEGATIVE_OPS: if operator in NEGATIVE_TERM_OPERATORS:
res_ids.append(False) # TODO this should not be appended if False was in 'right' res_ids.append(False) # TODO this should not be appended if False was in 'right'
return (left, 'in', res_ids) return (left, 'in', res_ids)