Add cardinality to the pagination display, resolves #94

This commit is contained in:
Bradley Ayers 2012-09-15 08:53:37 +10:00
parent 38bfda136d
commit 9effe9efe8
12 changed files with 103 additions and 21 deletions

View File

@ -89,6 +89,7 @@ v0.12.0
- Accessor's now honor ``alters_data`` during resolving. Fixes issue that would
delete all your data when a column had an accessor of ``delete``
- Add ``default`` and ``value`` to context of ``TemplateColumn``
- Add cardinality indication to the pagination area of a table
v0.11.0
-------

View File

@ -1,8 +1,12 @@
# coding: utf-8
from .tables import Table
from .columns import (Column, CheckBoxColumn, DateColumn, DateTimeColumn,
LinkColumn, TemplateColumn, EmailColumn, URLColumn)
from .config import RequestConfig
from .utils import A, Attrs
from .views import SingleTableMixin, SingleTableView
# pylint: disable=W0611
from .tables import Table
from .columns import (BooleanColumn, Column, CheckBoxColumn, DateColumn,
DateTimeColumn, LinkColumn, TemplateColumn, EmailColumn,
URLColumn)
from .config import RequestConfig
from .utils import A, Attrs
from .views import SingleTableMixin, SingleTableView
__version__ = "0.11.0"

View File

@ -88,6 +88,10 @@ table.paleblue + ul.pagination > li:first-child {
margin-left: 0;
}
table.paleblue + ul.pagination > li.cardinality {
float: right;
}
table.paleblue > tbody > tr > td > span.true,
table.paleblue > tbody > tr > td > span.false {
background-position: top left;

View File

@ -1,16 +1,16 @@
# coding: utf-8
import copy
from django.core.paginator import Paginator
from django.db.models.fields import FieldDoesNotExist
from django.core.paginator import Paginator
from django.db.models.fields import FieldDoesNotExist
from django.utils.datastructures import SortedDict
from django.template import RequestContext
from django.template.loader import get_template
from django.utils.encoding import StrAndUnicode
from django.template import RequestContext
from django.template.loader import get_template
from django.utils.encoding import StrAndUnicode
import warnings
from .utils import (Accessor, AttributeDict, OrderBy, OrderByTuple, segment,
Sequence)
from .rows import BoundRows
from . import columns
from .utils import (Accessor, AttributeDict, cached_property, OrderBy,
OrderByTuple, segment, Sequence)
from .rows import BoundRows
from . import columns
QUERYSET_ACCESSOR_SEPARATOR = '__'
@ -114,6 +114,30 @@ class TableData(object):
"""
return self.data[key]
@cached_property
def verbose_name(self):
"""
The full (singular) name for the data.
Queryset data has its model's ``Meta.verbose_name`` honored. List data
is checked for a ``verbose_name`` attribute, and falls back to using
``"item"``.
"""
if hasattr(self, "queryset"):
return self.queryset.model._meta.verbose_name
return getattr(self.list, "verbose_name", "item")
@cached_property
def verbose_name_plural(self):
"""
The full (plural) name of the data.
This uses the same approach as ``verbose_name``.
"""
if hasattr(self, "queryset"):
return self.queryset.model._meta.verbose_name_plural
return getattr(self.list, "verbose_name_plural", "items")
class DeclarativeColumnsMetaclass(type):
"""

View File

@ -47,19 +47,27 @@
{% endblock table %}
{% if table.page %}
{% with table.page.paginator.count as total %}
{% with table.page|length as count %}
{% block pagination %}
<ul class="pagination">
{% if table.page.has_previous %}
{% nospaceless %}{% block pagination.previous %}<li class="previous"><a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">{% trans "Previous" %}</a></li>{% endblock pagination.previous %}{% endnospaceless %}
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% nospaceless %}{% block pagination.current %}<li class="current">{% blocktrans with current=table.page.number total=table.paginator.num_pages %}Page {{ current }} of {{ total }}{% endblocktrans %}</li>{% endblock pagination.current %}{% endnospaceless %}
{% endif %}
{% if table.page.has_next %}
{% nospaceless %}{% block pagination.next %}<li class="next"><a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">{% trans "Next" %}</a></li>{% endblock pagination.next %}{% endnospaceless %}
{% endif %}
{% nospaceless %}{% block pagination.cardinality %}<li class="cardinality">{% if total != count %}{{ table.page|length }} of {% endif %}{{ total }} {% if total == 1 %}{{ table.data.verbose_name }}{% else %}{{ table.data.verbose_name_plural }}{% endif %}</li>{% endblock pagination.cardinality %}{% endnospaceless %}
</ul>
{% endblock pagination %}
{% endwith %}
{% endwith %}
</div>
{% endif %}
{% endspaceless %}

View File

@ -401,3 +401,18 @@ def segment(sequence, aliases):
continue
else:
yield [valias]
class cached_property(object):
"""
Decorator that creates converts a method with a single
self argument into a property cached on the instance.
Taken directly from Django 1.4.
"""
def __init__(self, func):
self.func = func
def __get__(self, instance, type):
res = instance.__dict__[self.func.__name__] = self.func(instance)
return res

View File

@ -3,4 +3,6 @@ from django.contrib import admin
from .models import Country
admin.site.register(Country)
class CountryAdmin(admin.ModelAdmin):
list_per_page = 2
admin.site.register(Country, CountryAdmin)

View File

@ -1,4 +1,5 @@
# coding: utf-8
from __future__ import unicode_literals
from django.db import models
from django.utils.translation import ugettext_lazy as _
@ -6,13 +7,13 @@ from django.utils.translation import ugettext_lazy as _
class Country(models.Model):
"""Represents a geographical Country"""
name = models.CharField(max_length=100)
population = models.PositiveIntegerField(verbose_name=u"Población")
population = models.PositiveIntegerField(verbose_name="población")
tz = models.CharField(max_length=50)
visits = models.PositiveIntegerField()
commonwealth = models.NullBooleanField()
class Meta:
verbose_name_plural = _("Countries")
verbose_name_plural = _("countries")
def __unicode__(self):
return self.name

View File

@ -25,6 +25,10 @@ class Person(models.Model):
safe = models.CharField(
max_length=200, blank=True, verbose_name=mark_safe("<b>Safe</b>"))
class Meta:
verbose_name = "person"
verbose_name_plural = "people"
def __unicode__(self):
return self.first_name

View File

@ -113,6 +113,13 @@ def attrs():
assert {"c": "d"} == TestTable4([], attrs={"c": "d"}).attrs
@core.test
def data_knows_its_name():
table = tables.Table([{}])
assert table.data.verbose_name == "item"
assert table.data.verbose_name_plural == "items"
@core.test
def datasource_untouched():
"""Ensure that data that is provided to the table (the datasource) is not

View File

@ -76,7 +76,7 @@ def mixins():
@models.test
def verbose_name():
def column_verbose_name():
"""
When using queryset data as input for a table, default to using model field
verbose names rather than an autogenerated string based on the column name.
@ -140,6 +140,13 @@ def verbose_name():
assert "translation test lazy" == table.columns["trans_test_lazy"].verbose_name
@models.test
def data_verbose_name():
table = tables.Table(Person.objects.all())
assert table.data.verbose_name == "person"
assert table.data.verbose_name_plural == "people"
@models.test
def field_choices_used_to_translated_value():
"""

View File

@ -108,15 +108,18 @@ def custom_rendering():
@templates.test
def render_table_templatetag():
# ensure it works with a multi-order-by
request = factory.get('/')
table = CountryTable(MEMORY_DATA, order_by=('name', 'population'))
RequestConfig(request).configure(table)
template = Template('{% load django_tables2 %}{% render_table table %}')
html = template.render(Context({'request': factory.get('/'), 'table': table}))
html = template.render(Context({'request': request, 'table': table}))
root = parse(html)
assert len(root.findall('.//thead/tr')) == 1
assert len(root.findall('.//thead/tr/th')) == 4
assert len(root.findall('.//tbody/tr')) == 4
assert len(root.findall('.//tbody/tr/td')) == 16
assert root.find('ul[@class="pagination"]/li[@class="cardinality"]').text == '4 items'
# no data with no empty_text
table = CountryTable([])
@ -128,9 +131,11 @@ def render_table_templatetag():
assert len(root.findall('.//tbody/tr')) == 0
# no data WITH empty_text
request = factory.get('/')
table = CountryTable([], empty_text='this table is empty')
RequestConfig(request).configure(table)
template = Template('{% load django_tables2 %}{% render_table table %}')
html = template.render(Context({'request': factory.get('/'), 'table': table}))
html = template.render(Context({'request': request, 'table': table}))
root = parse(html)
assert len(root.findall('.//thead/tr')) == 1
assert len(root.findall('.//thead/tr/th')) == 4