← Back to team overview

openerp-expert-framework team mailing list archive

[PATCH] Feature: operator "|child_of"

 

In some rules, we want to limit
  (field_id == NULL) or ( field_id child_of Sth)
but it is more convenient to have a compact operator for that.
---
 bin/osv/expression.py |   52 +++++++++++++++++++++++++++++++++----------------
 1 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/bin/osv/expression.py b/bin/osv/expression.py
index 38a3658..2fd02a0 100644
--- a/bin/osv/expression.py
+++ b/bin/osv/expression.py
@@ -53,7 +53,10 @@ class expression(object):
         return isinstance(element, (str, unicode)) and element in ['&', '|', '!']
 
     def _is_leaf(self, element, internal=False):
-        OPS = ('=', '!=', '<>', '<=', '<', '>', '>=', '=?', '=like', '=ilike', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in', 
'child_of')
+        OPS = ('=', '!=', '<>', '<=', '<', '>', '>=', '=?', 
+                '=like', '=ilike', 'like', 'not like', 'ilike', 'not ilike', 
+                'in', 'not in',
+                'child_of', '|child_of' )
         INTERNAL_OPS = OPS + ('inselect', 'not inselect')
         return (isinstance(element, tuple) or isinstance(element, list)) \
            and len(element) == 3 \
@@ -135,7 +138,12 @@ class expression(object):
         if not self.__exp:
             return self
 
-        def _rec_get(ids, table, parent=None, left='id', prefix=''):
+        def _rec_get(ids, table, parent=None, left='id', prefix='', null_too=False):
+            """ Compute the sub-expression for a recursive field's operator, 
+                typically 'child_of'
+                
+                If null_too is specified, the expression would also stand for left = NULL
+            """
             if table._parent_store and (not table.pool._init): #and False:
 # TODO: Improve where joins are implemented for many with '.', replace by:
 # doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)]
@@ -145,7 +153,11 @@ class expression(object):
                         doms.insert(0, '|')
                     doms += ['&', ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)]
                 if prefix:
+                    if null_too:
+                        return ['|', (left, '=', False), (left, 'in', table.search(cr, uid, doms, context=context))]
                     return [(left, 'in', table.search(cr, uid, doms, context=context))]
+                if null_too:
+                    doms = ['|', (left, '=', False)] + doms
                 return doms
             elif self.__mode == 'pg84':
                 # print "Recursive expand for 8.4, for %s" % table._table
@@ -178,6 +190,8 @@ class expression(object):
                 
                 # print "INSELECT %s" % qry
                 # print "args:", qu2
+                if null_too:
+                    return ['|', (left, '=', False), (left, 'inselect', (qry, qu2))]
                 return [(left, 'inselect', (qry, qu2))]
             # elif self.__mode == 'pgsql':
             #  any way  to do that in pg8.3?
@@ -187,7 +201,12 @@ class expression(object):
                         return []
                     ids2 = table.search(cr, uid, [(parent, 'in', ids)], context=context)
                     return ids + rg(ids2, table, parent)
-                return [(left, 'in', rg(ids, table, parent or table._parent_name))]
+                if null_too:
+                    res = ['|', (left, '=', False)]
+                else:
+                    res = []
+                res += [(left, 'in', rg(ids, table, parent or table._parent_name))]
+                return res
 
         self.__main_table = table
 
@@ -218,8 +237,8 @@ class expression(object):
             
             field = working_table._columns.get(fargs[0], False)
             if not field:
-                if left == 'id' and operator == 'child_of':
-                    dom = _rec_get(right, working_table)
+                if left == 'id' and (operator == 'child_of' or operator == '|child_of'):
+                    dom = _rec_get(right, working_table, null_too=(operator == '|child_of'))
                     self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
                 continue
 
@@ -253,16 +272,16 @@ class expression(object):
 
             elif field._type == 'one2many':
                 # Applying recursivity on field(one2many)
-                if operator == 'child_of':
-                    # TODO: pg8.4 extension
+                if operator == 'child_of' or operator == '|child_of':
                     if isinstance(right, basestring):
                         ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], 'like', context=context, limit=None)]
                     else:
                         ids2 = list(right)
+                    null_too = (operator == '|child_of')
                     if field._obj != working_table._name:
-                        dom = _rec_get(ids2, field_obj, left=left, prefix=field._obj)
+                        dom = _rec_get(ids2, field_obj, left=left, prefix=field._obj, null_too=null_too)
                     else:
-                        dom = _rec_get(ids2, working_table, parent=left)
+                        dom = _rec_get(ids2, working_table, parent=left, null_too=null_too)
                     self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
                 
                 else:
@@ -324,8 +343,7 @@ class expression(object):
 
             elif field._type == 'many2many':
                 #FIXME
-                if operator == 'child_of':
-		    # TODO: pg8.4 extension
+                if operator == 'child_of' or operator == '|child_of':
                     if isinstance(right, basestring):
                         ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], 'like', context=context, limit=None)]
                     else:
@@ -338,7 +356,7 @@ class expression(object):
                         assert(not erqu) # TODO
                         return erpa
 
-                    dom = _rec_get(ids2, field_obj)
+                    dom = _rec_get(ids2, field_obj, null_too=(operator == '|child_of'))
                     ids2 = field_obj.search(cr, uid, dom, context=context)
                     self.__exp[i] = ('id', 'in', _rec_convert(ids2))
                 else:
@@ -421,8 +439,7 @@ class expression(object):
 
                     self.__exp[i] = (left,'inselect', (qry, qu2))
                     print "Nested query now is like:", self.__exp[i]
-                elif operator == 'child_of':
-		    # TODO: pg8.4 extension
+                elif operator == 'child_of' or operator == '|child_of' :
                     if isinstance(right, basestring):
                         ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], 'like', limit=None)]
                     elif isinstance(right, (int, long)):
@@ -431,10 +448,11 @@ class expression(object):
                         ids2 = list(right)
 
                     self.__operator = 'in'
+                    null_too = (operator == '|child_of')
                     if field._obj != working_table._name:
-                        dom = _rec_get(ids2, field_obj, left=left, prefix=field._obj)
+                        dom = _rec_get(ids2, field_obj, left=left, prefix=field._obj, null_too=null_too)
                     else:
-                        dom = _rec_get(ids2, working_table, parent=left)
+                        dom = _rec_get(ids2, working_table, parent=left, null_too=null_too)
                     self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
                 else:
                     if isinstance(right, basestring): # and not isinstance(field, fields.related):
@@ -554,7 +572,7 @@ class expression(object):
                         query = "(%s.%s %s '%%s')" % (table._table, left, op)
                         params = right
 
-            elif (operator == 'child_of'):
+            elif (operator == 'child_of' or operator == '|child_of'):
                 raise Exception("Cannot compute %s %s %s in sql" %(left, operator, right))
             else:
                 if isinstance(right, placeholder):
-- 
1.6.4.4