Merge lp:~openerp-community/openobject-server/trunk-m2m-e2e into lp:openobject-server

Proposed by Nicolas DS
Status: Rejected
Rejected by: Olivier Dony (Odoo)
Proposed branch: lp:~openerp-community/openobject-server/trunk-m2m-e2e
Merge into: lp:openobject-server
Diff against target: 180 lines (+74/-29)
3 files modified
bin/osv/expression.py (+9/-4)
bin/osv/fields.py (+63/-23)
bin/osv/orm.py (+2/-2)
To merge this branch: bzr merge lp:~openerp-community/openobject-server/trunk-m2m-e2e
Reviewer Review Type Date Requested Status
Olivier Dony (Odoo) Disapprove
Mantavya Gajjar (Open ERP) (community) test module Needs Information
Stephane Wirtel (OpenERP) Pending
Review via email: mp+15187@code.launchpad.net

Description of the change

Add to ability of a many2many relation to link the same field.
It's a kind of equal2equal relation or a many2many bidirectional.

See blue print related to this branch.
https://blueprints.launchpad.net/openobject-server/+spec/many2many-equal2equal

To post a comment you must log in.
Revision history for this message
Nicolas DS (zyphos) wrote :

Add to ability of a many2many relation to link the same field.
It's a kind of equal2equal relation or a many2many bidirectional.

See blue print related to this branch.
https://blueprints.launchpad.net/openobject-server/+spec/many2many-equal2equal

1892. By Nicolas DS

[IMP] Removing #todo comment

1893. By Nicolas DS

[MERGE] from trunk

Revision history for this message
Mantavya Gajjar (Open ERP) (mga) wrote :

Hello Nicolas !

I see your development. it quite impressive but at first looks, I found there are some conflicts test appears in to the code. so it not seem a clean code

can you please make it clean so that, I can check and merge in to trunk.

Thanks
mga

review: Needs Fixing (code)
Revision history for this message
Mantavya Gajjar (Open ERP) (mga) wrote :

Hello Nicolas !

can you please send me a code to test in your way.

thanks
mga

review: Needs Information (test module)
1894. By Nicolas DS

[MERGE] merge 1 with trunk

1895. By Nicolas DS

[MERGE] Merge 2 with trunk

1896. By Nicolas DS

[FIX] M2M Equal2equal, bad fix in merge

1897. By Nicolas DS

[FIX] Bad bzr auto merge ???

1898. By Nicolas DS

[FIX] E2E implementation rewrite error

Revision history for this message
Nicolas DS (zyphos) wrote :

Code has been fixed according to the new server trunk code.
(The multi company handling broke my old code, Revision ID: <email address hidden>)

A test module is available at:
http://zyphos.be/openobject/e2etest.zip

Regards

1899. By Nicolas DS

[MERGE] from trunk

1900. By Nicolas DS

[FIX] Expression.py changes were lost in branch upgrade

1901. By Nicolas DS

[MERGE] from trunk

1902. By ScottP

Fixed minor typos

1903. By Nicolas DS

[REVERT] Reverting ScottP bad commit

1904. By Nicolas DS

[FIX] Minor typos (ScottP)

1905. By Nicolas DS

[MERGE] from official trunk

1906. By Nicolas DS

[FIX] Count has been added in trunk too since last merge

1907. By Nicolas DS

[MERGE] from official trunk

1908. By Nicolas DS

[IMP] Use new type of exception raising

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

Hello,

Neither the blueprint nor the code or the description of the merge proposal make very clear what you are trying to achieve. Specifically, a relationship never connects "fields" but records or objects, so it is not clear what you mean with "a many2many relation to link the same field".

Please provide a simple example of what you want to do. (what is your use case?)

As a last note, using the 'inspect' module to implement it seems like a very ugly hack, are you sure you need to do this?

review: Needs Information
Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

On second thought, the implementation seems a bit awkward and it seems to me that it would make m2m harder to understand and use.
Perhaps it could work better as a new type of relationship (even if it shares the same widget), if the use case is valid enough?
In any case, it cannot be merged in the current state, but you could consider providing an updated proposal with more explanations on the use case, for after v6.

Thank you!

review: Disapprove

Unmerged revisions

1908. By Nicolas DS

[IMP] Use new type of exception raising

1907. By Nicolas DS

[MERGE] from official trunk

1906. By Nicolas DS

[FIX] Count has been added in trunk too since last merge

1905. By Nicolas DS

[MERGE] from official trunk

1904. By Nicolas DS

[FIX] Minor typos (ScottP)

1903. By Nicolas DS

[REVERT] Reverting ScottP bad commit

1902. By ScottP

Fixed minor typos

1901. By Nicolas DS

[MERGE] from trunk

1900. By Nicolas DS

[FIX] Expression.py changes were lost in branch upgrade

1899. By Nicolas DS

[MERGE] from trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/osv/expression.py'
2--- bin/osv/expression.py 2010-06-24 15:56:02 +0000
3+++ bin/osv/expression.py 2010-07-05 08:20:43 +0000
4@@ -214,6 +214,11 @@
5 self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', [], operator, field._type) or [0])
6
7 elif field._type == 'many2many':
8+ def __execute_recursive_in_m2m(cr, field, ids, operator):
9+ new_ids = self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, ids, operator, field._type)
10+ if field._equal2equal:
11+ new_ids.extend(self.__execute_recursive_in(cr, field._id2, field._rel, field._id1, ids, operator, field._type))
12+ return new_ids
13 #FIXME
14 if operator == 'child_of':
15 if isinstance(right, basestring):
16@@ -224,7 +229,7 @@
17 def _rec_convert(ids):
18 if field_obj == table:
19 return ids
20- return self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, ids, operator, field._type)
21+ return __execute_recursive_in_m2m(cr, field, ids, operator)
22
23 dom = _rec_get(ids2, field_obj)
24 ids2 = field_obj.search(cr, uid, dom, context=context)
25@@ -255,12 +260,12 @@
26 if operator in ['not like','not ilike','not in','<>','!=']:
27 m2m_op = 'not in'
28
29- self.__exp[i] = ('id', m2m_op, self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, res_ids, operator, field._type) or [0])
30+ self.__exp[i] = ('id', m2m_op, __execute_recursive_in_m2m(cr, field, res_ids, operator) or [0])
31 if call_null_m2m:
32 m2m_op = 'not in'
33 if operator in ['not like','not ilike','not in','<>','!=']:
34- m2m_op = 'in'
35- self.__exp[i] = ('id', m2m_op, self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, [], operator, field._type) or [0])
36+ m2m_op = 'in'
37+ self.__exp[i] = ('id', m2m_op, __execute_recursive_in_m2m(cr, field, [], operator) or [0])
38
39 elif field._type == 'many2one':
40 if operator == 'child_of':
41
42=== modified file 'bin/osv/fields.py'
43--- bin/osv/fields.py 2010-06-25 15:03:40 +0000
44+++ bin/osv/fields.py 2010-07-05 08:20:43 +0000
45@@ -492,7 +492,21 @@
46 _classic_write = False
47 _prefetch = False
48 _type = 'many2many'
49+ _equal2equal = False
50 def __init__(self, obj, rel, id1, id2, string='unknown', limit=None, **args):
51+ # Check if it's a equal2equal, retrieve parent object
52+ parent_table = ''
53+ import inspect
54+ parent_frame = inspect.stack()[1][0]
55+ if '_name' in parent_frame.f_locals:
56+ parent_obj = parent_frame.f_locals['_name']
57+ elif '_inherit' in parent_frame.f_locals:
58+ parent_obj = parent_frame.f_locals['_inherit']
59+ if (parent_obj == obj) and (id1 == id2):
60+ self._equal2equal = True
61+ id1 += '1'
62+ id2 += '2'
63+
64 _column.__init__(self, string=string, **args)
65 self._obj = obj
66 if '.' in rel:
67@@ -520,26 +534,35 @@
68 if d1:
69 d1 = ' and ' + ' and '.join(d1)
70 else: d1 = ''
71- query = 'SELECT %(rel)s.%(id2)s, %(rel)s.%(id1)s \
72- FROM %(rel)s, %(tbl)s \
73- WHERE %(rel)s.%(id1)s in %%s \
74- AND %(rel)s.%(id2)s = %(tbl)s.id \
75- %(d1)s \
76- %(limit)s \
77- ORDER BY %(tbl)s.%(order)s \
78- OFFSET %(offset)d' \
79- % {'rel': self._rel,
80- 'tbl': obj._table,
81- 'id1': self._id1,
82- 'id2': self._id2,
83- 'd1': d1,
84- 'limit': limit_str,
85- 'order': obj._order,
86- 'offset': offset,
87- }
88- cr.execute(query, [tuple(ids)] + d2)
89- for r in cr.fetchall():
90- res[r[1]].append(r[0])
91+
92+ def _get_res(column1, column2):
93+ query = 'SELECT %(rel)s.%(id2)s, %(rel)s.%(id1)s \
94+ FROM %(rel)s, %(tbl)s \
95+ WHERE %(rel)s.%(id1)s in %%s \
96+ AND %(rel)s.%(id2)s = %(tbl)s.id \
97+ %(d1)s \
98+ %(limit)s \
99+ ORDER BY %(tbl)s.%(order)s \
100+ OFFSET %(offset)d' \
101+ % {'rel': self._rel,
102+ 'tbl': obj._table,
103+ 'id1': column1,
104+ 'id2': column2,
105+ 'd1': d1,
106+ 'limit': limit_str,
107+ 'order': obj._order,
108+ 'offset': offset,
109+ }
110+ cr.execute(query, [tuple(ids)] + d2)
111+ for r in cr.fetchall():
112+ if r[1] in ids:
113+ res[r[1]].append(r[0])
114+ if self._equal2equal:
115+ if r[0] in ids:
116+ res[r[0]].append(r[1])
117+ _get_res(self._id1, self._id2)
118+ if self._equal2equal:
119+ _get_res(self._id2, self._id1)
120 return res
121
122 def set(self, cr, obj, id, name, values, user=None, context=None):
123@@ -559,11 +582,20 @@
124 elif act[0] == 2:
125 obj.unlink(cr, user, [act[1]], context=context)
126 elif act[0] == 3:
127- cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%s and '+ self._id2 + '=%s', (id, act[1]))
128+ sql = 'delete from '+self._rel+' where (' + self._id1 + '=%s and '+ self._id2 + '=%s)'
129+ if self._equal2equal:
130+ sql += 'OR (' + self._id2 + '=%s and '+ self._id1 + '=%s)'
131+ cr.execute(sql, (id, act[1], id, act[1]))
132+ else:
133+ cr.execute(sql, (id, act[1]))
134 elif act[0] == 4:
135+ if self._equal2equal and id == act[1]:
136+ raise Exception(_('Could not link a record to itself.')) # Avoid recursion
137 cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s,%s)', (id, act[1]))
138 elif act[0] == 5:
139 cr.execute('update '+self._rel+' set '+self._id2+'=null where '+self._id2+'=%s', (id,))
140+ if self._equal2equal:
141+ cr.execute('update '+self._rel+' set '+self._id1+'=null where '+self._id1+'=%s', (id,))
142 elif act[0] == 6:
143
144 d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context)
145@@ -571,10 +603,18 @@
146 d1 = ' and ' + ' and '.join(d1)
147 else:
148 d1 = ''
149- cr.execute('delete from '+self._rel+' where '+self._id1+'=%s AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+','.join(tables)+' WHERE '+self._rel+'.'+self._id1+'=%s AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2)
150+
151+ def _del_rel(column1, column2):
152+ cr.execute('delete from '+self._rel+' where '+column1+'=%s AND '+column2+' IN (SELECT '+self._rel+'.'+column2+' FROM '+self._rel+', '+','.join(tables)+' WHERE '+self._rel+'.'+column1+'=%s AND '+self._rel+'.'+column2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2)
153+
154+ _del_rel(self._id1, self._id2)
155+ if self._equal2equal:
156+ _del_rel(self._id2, self._id1)
157
158 for act_nbr in act[2]:
159- cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s, %s)', (id, act_nbr))
160+ if not self._equal2equal or id != act_nbr:
161+ # Equal2equal can't be linked to himself
162+ cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s, %s)', (id, act_nbr))
163
164 #
165 # TODO: use a name_search
166
167=== modified file 'bin/osv/orm.py'
168--- bin/osv/orm.py 2010-07-04 12:57:27 +0000
169+++ bin/osv/orm.py 2010-07-05 08:20:43 +0000
170@@ -27,8 +27,8 @@
171 # . Optimised processing by complex query (multiple actions at once)
172 # . Default fields value
173 # . Permissions optimisation
174-# . Persistant object: DB postgresql
175-# . Datas conversions
176+# . Persistent object: DB postgresql
177+# . Data conversions
178 # . Multi-level caching system
179 # . 2 different inheritancies
180 # . Fields: