django-tables2 add dynamic columns to table class from hstore
<p>My general question is: can I use the data stored in a <strong><a href="https://docs.djangoproject.com/en/1.8/ref/contrib/postgres/fields/#django.contrib.postgres.fields.HStoreField" rel="nofollow">HStoreField</a></strong> (Django 1.8.9) to generate columns dynamically for an existing <strong>Table</strong> class of <strong><a href="https://django-tables2.readthedocs.io/en/latest/index.html" rel="nofollow">django-tables2</a></strong>? As an example below, say I have a model:</p>

<pre><code>from django.contrib.postgres import fields as pgfields

GameSession(models.Model):
    user = models.ForeignKey('profile.GamerProfile')
    game = models.ForeignKey('games.Game')
    last_achievement = models.ForeignKey('games.Achievement')
    extra_info = pgfields.HStoreField(null=True, blank=True)
</code></pre>

<p>Now, say I have a table defined as:</p>

<pre><code>GameSessionTable(tables.Table):
    class Meta(BaseMetaTable):
        model = GameSession
        fields = []
        orderable=False

    id = tables.LinkColumn(accessor='id', verbose_name='Id', viewname='reporting:session_stats', args=[A('id')], attrs={'a':{'target':'_blank'}})
    started = DateTimeColumn(accessor='startdata.when_started', verbose_name='Started')
    stopped = DateTimeColumn(accessor='stopdata.when_stopped', verbose_name='Stopped')
    game_name = tables.LinkColumn(accessor='game.name', verbose_name='Game name', viewname='reporting:game_stats', args=[A('mainjob.id')], attrs={'a':{'target':'_blank'}})
</code></pre>

<p>I want to be able to add columns for each of the keys stored in the <em>extra_info</em> column for all of the <code>GameSession</code>s. I have tried to override the <strong>init</strong>() method of the GameSessionTable class, where I have access to the queryset, then make a set of all the keys of my <code>GameSession</code> objects, then add them to <code>self</code>, however that doesn't seem to work. Code below:</p>

<pre><code>def __init__(self, data, *args, **kwargs):
    super(GameSessionTable, self).__init__(data, *args, **kwargs)

    if data:
        extra_cols=[]
        # just to be sure, check that the model has the extra_info HStore field
        if data.model._meta.get_field('extra_info'):
            extra_cols = list(set([item for q in data if q.extra_info for item in q.extra_info.keys()]))
        for col in extra_cols:
            self.columns.columns[col] = tables.Column(accessor='extra_info.%s' %col, verbose_name=col.replace("_", " ").title())
</code></pre>

<p>Just a mention, I have had a look at <a href="https://spapas.github.io/2015/10/05/django-dynamic-tables-similar-models/#introduction" rel="nofollow">https://spapas.github.io/2015/10/05/django-dynamic-tables-similar-models/#introduction</a> but it's not been much help because the use case there is related to the fields of a model, whereas my situation is slightly different as you can see above.</p>

<p>Just wanted to check, is this even possible or do I have to define an entirely different table for this data, or potentially use an entirely different library altogether like <a href="https://django-report-builder.readthedocs.io/en/latest/" rel="nofollow">django-reports-builder</a>?</p>

<p>Managed to figure this out to a certain extent. The code I was running above was <em>slightly</em> wrong, so I updated it to run my code before the superclass <em>init()</em> gets run, and changed where I was adding the columns.</p>

<p>As a result, my <strong>init()</strong> function now looks like this:</p>

<pre><code>def __init__(self, data, *args, **kwargs):
    if data:
        extra_cols=[]
        # just to be sure, check that the model has the extra_info HStore field
        if data.model._meta.get_field('extra_info'):
            extra_cols = list(set([item for q in data if q.extra_info for item in q.extra_info.keys()]))
        for col in extra_cols:
            self.base_columns[col] = tables.Column(accessor='extra_info.%s' %col, verbose_name=col.replace("_", " ").title())

    super(GameSessionTable, self).__init__(data, *args, **kwargs)
</code></pre>

<p>Note that I replaced <em>self.columns.columns</em> (which were BoundColumn instances) with <strong>self.base_columns</strong>. This allows the superclass to then consider these as well when initializing the <code>Table</code> class.</p>

<p>Might not be the most elegant solution, but it seems to do the trick for me.</p>

python django hstore django-tables2