option to cause exceptions to be raised for invalid columns
authorMichael Elsdörfer <michael@elsdoerfer.info>
Thu, 19 Jun 2008 18:38:00 +0000 (18:38 +0000)
committerMichael Elsdörfer <michael@elsdoerfer.info>
Thu, 19 Jun 2008 18:38:00 +0000 (18:38 +0000)
README
django_tables/tables.py
tests/test_basic.py
tests/test_models.py

diff --git a/README b/README
index 2a8c6285ebd7bfc9f335b428760bd4f7bd6fbe65..2b7ec37cc7c2728ba8c75b8bca9286d8c14ea190 100644 (file)
--- a/README
+++ b/README
@@ -232,6 +232,35 @@ for that particular field will be in reverse order.
 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
index aa0980370c264a6e3bf8bb72021dd54ac3edb149..da6c504423016e2ecb719afdcf465b3ffa7cf9e1 100644 (file)
@@ -4,7 +4,7 @@ from django.utils.encoding import StrAndUnicode
 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
@@ -89,6 +89,15 @@ class OrderByTuple(tuple, StrAndUnicode):
         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
@@ -193,12 +202,17 @@ class BaseTable(object):
         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
index 08dd01596b1a3116b5683eed180c006243220214..086decc1d663df8bfcef7f2b51862d302e28f1f2 100644 (file)
@@ -3,6 +3,7 @@
 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
@@ -85,6 +86,15 @@ def test_basic():
     # 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
@@ -111,6 +121,7 @@ def test_sort():
     # 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
index d04dc19b17dba7cbcdbdde04cd2e620d898d9c71..542d3008265df64044201362744e4b5ed92f4228 100644 (file)
@@ -148,9 +148,9 @@ def test_sort():
     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
@@ -173,4 +173,4 @@ def test_pagination():
 # 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