def __init__(self, options=None):\r
super(TableOptions, self).__init__()\r
self.sortable = getattr(options, 'sortable', None)\r
+ self.order_by = getattr(options, 'order_by', None)\r
\r
\r
class DeclarativeColumnsMetaclass(type):\r
\r
rows_class = Rows\r
\r
- def __init__(self, data, order_by=None):\r
+ # this value is not the same as None. it means 'use the default sort\r
+ # order', which may (or may not) be inherited from the table options.\r
+ # None means 'do not sort the data', ignoring the default.\r
+ DefaultOrder = type('DefaultSortType', (), {})()\r
+\r
+ def __init__(self, data, order_by=DefaultOrder):\r
"""Create a new table instance with the iterable ``data``.\r
\r
If ``order_by`` is specified, the data will be sorted accordingly.\r
+ Otherwise, the sort order can be specified in the table options.\r
\r
Note that unlike a ``Form``, tables are always bound to data. Also\r
unlike a form, the ``columns`` attribute is read-only and returns\r
self._rows = self.rows_class(self)\r
self._columns = Columns(self)\r
\r
- self.order_by = order_by\r
+ # None is a valid order, so we must use DefaultOrder as a flag\r
+ # to fall back to the table sort order. set the attr via the\r
+ # property, to wrap it in an OrderByTuple before being stored\r
+ if order_by != BaseTable.DefaultOrder:\r
+ self.order_by = order_by\r
+\r
+ else:\r
+ self.order_by = self._meta.order_by\r
\r
# Make a copy so that modifying this will not touch the class\r
# definition. Note that this is different from forms, where the\r
self._order_by = OrderByTuple(validated_order_by)\r
else:\r
self._order_by = OrderByTuple()\r
+\r
order_by = property(lambda s: s._order_by, _set_order_by)\r
\r
def __unicode__(self):\r
"""
-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
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)
Sets up a temporary Django project using a memory SQLite database.\r
"""\r
\r
-from nose.tools import assert_raises\r
+from nose.tools import assert_raises, assert_equal\r
from django.conf import settings\r
from django.core.paginator import *\r
import django_tables as tables\r
countries.order_by = ('custom1', 'custom2')\r
assert countries.order_by == ()\r
\r
+def test_default_sort():\r
+ class SortedCountryTable(tables.ModelTable):\r
+ class Meta:\r
+ model = Country\r
+ order_by = '-name'\r
+\r
+ # the default order can be inherited from the table\r
+ assert_equal(('-name',), SortedCountryTable().order_by)\r
+ assert_equal(4, SortedCountryTable().rows[0]['id'])\r
+\r
+ # and explicitly set (or reset) via __init__\r
+ assert_equal(2, SortedCountryTable(order_by='system').rows[0]['id'])\r
+ assert_equal(1, SortedCountryTable(order_by=None).rows[0]['id'])\r
+\r
def test_callable():\r
"""Some of the callable code is reimplemented for modeltables, so\r
test some specifics again.\r