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
=== modified file 'openerp/addons/base/ir/ir_translation.py'
--- openerp/addons/base/ir/ir_translation.py 2012-10-22 13:27:59 +0000
+++ openerp/addons/base/ir/ir_translation.py 2012-10-30 13:29:28 +0000
@@ -18,6 +18,7 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#19#
20##############################################################################20##############################################################################
21import re
2122
22import tools23import tools
23import logging24import logging
@@ -147,6 +148,11 @@
147class ir_translation(osv.osv):148class ir_translation(osv.osv):
148 _name = "ir.translation"149 _name = "ir.translation"
149 _log_access = False150 _log_access = False
151
152 _clause_pattern = re.compile(
153 r'\b(order by|select)?\b(.*$)', re.IGNORECASE)
154 _field_pattern = re.compile(
155 r'\b([\"|\w]+\.)?([\"|\w]+)\W*\b(asc|desc)?', re.IGNORECASE)
150156
151 def _get_language(self, cr, uid, context):157 def _get_language(self, cr, uid, context):
152 lang_model = self.pool.get('res.lang')158 lang_model = self.pool.get('res.lang')
@@ -411,6 +417,101 @@
411 elif lang_code != 'en_US':417 elif lang_code != 'en_US':
412 _logger.warning('module %s: no translation for language %s', module_name, lang_code)418 _logger.warning('module %s: no translation for language %s', module_name, lang_code)
413 return True419 return True
420
421 def check_translation_existing(self, cr, user, modelname, field, language):
422 '''Check wether any translation for field existing'''
423 statement = '''select count(*) from ir_translation
424 where lang = '%(language)s'
425 and type = 'model'
426 and name = '%(modelname)s,%(field)s'
427 ''' % {
428 'language': language,
429 'modelname': modelname,
430 'field': field}
431 cr.execute(statement)
432 res = cr.fetchall()
433 itr_count = res[0][0]
434 if itr_count:
435 return True
436 return False
437
438 def _compose_translation_join(
439 self, cr, user, modelname, tablename, field, context=None):
440 if not context:
441 context = {}
442 replace_dict = {}
443 replace_dict['contextlang'] = context.get('lang', 'en_US')
444 replace_dict['tablename'] = tablename
445 replace_dict['field'] = field
446 replace_dict['modelname'] = modelname
447 # use q_ for quoted stuff
448 replace_dict['q_tablename'] = '"%(tablename)s"' % replace_dict
449 replace_dict['q_joinname'] = '"itr_%(field)s"' % replace_dict
450 # split followed by join removes unneeded whitespace
451 new_join = (' '.join((
452 '''LEFT OUTER JOIN ir_translation as %(q_joinname)s
453 ON (%(q_joinname)s.res_id = %(q_tablename)s.id
454 AND %(q_joinname)s.name = '%(modelname)s,%(field)s'
455 AND %(q_joinname)s.lang = '%(contextlang)s'
456 AND %(q_joinname)s.type = 'model')''' % replace_dict).split()))
457 new_field = (
458 'coalesce(%(q_joinname)s.value, %(q_tablename)s.%(field)s)' %
459 replace_dict)
460 return (new_join, new_field)
461
462 def _apply_translation_2fields(
463 self, cr, user, modelname, tablename, sql_clause, context=None):
464 '''Scan all fields in sql order_by or select clause for translatable
465 fields. If found, determine joins needed to translate them, and
466 replace the fields in the clause by their translated equivalents.'''
467 assert modelname and tablename # must be filled
468 translation_joins = []
469 if (not context) or (not sql_clause):
470 # no context, means no translation, so nothing to do
471 return (translation_joins, sql_clause)
472 # check for default language, which does not need translation
473 contextlang = context.get('lang', 'en_US')
474 if contextlang == 'en_US':
475 return (translation_joins, sql_clause)
476 # Work to do. Start by splitting fieldlist from optional keyword
477 clause_parts = self._clause_pattern.search(sql_clause).groups()
478 clause_keyword = clause_parts[0] or ''
479 field_list = clause_parts[1].strip() or ''
480 if not field_list:
481 # We only had a keyword, so nothing to translate
482 return (translation_joins, sql_clause)
483 # Now set up field array to loop over
484 fields = field_list.split(',')
485 sql_clause_parts = []
486 for field in fields:
487 # default just replace existing field
488 replacing_field = field
489 # get rid of field qualifier and asc en desc field.trans
490 field_parts = self._field_pattern.search(field).groups()
491 if field_parts:
492 field_pre = field_parts[0] or ''
493 field_bare = field_parts[1] or ''
494 field_post = field_parts[2] or ''
495 if field_bare:
496 # check wether translatable
497 if self.check_translation_existing(
498 cr, user, modelname, field_bare, contextlang):
499 # join to translation table
500 # and replace field by translated version
501 new_join, new_field = (
502 self._compose_translation_join(
503 cr, user, modelname, tablename,
504 field_bare, context))
505 translation_joins.append(new_join)
506 field_bare = new_field
507 replacing_field = (
508 field_pre + new_field + ' ' + field_post)
509 sql_clause_parts.append(replacing_field)
510 if translation_joins:
511 # We have one or more translations, recompose sql_clause clause
512 sql_clause = clause_keyword + ' ' + ','.join(sql_clause_parts)
513 # return extra joins needed for translation, and modified clause
514 return (translation_joins, sql_clause)
414515
415516
416# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:517# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
417518
=== modified file 'openerp/osv/orm.py'
--- openerp/osv/orm.py 2012-10-29 18:07:42 +0000
+++ openerp/osv/orm.py 2012-10-30 13:29:28 +0000
@@ -4752,7 +4752,7 @@
4752 order_by_clause = ",".join(order_by_elements)4752 order_by_clause = ",".join(order_by_elements)
47534753
4754 return order_by_clause and (' ORDER BY %s ' % order_by_clause) or ''4754 return order_by_clause and (' ORDER BY %s ' % order_by_clause) or ''
47554755
4756 def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None):4756 def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None):
4757 """4757 """
4758 Private implementation of search() method, allowing specifying the uid to use for the access right check.4758 Private implementation of search() method, allowing specifying the uid to use for the access right check.
@@ -4784,6 +4784,13 @@
4784 cr.execute('SELECT count("%s".id) FROM ' % self._table + from_clause + where_str + limit_str + offset_str, where_clause_params)4784 cr.execute('SELECT count("%s".id) FROM ' % self._table + from_clause + where_str + limit_str + offset_str, where_clause_params)
4785 res = cr.fetchall()4785 res = cr.fetchall()
4786 return res[0][0]4786 return res[0][0]
4787 itr_obj = self.pool.get('ir.translation')
4788 translation_joins, modified_order_by = (
4789 itr_obj._apply_translation_2fields(
4790 cr, user, self._name, self._table, order_by, context))
4791 if translation_joins:
4792 from_clause = from_clause + ' ' + ' '.join(translation_joins)
4793 order_by = modified_order_by
4787 cr.execute('SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str, where_clause_params)4794 cr.execute('SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str, where_clause_params)
4788 res = cr.fetchall()4795 res = cr.fetchall()
4789 return [x[0] for x in res]4796 return [x[0] for x in res]