From 5c3fa9d5eac1ec87e510a63ec3f98e973fdb9b49 Mon Sep 17 00:00:00 2001 From: michael <> Date: Thu, 19 Jun 2008 16:06:01 +0000 Subject: [PATCH] we've just written support for a choices=True setting, when we noticed that we don't actually need it. instead of using special column property to enable row.column.id and row.column.value separation, the user is free to implement this behavior himself by simply making the value a dict. 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 | 8 +++++++- django_tables/columns.py | 7 ++++++- django_tables/tables.py | 31 +++++++++++++++++++++++++++++-- tests/test_basic.py | 25 ++++++++++++++++++++++--- tests/test_models.py | 7 +++++-- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/README b/README index b7813a5..6d241e2 100644 --- a/README +++ b/README @@ -34,7 +34,7 @@ To use the table, create an instance: countries = CountryTable([{'name': 'Germany', population: 80}, {'name': 'France', population: 64}]) -Decide how you the table should be sorted: +Decide how the table should be sorted: countries.order_by = ('name',) assert [row.name for row in countries.row] == ['France', 'Germany'] @@ -104,6 +104,7 @@ access columns directly: {{ table.columns.tz }} {% endfor %} + ModelTables ----------- @@ -189,6 +190,11 @@ verbose_name, default, visible, sortable Setting ``sortable`` to False will result in this column being unusable in ordering. + The ``choices`` argument currently expects a boolean value (defaults to + False). If enabled, the column will be able to hold an id/value pair. + more extensive choices support (mapping an id to predefined value) is + forthcoming. + ``django_tables.columns`` currently defines three classes, ``Column``, ``TextColumn`` and ``NumberColumn``. However, the two subclasses currently don't do anything special at all, so you can simply use the base class. diff --git a/django_tables/columns.py b/django_tables/columns.py index 9b8aced..2da8cac 100644 --- a/django_tables/columns.py +++ b/django_tables/columns.py @@ -25,18 +25,23 @@ class Column(object): Setting ``sortable`` to False will result in this column being unusable in ordering. + + The ``choices`` argument currently expects a boolean value (defaults to + False). If enabled, the column will be able to hold an id/value pair. """ # Tracks each time a Column instance is created. Used to retain order. creation_counter = 0 def __init__(self, verbose_name=None, name=None, default=None, - visible=True, inaccessible=False, sortable=True): + visible=True, inaccessible=False, sortable=True, + choices=None): self.verbose_name = verbose_name self.name = name self.default = default self.visible = visible self.inaccessible = inaccessible self.sortable = sortable + self.choices = choices self.creation_counter = Column.creation_counter Column.creation_counter += 1 diff --git a/django_tables/tables.py b/django_tables/tables.py index aa09803..44f6043 100644 --- a/django_tables/tables.py +++ b/django_tables/tables.py @@ -357,7 +357,8 @@ class BoundRow(object): def __getitem__(self, name): """Returns this row's value for a column. All other access methods, e.g. __iter__, lead ultimately to this.""" - return self.data[self.table.columns[name].declared_name] + column = self.table.columns[name] + return RowValue(self.data[column.declared_name], column) def __contains__(self, item): """Check by both row object and column name.""" @@ -372,4 +373,30 @@ class BoundRow(object): values = property(_get_values) def as_html(self): - pass \ No newline at end of file + pass + +class RowValue(StrAndUnicode): + """Very basic wrapper around a single row value of a column. + + Instead of returning the row values directly, ``BoundRow`` spawns + instances of this class. That's necessary since the ``choices`` + feature means that a single row value can consist of both the value + itself and an associated ID. + """ + def __init__(self, value, column): + if column.column.choices == True: + if isinstance(value, dict): + self.id = value.get('id') + self.value = value.get('value') + elif isinstance(value, (tuple,list,)): + self.id = value[0] + self.value = value[1] + else: + self.id = None + self.value = value + else: + self.id = None + self.value = value + + def __unicode__(self): + return unicode(self.value) \ No newline at end of file diff --git a/tests/test_basic.py b/tests/test_basic.py index 08dd015..a1ffd9f 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -68,12 +68,12 @@ def test_basic(): assert not 'id' in r # missing data is available as default assert 'answer' in r - assert r['answer'] == 42 # note: different from prev. line! + assert r['answer'].value == 42 # note: different from prev. line! # all that still works when name overrides are used assert not 'c' in r assert 'count' in r - assert r['count'] == 1 + assert r['count'].value == 1 # changing an instance's base_columns does not change the class assert id(books.base_columns) != id(BookTable.base_columns) @@ -121,4 +121,23 @@ def test_sort(): books.base_columns['language'].sortable = False books.order_by = 'language' assert not books.order_by - 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' + +def test_choices(): + # unrestricted choices + class BookTable(tables.Table): + id = tables.Column() + name = tables.Column() + author = tables.Column(choices=True) + + books = BookTable([ + {'id': 1, 'name': 'A'}, + {'id': 2, 'author': (99, 'Mr. Vanderlay'), 'name': 'B'}, + {'id': 3, 'author': 'Mr. Vanderlay', 'name': 'C'}, + {'id': 4, 'author': {'id': 99, 'value': 'Mr. Vanderlay'}, 'name': 'D'}, + ]) + + assert [r['author'].id for r in books.rows] == [None, 99, None, 99] + assert [r['author'].value for r in books.rows] == [None, 'Mr. Vanderlay', 'Mr. Vanderlay', 'Mr. Vanderlay'] + + # TODO: restricted choices (planned) \ No newline at end of file diff --git a/tests/test_models.py b/tests/test_models.py index a9f2e71..ffe8eb7 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -152,11 +152,14 @@ def test_sort(): countries.order_by = ('custom1', 'custom2') assert countries.order_by == () +def test_choices(): + pass # TODO + def test_pagination(): pass -# TODO: foreignkey columns: simply support foreignkeys, tuples and id, name dicts; support column choices attribute to validate id-only # TODO: pagination # TODO: support function column sources both for modeltables (methods on model) and static tables (functions in dict) # TODO: manual base columns change -> update() call (add as example in docstr here) -> rebuild snapshot: is row cache, column cache etc. reset? -# TODO: throw an exception on invalid order_by \ No newline at end of file +# TODO: throw an exception on invalid order_by +# TODO: option to skip model table generation (leave off model option?) \ No newline at end of file -- 2.26.2