Question

I'm trying to create a generic table view, that take a model and a tuple of columns names, and show the given columns of the model. My problem is with inner Meta class.

Assume this two models:

# models.py

from django.db import models


class Model_a(models.Model):
    column_a_1 = models.Field() 
    column_a_2 = models.Field()
    column_a_3 = models.Field()
    column_a_4 = models.Field()
    column_a_5 = models.Field()


class Model_b(models.Model):
    column_b_1 = models.Field() 
    column_b_2 = models.Field()
    column_b_3 = models.Field()
    column_b_4 = models.Field()
    column_b_5 = models.Field()

The objective is to have a single view that generates the right table with some of the columns in a given order. So, I've a urls.py like this:

# urls.py

from django.conf.urls import patterns, url
import myapp.views as myviews
import myapp.models as mymodels


urlpatterns = patterns( '',
                       url( r'^table_a/$',
                            myviews.show_table,
                            { 'model': mymodels.Model_a,
                              'columns': ("column_a_1", "column_a_2", "column_a_3") } ),
                       url( r'^table_b/$',
                            myviews.show_table,
                            { 'model': mymodels.Model_b,
                              'columns': ("column_b_5", "column_b_2") } ) )

And a simple views.py like this:

# views.py

from django.shortcuts import render
from django_tables2 import RequestConfig
import myapp.tables as mytables


def show_table(request, model, columns):
    entries = model.objects.all()
    table = mytables.ModelTable(entries, model, columns)
    RequestConfig(request).configure(table)
    return render(request, 'table_view.html', {'dynamic-table': table})

The key is in the Table subclassing, inside tables.py:

# tables.py

from django_tables2 import import Table


class ModelTable(Table):

    class Meta:
        pass

    def __init__(self, data, model, columns, order_by=None, orderable=None, empty_text=None,
                 exclude=None, attrs=None, sequence=None, prefix=None,
                 order_by_field=None, page_field=None, per_page_field=None,
                 template=None, sortable=None, default=None):
        self.Meta.model = model
        self.Meta.fields = columns
        self.Meta.sequence = columns
        super(ModelTable, self).__init__(data, order_by, orderable, empty_text,
                 exclude, attrs, sequence, prefix,
                 order_by_field, page_field, per_page_field,
                 template, sortable, default)

Clearly, I didn't understand how to set attributes, of Table.Meta, from the Table subclass initializer. The courious thing is that when I use {% render_table dynamic-table %} in my template, I get en empty table (so not visible), but a well working paginator.

Where is my mistake?

Was it helpful?

Solution

Solved. The problem was about Meta class: it "lives" before ModelTable initializer, so setting Meta attributes in said initializer was pointless.

The revelation cames reading this QA.

I've to define a new method that dinamically define the specialized class; here is my new tables.py :

# tables.py

from django_tables2 import import Table


class ModelTable(Table):
    class Meta:
        pass


def table_maker(model, columns):
    meta_class = type('Meta', (ModelTable.Meta,), {'model':model, 'fields':columns})
    table_class = type(model._meta.object_name + 'Table', (ModelTable,), {'Meta':meta_class})
    return table_class

And consequently also views.py changes a little bit:

# views.py

from django.shortcuts import render
from django_tables2 import RequestConfig
import myapp.tables as mytables


def show_table(request, model, columns):
    entries = model.objects.all()
    table = mytables.table_maker(model, columns)(entries)
    RequestConfig(request).configure(table)
    return render(request, 'table_view.html', {'dynamic-table': table})

Now it works as expected!

OTHER TIPS

There is an issue with submitted answer.

The code actually would not work because table_maker function is required to be like this:

def table_maker(model, columns):
    meta_class = type('Meta', (), {'model':model, 'fields':columns})
    table_class = type(model._meta.object_name + 'Table', (ModelTable,), {'Meta':meta_class})
    return table_class

See, the difference is that meta class is not supposed to take any object. Remove the object and it works :)

Here is a reference:

Equivalent of this:

class X(object):
   a = 1

Would be this one, (using type class maker):

X = type('X', (object,), dict(a=1))

and instead of object that we have Meta: We would then need to put ()

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top