gmazoyer
gmazoyer

Reputation: 43

django-tables2 permissions in TemplateColumn

I feel like I have read about this a hundred times but I still can't figure out how to use permissions within a django-tables2 TemplateColumn.

My goal is to be able to render buttons in a column based on permissions that a user may have or may not have on a given model. That does not sound complicated to me and from what I have read I should be able to use something like {% if perms.myapp.delete_mymodel %} to achieve what I'd like to do.

Here is the code I'm trying to get to work as I expect:

import django_tables2 as tables


MY_MODEL_ACTIONS = """
{% if perms.myapp.change_mymodel %}
<a href="{% url 'myapp:my_model_edit' pk=record.pk %}" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>
{% endif %}
{% if perms.myapp.delete_mymodel %}
<a href="{% url 'myapp:my_model_delete' pk=record.pk %}" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i></a>
{% endif %}
"""


class MyModelTable(tables.Table):
    # some columns
    actions = tables.TemplateColumn(
        verbose_name="",
        template_code=MY_MODEL_ACTIONS,
    )

    class Meta(BaseTable.Meta):
        model = MyModel
        fields = (
            # some columns
            "actions",
        )

When rendering the table no issues are triggered but the column just do not display any buttons (yes I do have the permissions for them to show up). Removing the {% if … %} clauses, thus removing the permission checks, allows the buttons to be seen of course.

Upvotes: 0

Views: 610

Answers (3)

My solution is:

class MyModelTable(ColumnShiftTable):
    delete = tables.LinkColumn("myapp:my_model_delete", text="delete", args=[
                                 A("pk")], orderable=False)


    class Meta:
        model = MyModel

        exclude = ("id",)
        attrs = {
            "class": "table table-responsive-sm table-bordered table-striped table-sm"
        }

    def before_render(self, request):
        if request.user.has_perm('perms.myapp.delete_mymodel'):
            self.columns.show('delete')
        else:
            self.columns.hide('delete')
            # hide other columns
            self.columns.hide('foo')
            self.columns.hide('bar')

Upvotes: 0

Jieter
Jieter

Reputation: 4229

What adds perms to your context?TemplateColumns do not have the same context as the template {{ render_table table }} is called from, so you must be a little more explicit.

The documentation for render_table mentions it will attach the context of the calling template to table.context, so this should fix your problem:

MY_MODEL_ACTIONS = """
{% if table.context.perms.myapp.change_mymodel %}
    <a href="{% url 'myapp:my_model_edit' pk=record.pk %}" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>
{% endif %}
{% if table.context.perms.myapp.delete_mymodel %}
    <a href="{% url 'myapp:my_model_delete' pk=record.pk %}" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i></a>
{% endif %}
"""

Upvotes: 0

gmazoyer
gmazoyer

Reputation: 43

The issue was a bit tricky. I defined my own template to render the table and did not used the {% render_table table %} tag inside it. Due to this the context was not reachable from the TemplateColumn code.

To fix this, I changed my template a bit and move my table rendering custom code to another template file. After that I used the render_table tag like this {% render_table table 'includes/table.html' %}

After this, the code that I mentioned above in the column works just fine, permissions are honored like expected.

Upvotes: 0

Related Questions