Add support for computed values.

This commit is contained in:
Bradley Ayers 2014-02-02 16:37:27 +11:00
parent 1db7b612a8
commit 86056eedeb
5 changed files with 96 additions and 9 deletions

View File

@ -4,7 +4,7 @@ from . import columns
from .config import RequestConfig
from .rows import BoundRows
from .utils import (Accessor, AttributeDict, build_request, cached_property,
OrderBy, OrderByTuple, segment, Sequence)
computed_values, OrderBy, OrderByTuple, segment, Sequence)
import copy
from django.core.paginator import Paginator
from django.db.models.fields import FieldDoesNotExist
@ -390,8 +390,9 @@ class TableBase(object):
default = self._meta.default
self.default = default
self.rows = BoundRows(data=self.data, table=self)
self.attrs = attrs
self.empty_text = empty_text
self.attrs = AttributeDict(computed_values(attrs if attrs is not None
else self._meta.attrs))
self.empty_text = empty_text if empty_text is not None else self._meta.empty_text
if sortable is not None:
warnings.warn("`sortable` is deprecated, use `orderable` instead.",
DeprecationWarning)
@ -454,7 +455,7 @@ class TableBase(object):
@property
def attrs(self):
return self._attrs if self._attrs is not None else self._meta.attrs
return self._attrs
@attrs.setter
def attrs(self, value):
@ -462,8 +463,7 @@ class TableBase(object):
@property
def empty_text(self):
return (self._empty_text if self._empty_text is not None
else self._meta.empty_text)
return self._empty_text
@empty_text.setter
def empty_text(self, value):

View File

@ -421,7 +421,7 @@ class AttributeDict(dict):
:rtype: `~django.utils.safestring.SafeUnicode` object
"""
return mark_safe(' '.join(['%s="%s"' % (k, escape(v))
return mark_safe(' '.join(['%s="%s"' % (k, escape(v if not callable(v) else v()))
for k, v in six.iteritems(self)]))
@ -551,3 +551,45 @@ def total_ordering(cls):
opfunc.__doc__ = getattr(int, opname).__doc__
setattr(cls, opname, opfunc)
return cls
def computed_values(d):
"""
Computes a new `dict` that has callable values replaced with the return values.
Simple example:
>>> compute_values({"foo": lambda: "bar"})
{"foo": "bar"}
Arbitrarily deep structures are supported. The logic is as follows:
1. If the value is callable, call it and make that the new value.
2. If the value is an instance of dict, use ComputableDict to compute its keys.
Example:
>>> def parents():
... return {
... "father": lambda: "Foo",
... "mother": "Bar"
... }
...
>>> a = {
... "name": "Brad",
... "parents": parents
... }
...
>>> computed_values(a)
{"name": "Brad", "parents": {"father": "Foo", "mother": "Bar"}}
:rtype: dict
"""
result = {}
for k, v in six.iteritems(d):
if callable(v):
v = v()
if isinstance(v, dict):
v = computed_values(v)
result[k] = v
return result

View File

@ -886,6 +886,25 @@ API Reference
class Meta:
attrs = {"class": "paleblue"}
.. versionadded:: 0.15.0
It's possible to use callables to create *dynamic* values. A few caveats:
- It's not supported for ``dict`` keys, i.e. only values.
- All values will be resolved on table instantiation.
Consider this example where a unique ``id`` is given to each instance
of the table::
import itertools
counter = itertools.count()
class UniqueIdTable(tables.Table):
name = tables.Column()
class Meta:
attrs = {"id": lambda: "table_%d" % next(counter)}
.. note::
This functionality is also available via the ``attrs`` keyword

View File

@ -7,6 +7,7 @@ from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
import django_tables2 as tables
from django_tables2.tables import DeclarativeColumnsMetaclass
import six
import itertools
core = Tests()
@ -118,6 +119,18 @@ def attrs():
assert {"c": "d"} == TestTable4([], attrs={"c": "d"}).attrs
@core.test
def attrs_support_computed_values():
counter = itertools.count()
class TestTable(tables.Table):
class Meta:
attrs = {"id": lambda: "test_table_%d" % next(counter)}
assert {"id": "test_table_0"} == TestTable([]).attrs
assert {"id": "test_table_1"} == TestTable([]).attrs
@core.test
def data_knows_its_name():
table = tables.Table([{}])

View File

@ -1,7 +1,8 @@
# coding: utf-8
from attest import assert_hook, raises, Tests
from django_tables2.utils import (Accessor, AttributeDict, OrderByTuple,
OrderBy, segment)
from django_tables2.utils import (Accessor, AttributeDict, computed_values,
OrderByTuple, OrderBy, segment)
import itertools
import six
@ -131,6 +132,18 @@ def attribute_dict_handles_escaping():
assert x.as_html() == 'x=""'x&"'
@utils.test
def compute_values_supports_shallow_structures():
x = computed_values({"foo": lambda: "bar"})
assert x == {"foo": "bar"}
@utils.test
def compute_values_supports_shallow_structures():
x = computed_values({"foo": lambda: {"bar": lambda: "baz"}})
assert x == {"foo": {"bar": "baz"}}
@utils.test
def segment_should_return_all_candidates():
assert set(segment(("a", "-b", "c"), {