* {% 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
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)"
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
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,
line-height: 13px;
border-bottom: 1px solid #EEE;
border-left: 1px solid #DDD;
+ text-align: left;
}
table.paleblue thead th:first-child,
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 {
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
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)
# 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.
| 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
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
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
----
-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.
: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:
--- /dev/null
+from django.contrib import admin
+from .models import Country
+
+
+admin.site.register(Country)
--- /dev/null
+[
+ {
+ "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
+ }
+ }
+]
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+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'}
--- /dev/null
+"""
+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)
--- /dev/null
+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))
--- /dev/null
+#!/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)
--- /dev/null
+# 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,
+ },
+ }
+}
--- /dev/null
+<!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>
--- /dev/null
+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)),
+)
setup(
name='django-tables',
- version='0.4.0',
+ version='0.4.1',
description='Table framework for Django',
author='Bradley Ayers',