1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
# 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 Column
from trytond.i18n import lazy_gettext
from trytond.model import Index, Model, fields
def sequence_ordered(
field_name='sequence',
field_label=lazy_gettext('ir.msg_sequence'),
order='ASC NULLS FIRST'):
"Returns a mixin to order the model by order fields"
assert order.startswith('ASC')
class SequenceOrderedMixin(object):
"Mixin to order model by a sequence field"
__slots__ = ()
@classmethod
def __setup__(cls):
super(SequenceOrderedMixin, cls).__setup__()
table = cls.__table__()
cls._order = [(field_name, order)] + cls._order
cls._sql_indexes.add(
Index(table,
(Column(table, field_name), Index.Range(order=order)),
(table.id, Index.Range(order=order))))
setattr(SequenceOrderedMixin, field_name, fields.Integer(field_label))
return SequenceOrderedMixin
class _attrgetter:
__slots__ = ('_attr', '_null')
def __init__(self, attr, null=None):
self._attr = attr
self._null = null
def __call__(self, obj):
for name in self._attr.split('.'):
obj = getattr(obj, name, None)
if obj is None:
break
if isinstance(obj, Model):
Target = obj.__class__
oname = 'id'
if (Target._rec_name in Target._fields
and Target._fields[Target._rec_name].sortable(Target)):
oname = Target._rec_name
if (Target._order_name in Target._fields
and Target._fields[Target._order_name].sortable(Target)):
oname = Target._order_name
obj = getattr(obj, oname)
null = obj is None
if self._null is not None:
if null:
null = self._null
else:
null = not self._null
return (null, obj)
def sort(records, order):
"Return a new list of records ordered"
if not order:
return records
for oexpr, otype in reversed(order):
try:
otype, null_ordering = otype.split(' ', 1)
except ValueError:
null_ordering = None
reverse = otype == 'DESC'
if null_ordering == 'NULLS FIRST':
null = reverse
elif null_ordering == 'NULLS LAST':
null = not reverse
else:
null = None
records = sorted(
records, key=_attrgetter(oexpr, null), reverse=reverse)
return records
|