Random ordering is currently not supported.\r
\r
\r
+Error handling\r
+--------------\r
+\r
+Passing incoming query string values from the request directly to the\r
+table constructor is a common thing to do. However, such data can easily\r
+be invalid, be it that a user manually modified it, or someone put up a\r
+broken link. In those cases, you usually would not want to raise an\r
+exception (nor be notified by Django's error notification mechanism) -\r
+there is nothing you could do anyway.\r
+\r
+Because of this, such errors will by default be silently ignored. For\r
+example, if one out of three columns in an "order_by" is invalid, the other\r
+two will still be applied:\r
+\r
+ table.order_by = ('name', 'totallynotacolumn', '-date)\r
+ assert table.order_by = ('name', '-date)\r
+\r
+This ensures that the following table will be created regardless of the\r
+string in "sort.\r
+\r
+ table = MyTable(data, order_by=request.GET.get('sort'))\r
+\r
+However, if you want, you can disable this behaviour and have an exception\r
+raised instead, using:\r
+\r
+ import django_tables\r
+ django_tables.options.IGNORE_INVALID_OPTIONS = False\r
+\r
+\r
Template Utilities\r
------------------\r
\r
from django.utils.text import capfirst\r
from columns import Column\r
\r
-__all__ = ('BaseTable', 'Table')\r
+__all__ = ('BaseTable', 'Table', 'options')\r
\r
def sort_table(data, order_by):\r
"""Sort a list of dicts according to the fieldnames in the\r
def __unicode__(self):\r
return ",".join(self)\r
\r
+# A common use case is passing incoming query values directly into the\r
+# table constructor - data that can easily be invalid, say if manually\r
+# modified by a user. So by default, such errors will be silently\r
+# ignored. Set the option below to False if you want an exceptions to be\r
+# raised instead.\r
+class DefaultOptions(object):\r
+ IGNORE_INVALID_OPTIONS = True\r
+options = DefaultOptions()\r
+\r
class BaseTable(object):\r
def __init__(self, data, order_by=None):\r
"""Create a new table instance with the iterable ``data``.\r
if self._snapshot is not None:\r
self._snapshot = None\r
# accept both string and tuple instructions\r
- self._order_by = (isinstance(value, basestring) \\r
+ order_by = (isinstance(value, basestring) \\r
and [value.split(',')] \\r
or [value])[0]\r
# validate, remove all invalid order instructions\r
- self._order_by = OrderByTuple([o for o in self._order_by\r
- if self._validate_column_name((o[:1]=='-' and [o[1:]] or [o])[0], "order_by")])\r
+ validated_order_by = []\r
+ for o in order_by:\r
+ if self._validate_column_name((o[:1]=='-' and [o[1:]] or [o])[0], "order_by"):\r
+ validated_order_by.append(o)\r
+ elif not options.IGNORE_INVALID_OPTIONS:\r
+ raise ValueError('Column name %s is invalid.' % o)\r
+ self._order_by = OrderByTuple(validated_order_by)\r
order_by = property(lambda s: s._order_by, _set_order_by)\r
\r
def __unicode__(self):\r
This includes the core, as well as static data, non-model tables.\r
"""\r
\r
+from py.test import raises\r
import django_tables as tables\r
\r
def test_declaration():\r
# TODO: row cache currently not used\r
#assert id(list(books.rows)[0]) == id(list(books.rows)[0])\r
\r
+ # optionally, exceptions can be raised when input is invalid\r
+ tables.options.IGNORE_INVALID_OPTIONS = False\r
+ raises(Exception, "books.order_by = '-name,made-up-column'")\r
+ raises(Exception, "books.order_by = ('made-up-column',)")\r
+ # when a column name is overwritten, the original won't work anymore\r
+ raises(Exception, "books.order_by = 'c'")\r
+ # reset for future tests\r
+ tables.options.IGNORE_INVALID_OPTIONS = True\r
+\r
def test_sort():\r
class BookTable(tables.Table):\r
id = tables.Column()\r
# using a simple string (for convinience as well as querystring passing\r
test_order('-num_pages', [4,2,3,1])\r
test_order('language,num_pages', [3,2,1,4])\r
+ # TODO: test that unrewritte name has no effect\r
\r
# [bug] test alternative order formats if passed to constructor\r
BookTable([], 'language,-num_pages')\r
test_order(('population',), [1,4,3,2])\r
test_order(('-population',), [2,3,4,1])\r
test_order(('name',), [1,3,2,4])\r
- # test sorting by a "rewritten" column name\r
- countries.order_by = 'domain,tld'\r
- countries.order_by == ('domain',)\r
+ # test sorting with a "rewritten" column name\r
+ countries.order_by = 'domain,tld' # "tld" would be invalid...\r
+ countries.order_by == ('domain',) # ...and is therefore removed\r
test_order(('-domain',), [4,3,2,1])\r
# test multiple order instructions; note: one row is missing a "system"\r
# value, but has a default set; however, that has no effect on sorting.\r
# TODO: pagination\r
# TODO: support function column sources both for modeltables (methods on model) and static tables (functions in dict)\r
# TODO: manual base columns change -> update() call (add as example in docstr here) -> rebuild snapshot: is row cache, column cache etc. reset?\r
-# TODO: throw an exception on invalid order_by
\ No newline at end of file
+# TODO: support relationship spanning columns (we could generate select_related() automatically)
\ No newline at end of file