we've just written support for a choices=True setting, when we noticed that we don...
authorMichael Elsdörfer <michael@elsdoerfer.info>
Thu, 19 Jun 2008 16:06:01 +0000 (16:06 +0000)
committerMichael Elsdörfer <michael@elsdoerfer.info>
Thu, 19 Jun 2008 16:06:01 +0000 (16:06 +0000)
in fact, when using model forms, this new behavior will be in the way, since when used with foreignkeys, we would have to map r.column.id to fk.id and r.column.value to fk, so that r.column.value.id == r.column.id; all that is completely unnecessary.

we're commiting this anyway to log it, but will revert right afterwards

README
django_tables/columns.py
django_tables/tables.py
tests/test_basic.py
tests/test_models.py

diff --git a/README b/README
index b7813a5adc14affe203e16ca209c33f99ed4e02b..6d241e2dcbb44eebd16d9987fc47a39837e11efc 100644 (file)
--- a/README
+++ b/README
@@ -34,7 +34,7 @@ To use the table, create an instance:
     countries = CountryTable([{'name': 'Germany', population: 80},\r
                               {'name': 'France', population: 64}])\r
 \r
-Decide how you the table should be sorted:\r
+Decide how the table should be sorted:\r
 \r
     countries.order_by = ('name',)\r
     assert [row.name for row in countries.row] == ['France', 'Germany']\r
@@ -104,6 +104,7 @@ access columns directly:
             {{ table.columns.tz }}\r
         {% endfor %}\r
 \r
+\r
 ModelTables\r
 -----------\r
 \r
@@ -189,6 +190,11 @@ verbose_name, default, visible, sortable
     Setting ``sortable`` to False will result in this column being unusable\r
     in ordering.\r
 \r
+    The ``choices`` argument currently expects a boolean value (defaults to\r
+    False). If enabled, the column will be able to hold an id/value pair.\r
+    more extensive choices support (mapping an id to predefined value) is\r
+    forthcoming.\r
+\r
 ``django_tables.columns`` currently defines three classes, ``Column``,\r
 ``TextColumn`` and ``NumberColumn``. However, the two subclasses currently\r
 don't do anything special at all, so you can simply use the base class.\r
index 9b8aced4ee6fec8b78c14a7299ba9cc4772b3d40..2da8cac9b7db21838043a66378ef1ba3d0fe5455 100644 (file)
@@ -25,18 +25,23 @@ class Column(object):
 \r
     Setting ``sortable`` to False will result in this column being unusable\r
     in ordering.\r
+\r
+    The ``choices`` argument currently expects a boolean value (defaults to\r
+    False). If enabled, the column will be able to hold an id/value pair.\r
     """\r
     # Tracks each time a Column instance is created. Used to retain order.\r
     creation_counter = 0\r
 \r
     def __init__(self, verbose_name=None, name=None, default=None,\r
-                 visible=True, inaccessible=False, sortable=True):\r
+                 visible=True, inaccessible=False, sortable=True,\r
+                 choices=None):\r
         self.verbose_name = verbose_name\r
         self.name = name\r
         self.default = default\r
         self.visible = visible\r
         self.inaccessible = inaccessible\r
         self.sortable = sortable\r
+        self.choices = choices\r
 \r
         self.creation_counter = Column.creation_counter\r
         Column.creation_counter += 1\r
index aa0980370c264a6e3bf8bb72021dd54ac3edb149..44f604331c0ecbdc805f546f5ad582f0078ca448 100644 (file)
@@ -357,7 +357,8 @@ class BoundRow(object):
     def __getitem__(self, name):\r
         """Returns this row's value for a column. All other access methods,\r
         e.g. __iter__, lead ultimately to this."""\r
-        return self.data[self.table.columns[name].declared_name]\r
+        column = self.table.columns[name]\r
+        return RowValue(self.data[column.declared_name], column)\r
 \r
     def __contains__(self, item):\r
         """Check by both row object and column name."""\r
@@ -372,4 +373,30 @@ class BoundRow(object):
     values = property(_get_values)\r
 \r
     def as_html(self):\r
-        pass
\ No newline at end of file
+        pass\r
+\r
+class RowValue(StrAndUnicode):\r
+    """Very basic wrapper around a single row value of a column.\r
+\r
+    Instead of returning the row values directly, ``BoundRow`` spawns\r
+    instances of this class. That's necessary since the ``choices``\r
+    feature means that a single row value can consist of both the value\r
+    itself and an associated ID.\r
+    """\r
+    def __init__(self, value, column):\r
+        if column.column.choices == True:\r
+            if isinstance(value, dict):\r
+                self.id = value.get('id')\r
+                self.value = value.get('value')\r
+            elif isinstance(value, (tuple,list,)):\r
+                self.id = value[0]\r
+                self.value = value[1]\r
+            else:\r
+                self.id = None\r
+                self.value = value\r
+        else:\r
+            self.id = None\r
+            self.value = value\r
+\r
+    def __unicode__(self):\r
+        return unicode(self.value)
\ No newline at end of file
index 08dd01596b1a3116b5683eed180c006243220214..a1ffd9fd89d0cad03f81f37f909e647deb72f8ae 100644 (file)
@@ -68,12 +68,12 @@ def test_basic():
         assert not 'id' in r\r
         # missing data is available as default\r
         assert 'answer' in r\r
-        assert r['answer'] == 42   # note: different from prev. line!\r
+        assert r['answer'].value == 42   # note: different from prev. line!\r
 \r
         # all that still works when name overrides are used\r
         assert not 'c' in r\r
         assert 'count' in r\r
-        assert r['count'] == 1\r
+        assert r['count'].value == 1\r
 \r
     # changing an instance's base_columns does not change the class\r
     assert id(books.base_columns) != id(BookTable.base_columns)\r
@@ -121,4 +121,23 @@ def test_sort():
     books.base_columns['language'].sortable = False\r
     books.order_by = 'language'\r
     assert not books.order_by\r
-    test_order(('language', 'num_pages'), [1,3,2,4])  # as if: 'num_pages'
\ No newline at end of file
+    test_order(('language', 'num_pages'), [1,3,2,4])  # as if: 'num_pages'\r
+\r
+def test_choices():\r
+    # unrestricted choices\r
+    class BookTable(tables.Table):\r
+        id = tables.Column()\r
+        name = tables.Column()\r
+        author = tables.Column(choices=True)\r
+\r
+    books = BookTable([\r
+        {'id': 1, 'name': 'A'},\r
+        {'id': 2, 'author': (99, 'Mr. Vanderlay'), 'name': 'B'},\r
+        {'id': 3, 'author': 'Mr. Vanderlay', 'name': 'C'},\r
+        {'id': 4, 'author': {'id': 99, 'value': 'Mr. Vanderlay'}, 'name': 'D'},\r
+    ])\r
+\r
+    assert [r['author'].id for r in books.rows] == [None, 99, None, 99]\r
+    assert [r['author'].value for r in books.rows] == [None, 'Mr. Vanderlay', 'Mr. Vanderlay', 'Mr. Vanderlay']\r
+\r
+    # TODO: restricted choices (planned)
\ No newline at end of file
index a9f2e71bf0ea3f024b0b1f94e2635103d48b4576..ffe8eb7a5ba3ed5a5f16ab6cd946121a1bdeba54 100644 (file)
@@ -152,11 +152,14 @@ def test_sort():
     countries.order_by = ('custom1', 'custom2')\r
     assert countries.order_by == ()\r
 \r
+def test_choices():\r
+    pass # TODO\r
+\r
 def test_pagination():\r
     pass\r
 \r
-# TODO: foreignkey columns: simply support foreignkeys, tuples and id, name dicts; support column choices attribute to validate id-only\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: throw an exception on invalid order_by\r
+# TODO: option to skip model table generation (leave off model option?)
\ No newline at end of file