Merge lp:~therp-nl/openobject-server/ronald@therp.nl_trunk_lp645980 into lp:openobject-server

Proposed by Ronald Portier (Therp)
Status: Needs review
Proposed branch: lp:~therp-nl/openobject-server/ronald@therp.nl_trunk_lp645980
Merge into: lp:openobject-server
Diff against target: 152 lines (+109/-1)
2 files modified
openerp/addons/base/ir/ir_translation.py (+101/-0)
openerp/osv/orm.py (+8/-1)
To merge this branch: bzr merge lp:~therp-nl/openobject-server/ronald@therp.nl_trunk_lp645980
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+132095@code.launchpad.net

Description of the change

This merge request contains the minimal changes to just correct the ordering of rows ordered on a translated field.

Did not (as yet) change/correct the selection, nor streamlined the reading of translated fields, just to keep this merge request as compact as possible.

To post a comment you must log in.

Unmerged revisions

4509. By Ronald Portier (Therp)

[FIX] Rows ordered by translated fields are shown in the correct order.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openerp/addons/base/ir/ir_translation.py'
2--- openerp/addons/base/ir/ir_translation.py 2012-10-22 13:27:59 +0000
3+++ openerp/addons/base/ir/ir_translation.py 2012-10-30 13:29:28 +0000
4@@ -18,6 +18,7 @@
5 # along with this program. If not, see <http://www.gnu.org/licenses/>.
6 #
7 ##############################################################################
8+import re
9
10 import tools
11 import logging
12@@ -147,6 +148,11 @@
13 class ir_translation(osv.osv):
14 _name = "ir.translation"
15 _log_access = False
16+
17+ _clause_pattern = re.compile(
18+ r'\b(order by|select)?\b(.*$)', re.IGNORECASE)
19+ _field_pattern = re.compile(
20+ r'\b([\"|\w]+\.)?([\"|\w]+)\W*\b(asc|desc)?', re.IGNORECASE)
21
22 def _get_language(self, cr, uid, context):
23 lang_model = self.pool.get('res.lang')
24@@ -411,6 +417,101 @@
25 elif lang_code != 'en_US':
26 _logger.warning('module %s: no translation for language %s', module_name, lang_code)
27 return True
28+
29+ def check_translation_existing(self, cr, user, modelname, field, language):
30+ '''Check wether any translation for field existing'''
31+ statement = '''select count(*) from ir_translation
32+ where lang = '%(language)s'
33+ and type = 'model'
34+ and name = '%(modelname)s,%(field)s'
35+ ''' % {
36+ 'language': language,
37+ 'modelname': modelname,
38+ 'field': field}
39+ cr.execute(statement)
40+ res = cr.fetchall()
41+ itr_count = res[0][0]
42+ if itr_count:
43+ return True
44+ return False
45+
46+ def _compose_translation_join(
47+ self, cr, user, modelname, tablename, field, context=None):
48+ if not context:
49+ context = {}
50+ replace_dict = {}
51+ replace_dict['contextlang'] = context.get('lang', 'en_US')
52+ replace_dict['tablename'] = tablename
53+ replace_dict['field'] = field
54+ replace_dict['modelname'] = modelname
55+ # use q_ for quoted stuff
56+ replace_dict['q_tablename'] = '"%(tablename)s"' % replace_dict
57+ replace_dict['q_joinname'] = '"itr_%(field)s"' % replace_dict
58+ # split followed by join removes unneeded whitespace
59+ new_join = (' '.join((
60+ '''LEFT OUTER JOIN ir_translation as %(q_joinname)s
61+ ON (%(q_joinname)s.res_id = %(q_tablename)s.id
62+ AND %(q_joinname)s.name = '%(modelname)s,%(field)s'
63+ AND %(q_joinname)s.lang = '%(contextlang)s'
64+ AND %(q_joinname)s.type = 'model')''' % replace_dict).split()))
65+ new_field = (
66+ 'coalesce(%(q_joinname)s.value, %(q_tablename)s.%(field)s)' %
67+ replace_dict)
68+ return (new_join, new_field)
69+
70+ def _apply_translation_2fields(
71+ self, cr, user, modelname, tablename, sql_clause, context=None):
72+ '''Scan all fields in sql order_by or select clause for translatable
73+ fields. If found, determine joins needed to translate them, and
74+ replace the fields in the clause by their translated equivalents.'''
75+ assert modelname and tablename # must be filled
76+ translation_joins = []
77+ if (not context) or (not sql_clause):
78+ # no context, means no translation, so nothing to do
79+ return (translation_joins, sql_clause)
80+ # check for default language, which does not need translation
81+ contextlang = context.get('lang', 'en_US')
82+ if contextlang == 'en_US':
83+ return (translation_joins, sql_clause)
84+ # Work to do. Start by splitting fieldlist from optional keyword
85+ clause_parts = self._clause_pattern.search(sql_clause).groups()
86+ clause_keyword = clause_parts[0] or ''
87+ field_list = clause_parts[1].strip() or ''
88+ if not field_list:
89+ # We only had a keyword, so nothing to translate
90+ return (translation_joins, sql_clause)
91+ # Now set up field array to loop over
92+ fields = field_list.split(',')
93+ sql_clause_parts = []
94+ for field in fields:
95+ # default just replace existing field
96+ replacing_field = field
97+ # get rid of field qualifier and asc en desc field.trans
98+ field_parts = self._field_pattern.search(field).groups()
99+ if field_parts:
100+ field_pre = field_parts[0] or ''
101+ field_bare = field_parts[1] or ''
102+ field_post = field_parts[2] or ''
103+ if field_bare:
104+ # check wether translatable
105+ if self.check_translation_existing(
106+ cr, user, modelname, field_bare, contextlang):
107+ # join to translation table
108+ # and replace field by translated version
109+ new_join, new_field = (
110+ self._compose_translation_join(
111+ cr, user, modelname, tablename,
112+ field_bare, context))
113+ translation_joins.append(new_join)
114+ field_bare = new_field
115+ replacing_field = (
116+ field_pre + new_field + ' ' + field_post)
117+ sql_clause_parts.append(replacing_field)
118+ if translation_joins:
119+ # We have one or more translations, recompose sql_clause clause
120+ sql_clause = clause_keyword + ' ' + ','.join(sql_clause_parts)
121+ # return extra joins needed for translation, and modified clause
122+ return (translation_joins, sql_clause)
123
124
125 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
126
127=== modified file 'openerp/osv/orm.py'
128--- openerp/osv/orm.py 2012-10-29 18:07:42 +0000
129+++ openerp/osv/orm.py 2012-10-30 13:29:28 +0000
130@@ -4752,7 +4752,7 @@
131 order_by_clause = ",".join(order_by_elements)
132
133 return order_by_clause and (' ORDER BY %s ' % order_by_clause) or ''
134-
135+
136 def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None):
137 """
138 Private implementation of search() method, allowing specifying the uid to use for the access right check.
139@@ -4784,6 +4784,13 @@
140 cr.execute('SELECT count("%s".id) FROM ' % self._table + from_clause + where_str + limit_str + offset_str, where_clause_params)
141 res = cr.fetchall()
142 return res[0][0]
143+ itr_obj = self.pool.get('ir.translation')
144+ translation_joins, modified_order_by = (
145+ itr_obj._apply_translation_2fields(
146+ cr, user, self._name, self._table, order_by, context))
147+ if translation_joins:
148+ from_clause = from_clause + ' ' + ' '.join(translation_joins)
149+ order_by = modified_order_by
150 cr.execute('SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str, where_clause_params)
151 res = cr.fetchall()
152 return [x[0] for x in res]