Allow to use the dotted notation for search order parameters
issue4370 review5801002
This commit is contained in:
1 parent
67d5291bfd
commit
dcf57d8c33
7 files changed
+57
-15
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Allow to use the dotted notation for order parameters
|
||||
* Use action_id to find report to use
|
||||
* Allow custom StateView without Model
|
||||
* Remove Pool.object_name_list
|
||||
|
||||
@@ -367,6 +367,10 @@ Class attributes are:
|
||||
second is the sort ordering as `ASC` for ascending or `DESC` for
|
||||
descending.
|
||||
|
||||
In case the field used for the first element is a :class:`fields.Many2One`,
|
||||
it is also possible to use the dotted notation to sort on a specific field
|
||||
from the target record.
|
||||
|
||||
.. attribute:: ModelSQL._order_name
|
||||
|
||||
The name of the field (or an SQL statement) on which the records must be
|
||||
|
||||
@@ -180,25 +180,33 @@ class Many2One(Field):
|
||||
return column.in_(query)
|
||||
|
||||
def convert_order(self, name, tables, Model):
|
||||
if getattr(Model, 'order_%s' % name, None):
|
||||
return super(Many2One, self).convert_order(name, tables, Model)
|
||||
assert name == self.name
|
||||
fname, _, oexpr = name.partition('.')
|
||||
if not oexpr and getattr(Model, 'order_%s' % fname, None):
|
||||
return super(Many2One, self).convert_order(fname, tables, Model)
|
||||
assert fname == self.name
|
||||
|
||||
Target = self.get_target()
|
||||
|
||||
oname = 'id'
|
||||
if Target._rec_name in Target._fields:
|
||||
oname = Target._rec_name
|
||||
if Target._order_name in Target._fields:
|
||||
oname = Target._order_name
|
||||
if oexpr:
|
||||
oname, _, _ = oexpr.partition('.')
|
||||
else:
|
||||
oname = 'id'
|
||||
if Target._rec_name in Target._fields:
|
||||
oname = Target._rec_name
|
||||
if Target._order_name in Target._fields:
|
||||
oname = Target._order_name
|
||||
oexpr = oname
|
||||
|
||||
table, _ = tables[None]
|
||||
if oname == 'id':
|
||||
return [self.sql_column(table)]
|
||||
|
||||
ofield = Target._fields[oname]
|
||||
table, _ = tables[None]
|
||||
target_tables = tables.get(name)
|
||||
target_tables = tables.get(fname)
|
||||
if target_tables is None:
|
||||
target = Target.__table__()
|
||||
target_tables = {
|
||||
None: (target, target.id == self.sql_column(table)),
|
||||
}
|
||||
tables[name] = target_tables
|
||||
return ofield.convert_order(oname, target_tables, Target)
|
||||
tables[fname] = target_tables
|
||||
return ofield.convert_order(oexpr, target_tables, Target)
|
||||
@@ -997,10 +997,11 @@ class ModelSQL(ModelStorage):
|
||||
}
|
||||
if order is None or order is False:
|
||||
order = cls._order
|
||||
for fname, otype in order:
|
||||
for oexpr, otype in order:
|
||||
fname, _, extra_expr = oexpr.partition('.')
|
||||
field = cls._fields[fname]
|
||||
Order = order_types[otype.upper()]
|
||||
forder = field.convert_order(fname, tables, cls)
|
||||
forder = field.convert_order(oexpr, tables, cls)
|
||||
order_by.extend((Order(o) for o in forder))
|
||||
|
||||
main_table, _ = tables[None]
|
||||
|
||||
@@ -154,6 +154,7 @@ def register():
|
||||
CopyMany2ManyReferenceRelation,
|
||||
Many2OneTarget,
|
||||
Many2OneDomainValidation,
|
||||
Many2OneOrderBy,
|
||||
TestHistory,
|
||||
TestHistoryLine,
|
||||
FieldContextChild,
|
||||
|
||||
@@ -34,7 +34,7 @@ __all__ = [
|
||||
'Selection', 'SelectionRequired',
|
||||
'DictSchema', 'Dict', 'DictDefault', 'DictRequired',
|
||||
'Binary', 'BinaryDefault', 'BinaryRequired',
|
||||
'Many2OneDomainValidation', 'Many2OneTarget',
|
||||
'Many2OneDomainValidation', 'Many2OneTarget', 'Many2OneOrderBy',
|
||||
]
|
||||
|
||||
|
||||
@@ -683,6 +683,7 @@ class BinaryRequired(ModelSQL):
|
||||
class Many2OneTarget(ModelSQL):
|
||||
"Many2One Domain Validation Target"
|
||||
__name__ = 'test.many2one_target'
|
||||
_order_name = 'value'
|
||||
|
||||
active = fields.Boolean('Active')
|
||||
value = fields.Integer('Value')
|
||||
@@ -701,3 +702,9 @@ class Many2OneDomainValidation(ModelSQL):
|
||||
('value', '>', 5),
|
||||
])
|
||||
dummy = fields.Char('Dummy')
|
||||
|
||||
|
||||
class Many2OneOrderBy(ModelSQL):
|
||||
"Many2One OrderBy"
|
||||
__name__ = 'test.many2one_orderby'
|
||||
many2one = fields.Many2One('test.many2one_target', 'many2one')
|
||||
@@ -111,6 +111,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.binary_required = POOL.get('test.binary_required')
|
||||
|
||||
self.m2o_domain_validation = POOL.get('test.many2one_domainvalidation')
|
||||
self.m2o_orderby = POOL.get('test.many2one_orderby')
|
||||
self.m2o_target = POOL.get('test.many2one_target')
|
||||
|
||||
def test0010boolean(self):
|
||||
@@ -3236,6 +3237,25 @@ class FieldsTestCase(unittest.TestCase):
|
||||
domain.dummy = 'Dummy'
|
||||
domain.save()
|
||||
|
||||
# Testing order_by
|
||||
for value in (5, 3, 2):
|
||||
m2o, = self.m2o_target.create([{'value': value}])
|
||||
self.m2o_orderby.create([{'many2one': m2o}])
|
||||
|
||||
search = self.m2o_orderby.search([], order=[('many2one', 'ASC')])
|
||||
self.assertTrue(all(x.many2one.value <= y.many2one.value
|
||||
for x, y in zip(search, search[1:])))
|
||||
|
||||
search = self.m2o_orderby.search([],
|
||||
order=[('many2one.id', 'ASC')])
|
||||
self.assertTrue(all(x.many2one.id <= y.many2one.id
|
||||
for x, y in zip(search, search[1:])))
|
||||
|
||||
search = self.m2o_orderby.search([],
|
||||
order=[('many2one.value', 'ASC')])
|
||||
self.assertTrue(all(x.many2one.value <= y.many2one.value
|
||||
for x, y in zip(search, search[1:])))
|
||||
|
||||
transaction.cursor.rollback()
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user