Add sequence_ordered
issue5105 review25561002
This commit is contained in:
1 parent
44bd45546c
commit
828afc310d
8 files changed
+100
-20
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Add sequence_ordered
|
||||
* Remove most country specific code in languages
|
||||
* Add support for derivative translations
|
||||
* Remove IDENTIFIER regexp on ir.model and ir.model.field
|
||||
|
||||
@@ -683,6 +683,19 @@ Class methods:
|
||||
|
||||
Return the SQL table and columns to use for the UNION for the model name.
|
||||
|
||||
================
|
||||
sequence_ordered
|
||||
================
|
||||
|
||||
.. method:: sequence_ordered([field_name, [field_label, [order, [null_first]]]])
|
||||
|
||||
Retuns a mixin_ class which defines the order of a :class:`ModelSQL` with an
|
||||
:class:`trytond.model.fields.Integer` field. field_name indicates the name of
|
||||
the field to be created and its default values is `sequence`. field_label
|
||||
defines the label which will be used by the field and defaults to `Sequence`.
|
||||
Order specifies the order direction and defaults to `ASC` and null first
|
||||
if the null values should be ordered first and defaults to `True`.
|
||||
|
||||
|
||||
.. _mixin: http://en.wikipedia.org/wiki/Mixin
|
||||
.. _JSON: http://en.wikipedia.org/wiki/Json
|
||||
|
||||
+3
-18
@@ -2,10 +2,7 @@
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from itertools import groupby
|
||||
|
||||
from sql import Null
|
||||
from sql.conditionals import Case
|
||||
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.model import ModelView, ModelSQL, fields, sequence_ordered
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.rpc import RPC
|
||||
@@ -72,12 +69,11 @@ CLIENT_ICONS = [(x, x) for x in (
|
||||
SEPARATOR = ' / '
|
||||
|
||||
|
||||
class UIMenu(ModelSQL, ModelView):
|
||||
class UIMenu(sequence_ordered(), ModelSQL, ModelView):
|
||||
"UI menu"
|
||||
__name__ = 'ir.ui.menu'
|
||||
|
||||
name = fields.Char('Menu', required=True, translate=True)
|
||||
sequence = fields.Integer('Sequence', required=True)
|
||||
childs = fields.One2Many('ir.ui.menu', 'parent', 'Children')
|
||||
parent = fields.Many2One('ir.ui.menu', 'Parent Menu', select=True,
|
||||
ondelete='CASCADE')
|
||||
@@ -102,7 +98,6 @@ class UIMenu(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(UIMenu, cls).__setup__()
|
||||
cls._order.insert(0, ('sequence', 'ASC'))
|
||||
cls._error_messages.update({
|
||||
'wrong_name': ('"%%s" is not a valid menu name because it is '
|
||||
'not allowed to contain "%s".' % SEPARATOR),
|
||||
@@ -271,13 +266,12 @@ class UIMenu(ModelSQL, ModelView):
|
||||
return menu2favorite
|
||||
|
||||
|
||||
class UIMenuFavorite(ModelSQL, ModelView):
|
||||
class UIMenuFavorite(sequence_ordered(), ModelSQL, ModelView):
|
||||
"Menu Favorite"
|
||||
__name__ = 'ir.ui.menu.favorite'
|
||||
|
||||
menu = fields.Many2One('ir.ui.menu', 'Menu', required=True,
|
||||
ondelete='CASCADE')
|
||||
sequence = fields.Integer('Sequence')
|
||||
user = fields.Many2One('res.user', 'User', required=True,
|
||||
ondelete='CASCADE')
|
||||
|
||||
@@ -289,15 +283,6 @@ class UIMenuFavorite(ModelSQL, ModelView):
|
||||
'set': RPC(readonly=False),
|
||||
'unset': RPC(readonly=False),
|
||||
})
|
||||
cls._order = [
|
||||
('sequence', 'ASC'),
|
||||
('id', 'DESC'),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def order_sequence(tables):
|
||||
table, _ = tables[None]
|
||||
return [Case((table.sequence == Null, 0), else_=1), table.sequence]
|
||||
|
||||
@staticmethod
|
||||
def default_user():
|
||||
|
||||
@@ -10,8 +10,9 @@ from .dictschema import DictSchemaMixin
|
||||
from .match import MatchMixin
|
||||
from .union import UnionMixin
|
||||
from .descriptors import dualmethod
|
||||
from .order import sequence_ordered
|
||||
|
||||
__all__ = ['Model', 'ModelView', 'ModelStorage', 'ModelSingleton', 'ModelSQL',
|
||||
'Check', 'Unique',
|
||||
'Workflow', 'DictSchemaMixin', 'MatchMixin', 'UnionMixin', 'dualmethod',
|
||||
'EvalEnvironment']
|
||||
'EvalEnvironment', 'sequence_ordered']
|
||||
@@ -0,0 +1,33 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the toplevel of this
|
||||
# repository contains the full copyright notices and license terms.
|
||||
from sql import Null, Column
|
||||
from sql.operators import Equal, NotEqual
|
||||
from sql.conditionals import Case
|
||||
|
||||
from trytond.model import fields
|
||||
|
||||
|
||||
def sequence_ordered(field_name='sequence', field_label='Sequence',
|
||||
order='ASC', null_first=True):
|
||||
"Returns a mixin to order the model by order fields"
|
||||
|
||||
class SequenceOrderedMixin(object):
|
||||
"Mixin to order model by a sequence field"
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(SequenceOrderedMixin, cls).__setup__()
|
||||
cls._order = [(field_name, order)] + cls._order
|
||||
|
||||
setattr(SequenceOrderedMixin, field_name, fields.Integer(field_label))
|
||||
|
||||
@classmethod
|
||||
def order_function(cls, tables):
|
||||
table, _ = tables[None]
|
||||
operator = Equal
|
||||
if not null_first:
|
||||
operator = NotEqual
|
||||
field = Column(table, field_name)
|
||||
return [Case((operator(field, Null), 0), else_=1), field]
|
||||
setattr(SequenceOrderedMixin, 'order_%s' % field_name, order_function)
|
||||
return SequenceOrderedMixin
|
||||
@@ -120,6 +120,7 @@ def register():
|
||||
Model4UnionTree1,
|
||||
Model4UnionTree2,
|
||||
UnionTree,
|
||||
SequenceOrderedModel,
|
||||
ModelViewChangedValues,
|
||||
ModelViewChangedValuesTarget,
|
||||
ModelViewButton,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from trytond.model import ModelSingleton, ModelSQL, UnionMixin, fields
|
||||
from trytond.model import (ModelSingleton, ModelSQL, UnionMixin, fields,
|
||||
sequence_ordered)
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
__all__ = [
|
||||
@@ -11,6 +12,7 @@ __all__ = [
|
||||
'Model4Union1', 'Model4Union2', 'Model4Union3', 'Model4Union4',
|
||||
'Union', 'UnionUnion',
|
||||
'Model4UnionTree1', 'Model4UnionTree2', 'UnionTree',
|
||||
'SequenceOrderedModel',
|
||||
]
|
||||
|
||||
|
||||
@@ -154,3 +156,8 @@ class UnionTree(UnionMixin, ModelSQL):
|
||||
@staticmethod
|
||||
def union_models():
|
||||
return ['test.model.union.tree1', 'test.model.union.tree2']
|
||||
|
||||
|
||||
class SequenceOrderedModel(sequence_ordered(), ModelSQL):
|
||||
'Sequence Ordered Model'
|
||||
__name__ = 'test.order.sequence'
|
||||
@@ -0,0 +1,39 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import unittest
|
||||
|
||||
from trytond.tests.test_tryton import install_module, with_transaction
|
||||
from trytond.pool import Pool
|
||||
|
||||
|
||||
class SequenceOrderedMixinTestCase(unittest.TestCase):
|
||||
'Test SequenceOrderedMixin'
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
install_module('tests')
|
||||
|
||||
@with_transaction()
|
||||
def test_order(self):
|
||||
'Test order'
|
||||
pool = Pool()
|
||||
Order = pool.get('test.order.sequence')
|
||||
|
||||
models = []
|
||||
for i in reversed(range(1, 4)):
|
||||
models.append(Order(sequence=i))
|
||||
Order.save(models)
|
||||
models.reverse()
|
||||
self.assertListEqual(Order.search([]), models)
|
||||
|
||||
model = models.pop()
|
||||
model.sequence = None
|
||||
model.save()
|
||||
models.insert(0, model)
|
||||
|
||||
self.assertListEqual(Order.search([]), models)
|
||||
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromTestCase(
|
||||
SequenceOrderedMixinTestCase)
|
||||
Reference in new issue
Block a user