From 5047081c9b1ba9dd9747312f32d26d4aeb537f92 Mon Sep 17 00:00:00 2001 From: adammck Date: Thu, 15 Apr 2010 23:17:22 -0400 Subject: [PATCH] added default 'order_by' option to tables. this allows tables to specify a default sort order, which includes all of the UI of an explicitly-sorted dataset. also, some tests. they fail for BaseTable, because sorting doesn't seem to be implemented there. it should be, though, despite being crazy. --- django_tables/base.py | 19 ++++++++++++-- tests/test_basic.py | 60 ++++++++++++++++++++++++++++++++++++++++++- tests/test_models.py | 16 +++++++++++- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/django_tables/base.py b/django_tables/base.py index d005226..b08e061 100644 --- a/django_tables/base.py +++ b/django_tables/base.py @@ -15,6 +15,7 @@ class TableOptions(object): def __init__(self, options=None): super(TableOptions, self).__init__() self.sortable = getattr(options, 'sortable', None) + self.order_by = getattr(options, 'order_by', None) class DeclarativeColumnsMetaclass(type): @@ -419,10 +420,16 @@ class BaseTable(object): rows_class = Rows - def __init__(self, data, order_by=None): + # this value is not the same as None. it means 'use the default sort + # order', which may (or may not) be inherited from the table options. + # None means 'do not sort the data', ignoring the default. + DefaultOrder = type('DefaultSortType', (), {})() + + def __init__(self, data, order_by=DefaultOrder): """Create a new table instance with the iterable ``data``. If ``order_by`` is specified, the data will be sorted accordingly. + Otherwise, the sort order can be specified in the table options. Note that unlike a ``Form``, tables are always bound to data. Also unlike a form, the ``columns`` attribute is read-only and returns @@ -439,7 +446,14 @@ class BaseTable(object): self._rows = self.rows_class(self) self._columns = Columns(self) - self.order_by = order_by + # None is a valid order, so we must use DefaultOrder as a flag + # to fall back to the table sort order. set the attr via the + # property, to wrap it in an OrderByTuple before being stored + if order_by != BaseTable.DefaultOrder: + self.order_by = order_by + + else: + self.order_by = self._meta.order_by # Make a copy so that modifying this will not touch the class # definition. Note that this is different from forms, where the @@ -545,6 +559,7 @@ class BaseTable(object): self._order_by = OrderByTuple(validated_order_by) else: self._order_by = OrderByTuple() + order_by = property(lambda s: s._order_by, _set_order_by) def __unicode__(self): diff --git a/tests/test_basic.py b/tests/test_basic.py index fe94278..4271a7d 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -2,7 +2,7 @@ """ -from nose.tools import assert_raises +from nose.tools import assert_raises, assert_equal from django.http import Http404 from django.core.paginator import Paginator import django_tables as tables @@ -53,6 +53,64 @@ def test_declaration(): assert 'motto' in StateTable2.base_columns +def test_sort(): + class MyUnsortedTable(TestTable): + alpha = tables.Column() + beta = tables.Column() + n = tables.Column() + + test_data = [ + {'alpha': "mmm", 'beta': "mmm", 'n': 1 }, + {'alpha': "aaa", 'beta': "zzz", 'n': 2 }, + {'alpha': "zzz", 'beta': "aaa", 'n': 3 }] + + # unsorted (default) preserves order + assert_equal(1, MyUnsortedTable(test_data ).rows[0]['n']) + assert_equal(1, MyUnsortedTable(test_data, order_by=None).rows[0]['n']) + assert_equal(1, MyUnsortedTable(test_data, order_by=[] ).rows[0]['n']) + assert_equal(1, MyUnsortedTable(test_data, order_by=() ).rows[0]['n']) + + # values of order_by are wrapped in tuples before being returned + assert_equal(('alpha',), MyUnsortedTable([], order_by='alpha').order_by) + assert_equal(('beta',), MyUnsortedTable([], order_by=('beta',)).order_by) + assert_equal((), MyUnsortedTable([]).order_by) + + # a rewritten order_by is also wrapped + table = MyUnsortedTable([]) + table.order_by = 'alpha' + assert_equal(('alpha',), table.order_by) + + # data can be sorted by any column + assert_equal(2, MyUnsortedTable(test_data, order_by='alpha').rows[0]['n']) + assert_equal(3, MyUnsortedTable(test_data, order_by='beta' ).rows[0]['n']) + + # default sort order can be specified in table options + class MySortedTable(MyUnsortedTable): + class Meta: + order_by = 'alpha' + + # order_by is inherited from the options if not explitly set + table = MySortedTable(test_data) + assert_equal(('alpha',), table.order_by) + assert_equal(2, table.rows[0]['n']) + + # ...but can be overloaded at __init___ + table = MySortedTable(test_data, order_by='beta') + assert_equal(('beta',), table.order_by) + assert_equal(3, table.rows[0]['n']) + + # ...or rewritten later + table = MySortedTable(test_data) + table.order_by = 'beta' + assert_equal(('beta',), table.order_by) + assert_equal(3, table.rows[0]['n']) + + # ...or reset to None (unsorted), ignoring the table default + table = MySortedTable(test_data, order_by=None) + assert_equal((), table.order_by) + assert_equal(1, table.rows[0]['n']) + + def test_column_count(): class MyTable(TestTable): visbible = tables.Column(visible=True) diff --git a/tests/test_models.py b/tests/test_models.py index 61f97b0..47485a0 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -3,7 +3,7 @@ Sets up a temporary Django project using a memory SQLite database. """ -from nose.tools import assert_raises +from nose.tools import assert_raises, assert_equal from django.conf import settings from django.core.paginator import * import django_tables as tables @@ -206,6 +206,20 @@ def test_sort(): countries.order_by = ('custom1', 'custom2') assert countries.order_by == () +def test_default_sort(): + class SortedCountryTable(tables.ModelTable): + class Meta: + model = Country + order_by = '-name' + + # the default order can be inherited from the table + assert_equal(('-name',), SortedCountryTable().order_by) + assert_equal(4, SortedCountryTable().rows[0]['id']) + + # and explicitly set (or reset) via __init__ + assert_equal(2, SortedCountryTable(order_by='system').rows[0]['id']) + assert_equal(1, SortedCountryTable(order_by=None).rows[0]['id']) + def test_callable(): """Some of the callable code is reimplemented for modeltables, so test some specifics again. -- 2.26.2