Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3936e11

Browse files
committed
Fixed issue jieter#32, bumped to v0.7.8
1 parent 7a4bd5a commit 3936e11

File tree

9 files changed

+154
-117
lines changed

9 files changed

+154
-117
lines changed

README.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,17 @@ Building the documentation
5959
If you want to build the docs from within a virtualenv, use::
6060

6161
make html SPHINXBUILD="python $(which sphinx-build)"
62+
63+
64+
Change log
65+
==========
66+
67+
v0.7.8
68+
------
69+
70+
- Tables now support using both ``sequence`` and ``exclude`` (issue #32).
71+
- ``Sequence`` class moved to ``django_tables2/utils.py``.
72+
- Table instances now support modification to the ``exclude`` property.
73+
- Removed ``BoundColumns._spawn_columns``.
74+
- ``Table.data``, ``Table.rows``, and ``Table.columns`` are now attributes
75+
rather than properties.

django_tables2/columns.py

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from django.utils.safestring import mark_safe
88
from django.template import RequestContext, Context, Template
99
from django.db.models.fields import FieldDoesNotExist
10-
from .utils import OrderBy, A, AttributeDict
11-
from itertools import ifilter
10+
from .utils import A, AttributeDict, OrderBy, Sequence
11+
from itertools import ifilter, islice
1212

1313

1414
class Column(object):
@@ -474,24 +474,6 @@ class BoundColumns(object):
474474
"""
475475
def __init__(self, table):
476476
self.table = table
477-
# ``self._columns`` attribute stores the bound columns (columns that
478-
# have a real name, )
479-
self._columns = SortedDict()
480-
481-
def _spawn_columns(self):
482-
"""
483-
(re)build the "_bound_columns" cache of :class:`.BoundColumn` objects
484-
(note that :attr:`.base_columns` might have changed since last time);
485-
creating :class:`.BoundColumn` instances can be costly, so we reuse
486-
existing ones.
487-
"""
488-
columns = SortedDict()
489-
for name, column in self.table.base_columns.items():
490-
if name in self._columns:
491-
columns[name] = self._columns[name]
492-
else:
493-
columns[name] = BoundColumn(self.table, column, name)
494-
self._columns = columns
495477

496478
def iternames(self):
497479
return (name for name, column in self.iteritems())
@@ -513,12 +495,24 @@ def iteritems(self):
513495
"""
514496
Return an iterator of ``(name, column)`` pairs (where ``column`` is a
515497
:class:`.BoundColumn` object).
498+
499+
This method is the mechanism for retrieving columns that takes into
500+
consideration all of the ordering and filtering modifiers that a table
501+
supports (e.g. ``exclude`` and ``sequence``).
516502
"""
517-
self._spawn_columns()
518-
if self.table.sequence:
519-
return ((x, self._columns[x]) for x in self.table.sequence)
520-
else:
521-
return self._columns.iteritems()
503+
# First we build a sorted dict of all the columns that we need.
504+
columns = SortedDict()
505+
for name, column in self.table.base_columns.iteritems():
506+
columns[name] = BoundColumn(self.table, column, name)
507+
508+
# A list of column names in the correct sequence that they should be
509+
# rendered in the table.
510+
sequence = (self.table.sequence or Sequence(('...', )))
511+
sequence.expand(self.table.base_columns.keys())
512+
513+
for name in sequence:
514+
if name not in self.table.exclude:
515+
yield (name, columns[name])
522516

523517
def items(self):
524518
return list(self.iteritems())
@@ -533,7 +527,7 @@ def itersortable(self):
533527
conjunction with e.g. ``{{ forloop.last }}`` (the last column might not
534528
be the actual last that is rendered).
535529
"""
536-
return ifilter(lambda x: x.sortable, self.all())
530+
return ifilter(lambda x: x.sortable, self.iterall())
537531

538532
def sortable(self):
539533
return list(self.itersortable())
@@ -545,14 +539,14 @@ def itervisible(self):
545539
546540
This is geared towards table rendering.
547541
"""
548-
return ifilter(lambda x: x.visible, self.all())
542+
return ifilter(lambda x: x.visible, self.iterall())
549543

550544
def visible(self):
551545
return list(self.itervisible())
552546

553547
def __iter__(self):
554548
"""
555-
Convenience API with identical functionality to :meth:`visible`.
549+
Convenience API, alias of :meth:`.itervisible`.
556550
"""
557551
return self.itervisible()
558552

@@ -574,7 +568,6 @@ def __len__(self):
574568
Return how many :class:`BoundColumn` objects are contained (and
575569
visible).
576570
"""
577-
self._spawn_columns()
578571
return len(self.visible())
579572

580573
def __getitem__(self, index):
@@ -588,11 +581,16 @@ def __getitem__(self, index):
588581
columns['speed'] # returns a bound column with name 'speed'
589582
columns[0] # returns the first column
590583
"""
591-
self._spawn_columns()
592584
if isinstance(index, int):
593-
return self._columns.value_for_index(index)
585+
try:
586+
return next(islice(self.iterall(), index, index+1))
587+
except StopIteration:
588+
raise IndexError
594589
elif isinstance(index, basestring):
595-
return self._columns[index]
590+
for column in self.iterall():
591+
if column.name == index:
592+
return column
593+
raise KeyError("Column with name '%s' does not exist." % index)
596594
else:
597-
raise TypeError(u'row indices must be integers or str, not %s' %
598-
index.__class__.__name__)
595+
raise TypeError(u'row indices must be integers or str, not %s'
596+
% index.__class__.__name__)

django_tables2/rows.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# -*- coding: utf-8 -*-
2-
from itertools import imap, ifilter
32
import inspect
3+
from itertools import imap, ifilter
44
from django.db import models
55
from django.db.models.fields import FieldDoesNotExist
6-
from django.utils.safestring import EscapeUnicode, SafeData
76
from django.utils.functional import curry
7+
from django.utils.safestring import EscapeUnicode, SafeData
88
from .utils import A
99

1010

django_tables2/tables.py

Lines changed: 8 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,20 @@
11
# -*- coding: utf-8 -*-
22
import copy
33
from django.core.paginator import Paginator
4-
from django.utils.datastructures import SortedDict
4+
from django.db.models.query import QuerySet
55
from django.http import Http404
6-
from django.template.loader import get_template
6+
from django.utils.datastructures import SortedDict
77
from django.template import Context
8+
from django.template.loader import get_template
89
from django.utils.encoding import StrAndUnicode
9-
from django.db.models.query import QuerySet
10-
from itertools import chain
11-
from .utils import OrderBy, OrderByTuple, Accessor, AttributeDict
10+
from .utils import Accessor, AttributeDict, OrderBy, OrderByTuple, Sequence
1211
from .rows import BoundRows, BoundRow
1312
from .columns import BoundColumns, Column
1413

1514

1615
QUERYSET_ACCESSOR_SEPARATOR = '__'
1716

1817

19-
class Sequence(list):
20-
"""
21-
Represents a column sequence, e.g. ("first_name", "...", "last_name")
22-
23-
This is used to represent ``Table.Meta.sequence`` or the Table
24-
constructors's ``sequence`` keyword argument.
25-
26-
The sequence must be a list of column names and is used to specify the
27-
order of the columns on a table. Optionally a "..." item can be inserted,
28-
which is treated as a *catch-all* for column names that aren't explicitly
29-
specified.
30-
"""
31-
def expand(self, columns):
32-
"""
33-
Expands the "..." item in the sequence into the appropriate column
34-
names that should be placed there.
35-
36-
:raises: ``ValueError`` if the sequence is invalid for the columns.
37-
"""
38-
# validation
39-
if self.count("...") > 1:
40-
raise ValueError("'...' must be used at most once in a sequence.")
41-
elif "..." in self:
42-
# Check for columns in the sequence that don't exist in *columns*
43-
extra = (set(self) - set(("...", ))).difference(columns)
44-
if extra:
45-
raise ValueError(u"sequence contains columns that do not exist"
46-
u" in the table. Remove '%s'."
47-
% "', '".join(extra))
48-
else:
49-
diff = set(self) ^ set(columns)
50-
if diff:
51-
raise ValueError(u"sequence does not match columns. Fix '%s' "
52-
u"or possibly add '...'." % "', '".join(diff))
53-
# everything looks good, let's expand the "..." item
54-
columns = columns[:] # don't modify
55-
head = []
56-
tail = []
57-
target = head # start by adding things to the head
58-
for name in self:
59-
if name == "...":
60-
# now we'll start adding elements to the tail
61-
target = tail
62-
continue
63-
else:
64-
target.append(columns.pop(columns.index(name)))
65-
self[:] = list(chain(head, columns, tail))
66-
67-
6818
class TableData(object):
6919
"""
7020
Exposes a consistent API for :term:`table data`. It currently supports a
@@ -284,9 +234,9 @@ def obj_list(request):
284234
def __init__(self, data, order_by=None, sortable=None, empty_text=None,
285235
exclude=None, attrs=None, sequence=None, prefix=None,
286236
order_by_field=None, page_field=None, per_page_field=None):
287-
self._rows = BoundRows(self)
288-
self._columns = BoundColumns(self)
289-
self._data = self.TableDataClass(data=data, table=self)
237+
self.rows = BoundRows(self)
238+
self.columns = BoundColumns(self)
239+
self.data = self.TableDataClass(data=data, table=self)
290240
self.attrs = attrs
291241
self.empty_text = empty_text
292242
self.sortable = sortable
@@ -299,9 +249,6 @@ def __init__(self, data, order_by=None, sortable=None, empty_text=None,
299249
# copy is made available in a ``fields`` attribute.
300250
self.base_columns = copy.deepcopy(self.__class__.base_columns)
301251
self.exclude = exclude or ()
302-
for ex in self.exclude:
303-
if ex in self.base_columns:
304-
self.base_columns.pop(ex)
305252
self.sequence = sequence
306253
if order_by is None:
307254
self.order_by = self._meta.order_by
@@ -337,14 +284,6 @@ def attrs(self):
337284
def attrs(self, value):
338285
self._attrs = value
339286

340-
@property
341-
def columns(self):
342-
return self._columns
343-
344-
@property
345-
def data(self):
346-
return self._data
347-
348287
@property
349288
def empty_text(self):
350289
return (self._empty_text if self._empty_text is not None
@@ -377,7 +316,7 @@ def order_by(self, value):
377316
new.append(ob)
378317
order_by = OrderByTuple(new)
379318
self._order_by = order_by
380-
self._data.order_by(order_by)
319+
self.data.order_by(order_by)
381320

382321
@property
383322
def order_by_field(self):
@@ -447,10 +386,6 @@ def prefixed_page_field(self):
447386
def prefixed_per_page_field(self):
448387
return u"%s%s" % (self.prefix, self.per_page_field)
449388

450-
@property
451-
def rows(self):
452-
return self._rows
453-
454389
@property
455390
def sequence(self):
456391
return (self._sequence if self._sequence is not None

django_tables2/utils.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,59 @@
11
# -*- coding: utf-8 -*-
2-
from django.utils.datastructures import SortedDict
32
from django.template import Context
3+
from django.utils.datastructures import SortedDict
44
from django.utils.encoding import force_unicode, StrAndUnicode
5-
from django.utils.safestring import mark_safe
65
from django.utils.html import escape
6+
from django.utils.safestring import mark_safe
7+
from itertools import chain
8+
9+
10+
class Sequence(list):
11+
"""
12+
Represents a column sequence, e.g. ("first_name", "...", "last_name")
13+
14+
This is used to represent ``Table.Meta.sequence`` or the Table
15+
constructors's ``sequence`` keyword argument.
716
17+
The sequence must be a list of column names and is used to specify the
18+
order of the columns on a table. Optionally a "..." item can be inserted,
19+
which is treated as a *catch-all* for column names that aren't explicitly
20+
specified.
21+
"""
22+
def expand(self, columns):
23+
"""
24+
Expands the "..." item in the sequence into the appropriate column
25+
names that should be placed there.
826
9-
__all__ = ('BaseTable', 'options')
27+
:raises: ``ValueError`` if the sequence is invalid for the columns.
28+
"""
29+
# validation
30+
if self.count("...") > 1:
31+
raise ValueError("'...' must be used at most once in a sequence.")
32+
elif "..." in self:
33+
# Check for columns in the sequence that don't exist in *columns*
34+
extra = (set(self) - set(("...", ))).difference(columns)
35+
if extra:
36+
raise ValueError(u"sequence contains columns that do not exist"
37+
u" in the table. Remove '%s'."
38+
% "', '".join(extra))
39+
else:
40+
diff = set(self) ^ set(columns)
41+
if diff:
42+
raise ValueError(u"sequence does not match columns. Fix '%s' "
43+
u"or possibly add '...'." % "', '".join(diff))
44+
# everything looks good, let's expand the "..." item
45+
columns = columns[:] # don't modify
46+
head = []
47+
tail = []
48+
target = head # start by adding things to the head
49+
for name in self:
50+
if name == "...":
51+
# now we'll start adding elements to the tail
52+
target = tail
53+
continue
54+
else:
55+
target.append(columns.pop(columns.index(name)))
56+
self[:] = list(chain(head, columns, tail))
1057

1158

1259
class OrderBy(str):

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
# The short X.Y version.
5757
version = '0.7'
5858
# The full version, including alpha/beta/rc tags.
59-
release = '0.7.7'
59+
release = '0.7.8'
6060

6161
# The language for content autogenerated by Sphinx. Refer to documentation
6262
# for a list of supported languages.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
setup(
66
name='django-tables2',
7-
version='0.7.7',
7+
version='0.7.8',
88
description='Table framework for Django',
99

1010
author='Bradley Ayers',

0 commit comments

Comments
 (0)