Permalink
Browse files

Instantiate ``BoundColumn``s only once when table is first instantiat…

…ed instead of every time a row is being read.
  • Loading branch information...
1 parent a0503aa commit 98d7d282fb22eb449f48a340262b8c10d32a85df @selwin committed Apr 27, 2012
Showing with 28 additions and 29 deletions.
  1. +22 −13 django_tables2/columns.py
  2. +4 −14 django_tables2/rows.py
  3. +2 −2 django_tables2/tables.py
@@ -5,11 +5,13 @@
from django.template import RequestContext, Context, Template
from django.utils.encoding import force_unicode, StrAndUnicode
from django.utils.datastructures import SortedDict
+from django.utils.functional import curry
from django.utils.text import capfirst
from django.utils.html import escape
from django.utils.safestring import mark_safe, SafeData
from itertools import ifilter, islice
import warnings
+import inspect
from .templatetags.django_tables2 import title
from .utils import A, AttributeDict, Attrs, OrderBy, OrderByTuple, Sequence
@@ -661,7 +663,24 @@ class BoundColumns(object):
:param table: the table containing the columns
"""
def __init__(self, table):
- self.table = table
+ self.table = table
+ self.columns = SortedDict()
+ for name, column in self.table.base_columns.iteritems():
+ self.columns[name] = BoundColumn(self.table, column, name)
+
+ # A list of column names in the correct sequence that they should be
+ # rendered in the table.
+ self.sequence = (self.table.sequence or Sequence(('...', )))
+ self.sequence.expand(self.table.base_columns.keys())
+
+ #Prepare each column's ``render`` function and its expected argument
+ #so they can be easily called when each row is iterated.
+ funcs = ifilter(curry(hasattr, inspect), ('getfullargspec', 'getargspec'))
+ spec = getattr(inspect, next(funcs))
+ for name, bound_column in self.iteritems():
+ bound_column.render = getattr(self.table, 'render_' + bound_column.name,
+ bound_column.column.render)
+ bound_column.render_args = [arg for arg in spec(bound_column.render).args][1:]
def iternames(self):
return (name for name, column in self.iteritems())
@@ -688,19 +707,9 @@ def iteritems(self):
consideration all of the ordering and filtering modifiers that a table
supports (e.g. ``exclude`` and ``sequence``).
"""
- # First we build a sorted dict of all the columns that we need.
- columns = SortedDict()
- for name, column in self.table.base_columns.iteritems():
- columns[name] = BoundColumn(self.table, column, name)
-
- # A list of column names in the correct sequence that they should be
- # rendered in the table.
- sequence = (self.table.sequence or Sequence(('...', )))
- sequence.expand(self.table.base_columns.keys())
-
- for name in sequence:
+ for name in self.sequence:
if name not in self.table.exclude:
- yield (name, columns[name])
+ yield (name, self.columns[name])
def items(self):
return list(self.iteritems())
@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-
-import inspect
-from itertools import imap, ifilter
+from itertools import imap
from django.db import models
from django.db.models.fields import FieldDoesNotExist
-from django.utils.functional import curry
from django.utils.safestring import EscapeUnicode, SafeData
from .utils import A
@@ -137,19 +135,11 @@ def value():
'bound_row': lambda: self,
'table': lambda: self._table,
}
- render_FOO = 'render_' + bound_column.name
- render = getattr(self.table, render_FOO, bound_column.column.render)
- # just give a list of all available methods
- funcs = ifilter(curry(hasattr, inspect), ('getfullargspec', 'getargspec'))
- spec = getattr(inspect, next(funcs))
- # only provide the arguments that the func is interested in
kw = {}
- for name in spec(render).args:
- if name == 'self':
- continue
- kw[name] = kwargs[name]()
- return render(**kw)
+ for arg_name in bound_column.render_args:
+ kw[arg_name] = kwargs[arg_name]()
+ return bound_column.render(**kw)
def __contains__(self, item):
"""Check by both row object and column name."""
@@ -232,6 +232,8 @@ def __init__(self, data, order_by=None, orderable=None, empty_text=None,
order_by_field=None, page_field=None, per_page_field=None,
template=None, sortable=None):
super(Table, self).__init__()
+ self.exclude = exclude or ()
+ self.sequence = sequence
self.data = self.TableDataClass(data=data, table=self)
self.rows = BoundRows(self.data)
self.columns = BoundColumns(self)
@@ -251,8 +253,6 @@ def __init__(self, data, order_by=None, orderable=None, empty_text=None,
# definition. Note that this is different from forms, where the
# copy is made available in a ``fields`` attribute.
self.base_columns = copy.deepcopy(type(self).base_columns)
- self.exclude = exclude or ()
- self.sequence = sequence
# `None` value for order_by means no order is specified. This means we
# `shouldn't touch our data's ordering in any way. *However*
# `table.order_by = None` means "remove any ordering from the data"

0 comments on commit 98d7d28

Please sign in to comment.