* Added example project to demonstrate usage.

* {% render_table %} now raises an exception if a RequestContext 'request' isn't in the template context.
* Added better documentation in the "Slow Start Guide" about pagination and ordering
* Fixed some styling issues with the paleblue theme
* Added instructions on how to build the docs when using a virtualenv
* bumped version to v0.4.1
This commit is contained in:
Bradley Ayers 2011-05-12 07:02:26 +10:00
parent 19f1242f71
commit 7c82e63873
19 changed files with 491 additions and 23 deletions

View File

@ -9,3 +9,11 @@ has native support for pagination and sorting. It does for HTML tables what
Documentation_ is available on http://readthedocs.org
.. _Documentation: http://readthedocs.org/docs/django-tables/en/latest/
Building the documentation
==========================
If you want to build the docs from within a virtualenv, use::
make html SPHINXBUILD="python $(which sphinx-build)"

View File

@ -274,13 +274,13 @@ class TemplateColumn(Column):
Both columns will have the same output.
.. important::
In order to use template tags or filters that require a
``RequestContext``, the table **must** be rendered via
:ref:`{% render_table %} <template-tags.render_table>`.
"""
"""
def __init__(self, template_code=None, **extra):
super(TemplateColumn, self).__init__(**extra)
self.template_code = template_code

View File

@ -2,7 +2,11 @@ table.paleblue {
border-collapse: collapse;
border-color: #CCC;
border: 1px solid #DDD;
font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
}
table.paleblue,
table.paleblue + ul.pagination {
font: normal 11px/14px 'Lucida Grande', Verdana, Arial, sans-serif;
}
table.paleblue a:link,
@ -23,6 +27,7 @@ table.paleblue th {
line-height: 13px;
border-bottom: 1px solid #EEE;
border-left: 1px solid #DDD;
text-align: left;
}
table.paleblue thead th:first-child,
@ -69,8 +74,10 @@ table.paleblue tr.even {
table.paleblue + ul.pagination {
background: white url(../img/pagination-bg.gif) left 180% repeat-x;
overflow: auto;
margin: 0;
padding: 10px;
border: 1px solid #DDD;
list-style: none;
}
table.paleblue + ul.pagination > li {

View File

@ -90,14 +90,17 @@ class RenderTableNode(template.Node):
def render(self, context):
table = self.table_var.resolve(context)
request = context.get('request', None)
context = template.Context({'request': request, 'table': table})
if 'request' not in context:
raise AssertionError('{% render_table %} requires that the '
'template context contains the HttpRequest in'
' a "request" variable, check your '
' TEMPLATE_CONTEXT_PROCESSORS setting.')
context = template.Context({'request': context['request'], 'table': table})
try:
table.request = request
table.request = context['request']
return get_template('django_tables/table.html').render(context)
finally:
pass
#del table.request
del table.request
@register.tag
@ -105,6 +108,6 @@ def render_table(parser, token):
try:
_, table_var_name = token.contents.split()
except ValueError:
raise template.TemplateSyntaxError,\
"%r tag requires a single argument" % token.contents.split()[0]
raise (template.TemplateSyntaxError,
'%r tag requires a single argument' % token.contents.split()[0])
return RenderTableNode(table_var_name)

View File

@ -50,9 +50,9 @@ project = u'django-tables'
# built documents.
#
# The short X.Y version.
version = '0.4.0'
version = '0.4.1'
# The full version, including alpha/beta/rc tags.
release = '0.4.0'
release = '0.4.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -90,16 +90,36 @@ In your template, the easiest way to :term:`render` the table is via the
| Mexico | 107 | UTC -6 | 0 |
+--------------+------------+---------+--------+
This approach is easy, but it's not fully featured. For slightly more effort,
you can render a table with sortable columns. For this, you must use the
template tag.
This approach is easy, but it's not fully featured (e.g. no pagination, no
sorting). Don't worry it's very easy to add these. First, you must render the
table via the :ref:`template tag <template-tags.render_table>` rather than
``as_html()``:
.. code-block:: django
{% load django_tables %}
{% render_table table %}
See :ref:`template-tags.render_table` for more information.
.. note::
``{% render_table %}`` requires that the ``TEMPLATE_CONTEXT_PROCESSORS``
setting contains ``"django.core.context_processors.request"``. See
:ref:`template-tags.render_table` for details.
This is all that's required for the template, but in the view you'll need to
tell the table to which column to order by, and which page of data to display
(pagination). This is achieved as follows:
.. code-block:: python
def home(request):
countries = Country.objects.all()
table = CountryTable(countries, order_by=request.GET.get('sort'))
table.paginate(page=request.GET.get('page', 1))
return render_to_response('home.html', {'table': table},
context_instance=RequestContext(request))
See :ref:`ordering`, and :ref:`pagination` for more information.
The table will be rendered, but chances are it will still look quite ugly. An
easy way to make it pretty is to use the built-in *paleblue* theme. For this to
@ -167,8 +187,8 @@ same thing:
class Meta:
order_by = 'name'
The following allows the ``Meta.order_by`` option to be overridden on a
per-instance basis.
By passing in a value for ``order_by`` into the ``Table`` constructor, the
``Meta.order_by`` option can be overridden on a per-instance basis.
.. code-block:: python
@ -177,7 +197,21 @@ per-instance basis.
table = SimpleTable(..., order_by='name')
Finally the attribute method overrides both of the previous approaches.
This approach allows column sorting to be enabled for use with the ``{%
render_table %}`` template tag. The template tag converts column headers into
hyperlinks that add the querystring parameter ``sort`` to the current URL. This
means your view will need to look something like:
.. code-block:: python
def home(request):
countries = Country.objects.all()
table = CountryTable(countries, order_by=request.GET.get('sort'))
return render_to_response('home.html', {'table': table},
context_instance=RequestContext(request))
The final approach allows both of the previous approaches to be overridden. The
instance property ``order_by`` can be
.. code-block:: python
@ -189,9 +223,30 @@ Finally the attribute method overrides both of the previous approaches.
----
By default all table columns support sorting. This means that the headers for
columns are rendered as links which allow that column to be toggled as the
between ascending and descending ordering preference.
By default all table columns support sorting. This means that if you're using
the :ref:`template tag <template-tags.render_table>` to render the table,
users can sort the table based on any column by clicking the corresponding
header link.
In some cases this may not be appropriate. For example you may have a column
which displays data that isn't in the dataset:
.. code-block:: python
class SimpleTable(tables.Table):
name = tables.Column()
lucky_rating = tables.Column()
class Meta:
sortable = False
def render_lucky_rating(self):
import random
return random.randint(1, 10)
In these types of scenarios, it's simply not possible to sort the table based
on column data that isn't in the dataset. The solution is to disable sorting
for these columns.
Sorting can be disabled on a column, table, or table instance basis via the
:attr:`.Table.Meta.sortable` option.
@ -448,6 +503,26 @@ rendered. It adds a ``request`` attribute to the table, which allows
:class:`Column` objects to have access to a ``RequestContext``. See
:class:`.TemplateColumn` for an example.
This tag requires that the template in which it's rendered contains the
``HttpRequest`` inside a ``request`` variable. This can be achieved by ensuring
the ``TEMPLATE_CONTEXT_PROCESSORS`` setting contains
``"django.core.context_processors.request"``. By default it is not included,
and the setting itself is not even defined within your project's
``settings.py``. To resolve this simply add the following to your
``settings.py``:
.. code-block:: python
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages",
"django.core.context_processors.request",
)
.. _template-tags.set_url_param:

0
example/__init__.py Normal file
View File

0
example/app/__init__.py Normal file
View File

5
example/app/admin.py Normal file
View File

@ -0,0 +1,5 @@
from django.contrib import admin
from .models import Country
admin.site.register(Country)

View File

@ -0,0 +1,42 @@
[
{
"pk": 1,
"model": "app.country",
"fields": {
"tz": "Australia/Brisbane",
"name": "Australia",
"visits": 2,
"population": 20000000
}
},
{
"pk": 2,
"model": "app.country",
"fields": {
"tz": "NZST",
"name": "New Zealand",
"visits": 1,
"population": 12000000
}
},
{
"pk": 3,
"model": "app.country",
"fields": {
"tz": "CAT",
"name": "Africa",
"visits": 0,
"population": 1000010000
}
},
{
"pk": 4,
"model": "app.country",
"fields": {
"tz": "UTC\u22123.5",
"name": "Canada",
"visits": 1,
"population": 34447000
}
}
]

15
example/app/models.py Normal file
View File

@ -0,0 +1,15 @@
from django.db import models
class Country(models.Model):
"""Represents a geographical Country"""
name = models.CharField(max_length=100)
population = models.PositiveIntegerField()
tz = models.CharField(max_length=50)
visits = models.PositiveIntegerField()
class Meta:
verbose_name_plural = 'Countries'
def __unicode__(self):
return self.name

13
example/app/tables.py Normal file
View File

@ -0,0 +1,13 @@
import django_tables as tables
class CountryTable(tables.Table):
name = tables.Column()
population = tables.Column()
tz = tables.Column(verbose_name='Time Zone')
visits = tables.Column()
class ThemedCountryTable(CountryTable):
class Meta:
attrs = {'class': 'paleblue'}

16
example/app/tests.py Normal file
View File

@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

26
example/app/views.py Normal file
View File

@ -0,0 +1,26 @@
from django.shortcuts import render_to_response
from django.template import RequestContext
from .tables import CountryTable, ThemedCountryTable
from .models import Country
def home(request):
order_by = request.GET.get('sort')
queryset = Country.objects.all()
#
example1 = CountryTable(queryset, order_by=order_by)
#
example2 = CountryTable(queryset, order_by=order_by)
example2.paginate(page=request.GET.get('page', 1), per_page=3)
#
example3 = ThemedCountryTable(queryset, order_by=order_by)
#
example4 = ThemedCountryTable(queryset, order_by=order_by)
example4.paginate(page=request.GET.get('page', 1), per_page=3)
return render_to_response('example.html', {
'example1': example1,
'example2': example2,
'example3': example3,
'example4': example4,
}, context_instance=RequestContext(request))

14
example/manage.py Normal file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env python
from django.core.management import execute_manager
import imp
try:
imp.find_module('settings') # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
sys.exit(1)
import settings
if __name__ == "__main__":
execute_manager(settings)

163
example/settings.py Normal file
View File

@ -0,0 +1,163 @@
# import django_tables
from os.path import dirname, join, abspath
import sys
ROOT = dirname(abspath(__file__))
sys.path.insert(0, join(ROOT, '..'))
import django_tables
sys.path.pop(0)
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', 'your_email@example.com'),
)
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': join(ROOT, 'database.sqlite'), # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# On Unix systems, a value of None will cause Django to use the same
# timezone as the operating system.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale
USE_L10N = True
# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/home/media/media.lawrence.com/media/"
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
MEDIA_URL = ''
# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/home/media/media.lawrence.com/static/"
STATIC_ROOT = ''
# URL prefix for static files.
# Example: "http://media.lawrence.com/static/"
STATIC_URL = '/static/'
# URL prefix for admin static files -- CSS, JavaScript and images.
# Make sure to use a trailing slash.
# Examples: "http://foo.com/static/admin/", "/static/admin/".
ADMIN_MEDIA_PREFIX = '/static/admin/'
# Additional locations of static files
STATICFILES_DIRS = (
# Put strings here, like "/home/html/static" or "C:/www/django/static".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
# Make this unique, and don't share it with anybody.
SECRET_KEY = '=nzw@mkqk)tz+_#vf%li&8sn7yn8z7!2-4njuyf1rxs*^muhvh'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
# 'django.template.loaders.eggs.Loader',
)
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages",
"django.core.context_processors.request",
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
ROOT_URLCONF = 'example.urls'
TEMPLATE_DIRS = (
join(ROOT, 'templates'),
)
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'example.app',
'django_tables',
)
# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}

View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ LANGUAGE_CODE }}" xml:lang="{{ LANGUAGE_CODE }}">
<head>
<title>django-tables examples</title>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}django_tables/themes/paleblue/css/screen.css" />
<style type="text/css">
pre {
background-color: #D8F0FF;
border: 1px solid #9BBBD5;
padding: 8px;
}
</style>
</head>
<body>
<h1><tt>django-tables</tt> examples</h1>
<p>This page demonstrates various types of tables being rendered via
<tt>django-tables</tt>.</p>
<h2>Example 1 — QuerySet</h2>
<h3>via <tt>as_html()</tt></h3>
<pre>{% templatetag openvariable %} example1.as_html {% templatetag closevariable %}</pre>
{{ example1.as_html }}
<h3>via template tag</h3>
<pre>{% templatetag openblock %} load django_tables {% templatetag closeblock %}
{% templatetag openblock %} render_table example1 {% templatetag closeblock %}</pre>
{% load django_tables %}
{% render_table example1 %}
<h2>Example 2 — QuerySet + pagination</h2>
<h3>via <tt>as_html()</tt></h3>
<pre>{% templatetag openvariable %} example2.as_html {% templatetag closevariable %}</pre>
{{ example2.as_html }}
<h3>via template tag</h3>
<pre>{% templatetag openblock %} load django_tables {% templatetag closeblock %}
{% templatetag openblock %} render_table example2 {% templatetag closeblock %}</pre>
{% load django_tables %}
{% render_table example2 %}
<h2>Example 3 — QuerySet + paleblue theme</h2>
<h3>via <tt>as_html()</tt></h3>
<pre>{% templatetag openvariable %} example3.as_html {% templatetag closevariable %}</pre>
{{ example3.as_html }}
<h3>via template tag</h3>
<pre>{% templatetag openblock %} load django_tables {% templatetag closeblock %}
{% templatetag openblock %} render_table example3 {% templatetag closeblock %}</pre>
{% load django_tables %}
{% render_table example3 %}
<h2>Example 4 — QuerySet + pagination + paleblue theme</h2>
<h3>via <tt>as_html()</tt></h3>
<pre>{% templatetag openvariable %} example4.as_html {% templatetag closevariable %}</pre>
{{ example4.as_html }}
<h3>via template tag</h3>
<pre>{% templatetag openblock %} load django_tables {% templatetag closeblock %}
{% templatetag openblock %} render_table example4 {% templatetag closeblock %}</pre>
{% load django_tables %}
{% render_table example4 %}
</body>
</html>

16
example/urls.py Normal file
View File

@ -0,0 +1,16 @@
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', 'example.app.views.home', name='home'),
# Uncomment the admin/doc line below to enable admin documentation:
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
)

View File

@ -4,7 +4,7 @@ from setuptools import setup, find_packages
setup(
name='django-tables',
version='0.4.0',
version='0.4.1',
description='Table framework for Django',
author='Bradley Ayers',