Giter VIP home page Giter VIP logo

django-tables2's Introduction

django-tables2 - An app for creating HTML tables

Latest PyPI version Any color you like

django-tables2 simplifies the task of turning sets of data into HTML tables. It has native support for pagination and sorting. It does for HTML tables what django.forms does for HTML forms. e.g.

Features:

  • Any iterable can be a data-source, but special support for Django QuerySets is included.
  • The builtin UI does not rely on JavaScript.
  • Support for automatic table generation based on a Django model.
  • Supports custom column functionality via subclassing.
  • Pagination.
  • Column based table sorting.
  • Template tag to enable trivial rendering to HTML.
  • Generic view mixin.

An example table rendered using django-tables2

An example table rendered using django-tables2 and bootstrap theme

An example table rendered using django-tables2 and semantic-ui theme

Example

Start by adding django_tables2 to your INSTALLED_APPS setting like this:

INSTALLED_APPS = (
    ...,
    "django_tables2",
)

Creating a table for a model Simple is as simple as:

import django_tables2 as tables

class SimpleTable(tables.Table):
    class Meta:
        model = Simple

This would then be used in a view:

class TableView(tables.SingleTableView):
    table_class = SimpleTable
    queryset = Simple.objects.all()
    template_name = "simple_list.html"

And finally in the template:

{% load django_tables2 %}
{% render_table table %}

This example shows one of the simplest cases, but django-tables2 can do a lot more! Check out the documentation for more details.

django-tables2's People

Contributors

ad-m avatar adammck avatar alasdairnicol avatar alex-sichkar avatar bertrandbordage avatar bradleyayers avatar dependabot[bot] avatar federicobond avatar foldedpaper avatar goinnn avatar graup avatar ionelmc avatar jieter avatar khirstinova avatar kviktor avatar luzfcb avatar matheusjardimb avatar mfogel avatar miracle2k avatar mpasternak avatar mschoettle avatar qris avatar selwin avatar slafs avatar spapas avatar thetarkus avatar toudi avatar vvs31415 avatar weber-s avatar zdanozdan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-tables2's Issues

DeclarativeColumnsMetaclass.__new__ should super-call

If you want a subclass of Table to have a metaclass such as ABCMeta, you need to subclass both DeclarativeColumnsMetaclass and the other class. Because DeclarativeColumnsMetaclass just calls type.new rather than super(DeclarativeColumnsMetaclass, cls).new, you have to be more careful about the order you subclass things that you should have to be.

Luckily the fix is trivial :-)

Table footer

It would be nice, if django-tables2 supports rendering an arbitrary number of rows within <tfoot></tfoot>.

Intuitive way to dynamically add columns

Due to all the meta magic going on, I am currently using:

class StatsTable(tables.Table):
   class Meta:
        orderable = False
        attrs = {'class': 'tablesorter'}

    def __init__(self, form, request, *args, **kwargs):
        rf_values = form.get_values()
        rf_annotations = form.get_annotations()
        rf_filter = form.get_filter()
        rf_calculations = form.get_calculations()                  
        data = list(
            HourlyStat.objects.filter(**rf_filter).values(*rf_values).annotate(**rf_annotations).order_by('-clicks')
        ) 
        super(StatsTable, self).__init__(data, *args, **kwargs)      
        columns = rf_values + rf_annotations.keys() + rf_calculations
        for col in columns:
            self.base_columns[col] = tables.Column()     

really the only issue with me using it like this is the fact that I can't simply call super() last it has to be in the middle. I guess its because the meta class doesn't actually look for base_columns on itself but only on inherited models. Really this just seems way messier than the self.fields on django forms.

LinkColumn doesn't handle null foreign keys well.

The column should render the default value, however instead a NoReverseMatch exception is raised with a message: Reverse for 'urlname' with arguments '(None,)' and keyword arguments '{}' not found.

DeprecationWarning: attrs must be Attrs object, not dict

Only the default, if nothing specified, is a dict.

Suggest the following change:

diff --git a/django_tables2/columns.py b/django_tables2/columns.py
index ed5c59d..69adc2e 100644
--- a/django_tables2/columns.py
+++ b/django_tables2/columns.py
@@ -86,7 +86,7 @@ class Column(object):
             if orderable is None:
                 orderable = sortable
         self.orderable = orderable
-        attrs = attrs or {}
+        attrs = attrs or Attrs()
         if not isinstance(attrs, Attrs):
             warnings.warn('attrs must be Attrs object, not %s'
                           % type(attrs).__name__, DeprecationWarning)

verbose_name with gettext

There's a problem if a verbose_name of a column in my model is wrapped around with an gettext function
( from django.utils.translation import ugettext_lazy as _ )

Then with a standard Table with Meta.model = ... we can produce an TemplateSyntaxError:

Caught TypeError while rendering: coercing to Unicode: need string or buffer, proxy found

the issue is in columns.py:430 (verbose_name property)

order_by is always called on a passed query set

This feels unintentional. But init sets order_by and even if its being set to None, this ends up calling self.data.order_by(()), which ends up calling .order_by() with no arguments on the queryset, which aiui will lose any ordering that has happened to the queryset already. As I said, this doesn't seem right -- have I misunderstood?

Adding attributes to each row in the table

Each row in my table is a concise representation of an object. When the row is clicked, a detailed page is loaded.
To do that I need the item's id in the db.

I am currently displaying this id as a table column, but would prefer to set it as an attribute for each row in the table body.

Another option might be to hide that column, but I think putting the data in a row attribute will be cleaner.

Can such a feature be added?

Make it easier to get started.

You should be able to just pass a QuerySet to a template, then write:

{% load render_table from django_tables2 %}
{% render_table queryset %}

This makes it trivial to get started, and avoids the need to modify generic class based views or write a Table class. Both QuerySet and django-filter's FilterSet should be supported.

HTML attributes for columns

django-tables supports setting arbitrary attributes of the table element. It would be nice, if one can do that with columns as well, so that one column can be right aligned, etc.

Error with sequence and exclude

Setting a sequence in a table's Meta class and then passing an exclude seems to cause an error when rendering with the render_table template tag. Specifically, I have the following:

class MemberReportTable(django_tables2.Table):
  class Meta:
    model = Profile
    sequence = ('id', 'user', '...')
table = MemberReportTable(Profile.objects.all(), exclude=('last_name',))

When rendering this table with {% render_table table %}, I get a "Caught KeyError while rendering: 'last_name'" from columns.py line 519: return ((x, self._columns[x]) for x in self.table.sequence)

Documentation should clarify paths used (for templates)

I found this plugin within days of learning Django and ran into the following problem:

I did the official Django tutorial the other day and then later installed this plugin via easy_setup. I am not sure if the tutorial's having users edit the template path is what caused this, but when I got to the point where the DT2 guide has you enter "{{ table.as_html }}" in your HTML file, the page returned a "template not found" error until I copied the relevant dirs/files from the DT2 egg into my project's template path.

I was only able to figure this out b/c Django's official docs list that templates whose path aren't specified are searched for in the Django contrib folder which in my case was extracted from the egg to a folder with the same name:

"...\Python27\Lib\site-packages\django-1.3.1-py2.7.egg"

I can see why this happens now, but perhaps a note should be made of this in the docs, as new users may not understand the method behind finding files.

Sorting doesn't work if TEMPLATE_STRING_IF_INVALID is set

The logic for setting the order_by field in render_table:

<a href="{% querystring table.prefixed_order_by_field=ob.opposite|default:column.name %}">{{ column.header }}</a>

Doesn't work if the django setting TEMPLATE_STRING_IF_INVALID is set. As the |default:column.name portion is ignored, since ob.opposite evaluates to the TEMPLATE_STRING_IF_INVALID text. So the query string will show ?sort=<TEMPLATE_STRING_IF_INVALID> always.

Using Django version 1.3.1

When using Meta.model + non-queryset data, column verbose_name not taken from model

When using the Meta.model property on a Table, there is a bit of unexpected behaviour when you instantiate a table using non-queryset data. The quirk is that the verbose_name falls back to a prettified field name, rather than the actual verbose_name property of the model field.

I feel that the expected resolution order for a column's verbose_name is:

  1. Explicit verbose_name when defining a column on the table -- foo = tables.Column(verbose_name="bah") -- (typically this won't be defined when using Meta.model)
  2. verbose_name of the model's column.

Allow separate accessor for sort field on columns (code attached)

In my code I use an accessor for a column that points to a function in my model (the function translates a ManyToMany field into a comma-separated string). But when trying to sort on said column, it will fail because the accessor does not resolve to an actual field on my model. So I added an extra kwarg to the Column initializer so you can specify an optional sort_accessor, which instead of pointing to my model function it points to one of the fields:

In columns.py - modified Column and BoundColumn to accept sort_accessor kwarg
http://dpaste.com/579958/

In tables.py - modified _translate_order_by function
http://dpaste.com/579959/

Sorry-- I would submit a pull request but I haven't forked you project (plus the version I'm using is a few months old), and I don't really have time to set all that up right now.

Broken kwargs iteration in LinkColumn

LinkColumn currently iterates through kwargs as if it were an iterable of tuples, but reverse iterates through kwargs as if it were a dict. This patch fixes LinkColumn:

--- a/django_tables2/columns.py
+++ b/django_tables2/columns.py
@@ -239,7 +239,7 @@ class LinkColumn(Column):
                               for a in self.args]
         if self.kwargs:
             params['kwargs'] = self.kwargs
-            for key, value in self.kwargs:
+            for key, value in self.kwargs.items():
                 if isinstance(value, A):
                     params['kwargs'][key] = value.resolve(record)
         if self.current_app:

Either apply by hand, or pull from my Git branch:

Support media requirements for columns

This would work in the same way that media for form widgets works. The first use-case that comes to mind is to allow the checkbox in the header of a CheckBoxColumn to actually perform a select none/all function.

Add generic view mixins for Django 1.3

I'm thinking a SingleTableMixin and MultipleTableMixin would be good. The following is a quick attempt at a SingleTableMixin, feedback would be appreciated. I think implementing the ability to change querystring variable names first (issue #5) would be a good idea, and necessary for MultipleTableMixin anyway.

class SingleTableMixin(object):
    """
    Adds a suitable table to the context. Requires a ``table`` attribute on the
    view.
    """
    table = None
    context_table_name = "table"

    def get_context_data(self, **kwargs):
        table = self.table(self.get_queryset(), order_by=self.request.GET.get('sort'))
        table.paginate(page=self.request.GET.get('page', 1))
        context = super(TableMixin, self).get_context_data(**kwargs)
        context[self.context_table_name] = table
        return context

Sorting can cause TypeError if sorting a date and None

More generally, you also cannot sort any column that contains data not comparable using Python's built-in cmp function. This does not effect model based tables because those are sorted at the database level.

Old Python types are comparable with each other as well as with None, but starting in Python3, this will not be the case. Newer Python types (including dates) are not comparable to different types or with None. There was a similar issue against the Python source.

>>> import django_tables2 as tables
>>> class UserTable(tables.Table):
...     name = tables.Column()
...     dt = tables.Column()
... 
>>> from datetime import datetime
>>> users = [{'name': 'David', 'dt': datetime.now()}, {'name': 'Joe', 'dt': None}]
>>> table = UserTable(users, order_by='dt')
Traceback (most recent call last):
...
TypeError: can't compare datetime.datetime to NoneType

Rendering 'None', sucks.

I think also having Table.Meta.default would be good, but just rendering an empty string would be more useful instead of None.

Debian squeeze python/django version dependancies

Unfortunately Debian stable only has Python 2.6. Fortunately, the solution is a one line change:

commit 46f7128107b29ed6521aa78a4e70a189a80e05dd
Author: Brian May <[email protected]>
Date:   Sat Mar 10 18:05:06 2012 +1100

    Use old style syntax for exception, making it compatable with Python 2.6

diff --git a/django_tables2/columns.py b/django_tables2/columns.py
index 31ec9eb..186bb41 100644
--- a/django_tables2/columns.py
+++ b/django_tables2/columns.py
@@ -276,7 +276,7 @@ class LinkColumn(Column):
         # and use that to decide whether to render a link or just the default
         try:
             raw = bound_column.accessor.resolve(record)
-        except (TypeError, AttributeError, KeyError, ValueError) as e:
+        except (TypeError, AttributeError, KeyError, ValueError), e:
             raw = None
         if raw is None:
             return self.default

Unfortunately this might break Python 3.x compatibility if that is desired.

django-tables2, unfortunately requires django >= 1.3, at least there is a backport available for Debian stable. This is required for django.test.client.RequestFactory

I see that django.test.client.RequestFactory is only required for one method; a method I don't use - would it be possible to make this import optional?

Populate table_data with mixedin ListView's object_list

I'm in the process of moving over to class-based views, in particular the part of our app that used django-tables. Django-tables2 seems a whole lot better, so I picked up v(0.2.0). I'm running django 1.4.0rc1. Once I applied koledennix's handy pull request it looked like the example you give in the docs for class-based view was working, but for the lack of data in the table. The example says this should happen automatically.

Noticing a comment from a previous pull request:

The difference between SingleTableView and ListView
is that the table view adds a table to the list view.
It doesn't force you to populate the table
with the same data that the list displays.

I tried using SingleTableView instead of a SingleTableMixin with Listview in case was understanding the above comment incorrectly. When I checked my context, object_list was full of data, and table_data empty, just as before. Perhaps this is my unfamiliarity with class-based views, but since object_list is only accessible from the template after .as_view creates it, there's no way to pass it in to the MyTableView.as_view() function through table_data = object_list.

Even if django-tables2 doesn't "force you to populate the table with the same data the list displays", shouldn't you be able to ask it to populate?

It's also possible this feature isn't missing, but I'm implementing it wrong. Perhaps the example is incorrect? Or is there another bug in 0.2.0 and django1.4 with class-based views I'm not finding? Either way, there's not much in the community about this kind of error, and your class-based documentation is pretty sparse. In which case, I'd re-title this issue to 'Provide Class-Based Documentation', something I'd be happy to contribute to if I could get it working myself.

Null ForeignKey results in [ Caught DoesNotExist while rendering ]

Hello again,

I've found a bug, when a model contains ForeignKey, that can be Null.

When the key is null actually, an error occurs in template /usr/local/lib/python2.6/dist-packages/django_tables-0.4.3-py2.6.egg/django_tables/templates/django_tables/table.html, error at line 21
[ Caught DoesNotExist while rendering ]

http://paste2.org/p/1426496
http://paste2.org/p/1426494

my 'hot' workaround in the table model: http://paste2.org/p/1426469 (the FK in the model is called 'kunde_nr')

At the same time, that would specify the fields, that are enabled. The same like django ModelForm do.

Table.page handle incorrect page number

Hi,
Are there any thoughts yet on changing the handling of invalid page numbers to provide a default behaviour (empty table/first page/HttpRedirect/..)?

In the django documentation (https://docs.djangoproject.com/en/dev/topics/pagination/?from=olddocs#using-paginator-in-a-view) is a reference implementatoin which handels the EmptyPage and PageNotAnInteger exceptions accordingly.

In my current straight forward use case (example code below) I'd have to catch the Http404 Exception raised by the Paginator during the render() call, call table.page() and handle the 2 exceptions accordingly which doesn't feel very good. I could also implement an own Paginator (derived from django.core.Paginator) which does the handling but still feels like a bit too much "work".

It would also be alot more uglier with multiple tables on the same page..

My proposal would be some sort of Meta options flag on how to handle invalid page numbers.


 table = Table(queryset)
 table.RequestConfig(request).configure(table)
 return render(request,'xyz.html',{'table':table}, context_instance=request_context)

``render_table`` using custom template does not work:w

Using something like this in my template produces errors:

{% load django_tables2 %}
{% render_table table "table.html" %}

Line 177 of django_tables2/templatetags/django_tables2.py throws a NameError because you don't import select_template.

Adding the import produces a TypeError:

Caught TypeError while rendering: 'FilterExpression' object is not iterable

...which is thrown by select_template.

It shouldn't get to that point, because I'm passing it a string. But render_table is being sent a FilterExpression, which needs to be stringified using FilterExpression.var (I guess?)

verbose_name from the model

verbose_name should be copied from the model.

the current version forces me to use: k_str = tables.Column('Strasse') instead of
k_str = tables.Column()

This breaks the DRY

Use the accessor for sort_by if none is provided

Following on from #20, I'm using a table built out of a nested data structure:

class TopTalkers(tables.Table):
    protocol = tables.Column()
    source_address = tables.Column(accessor='src.textaddr')
    source_port = tables.Column(accessor='src.port')
    dest_address = tables.Column(accessor='dst.textaddr')
    dest_port = tables.Column(accessor='dst.port')
    bytes = tables.Column()

If I try to sort the table by one of the columns with an accessor, it fails, because it tries to resolve using the column name and not the accessor.

I know I could work around this by specifying order_by as well as accessor, with the same value. But that feels ugly and unnecessary.

If an accessor is provided but no order_by, could you default order_by to the value of the accessor?

Cheers, Chris.

LinkColumn args not related to row object

I've got a link column that refers to a form. When the user does a 'save' on the form I'd like to redirect back to the page with the table they started from but I'm can't come up with a way of adding a args/kwargs to the LinkColumn which I can then use with HttpResponseRedirect. Who knows maybe I'm thinking about this in the wrong way. Anyway help would be appreciated.

Ted

Support fields with 'choice' arguments

When generating a table for model with a field setting a 'choice' argument like:

INSTITUTION_STATUSES = (
    ('a', 'Academic'),
    ('c', 'Commercial'),
)

class Company(models.Model):
    name = models.CharField(max_length=150)
    status = models.CharField(max_length=20, choices=INSTITUTION_STATUSES)

I'd expect the default rendering to expand 'a' or 'c' to the defined values.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.