4

I want to add a link to my listview using linkify in the Columns of the API Reference. I am using Django 2 with Django_tables2 v 2.0.0b3

I have a URL with two context variables name, which is passed from the ListView and the slug field species:

URL.py

app_name = 'main'

urlpatterns = [
#The list view
path('genus/<slug:name>/species/', views.SpeciesListView.as_view(), name='species_list'),
# The Detail view
path('genus/<name>/species/<slug:species>', views.SpeciesDetailView.as_view(), name='species'),
]

The DetailView is currently accessible if I manually type the URL.

I want to use the option where I can enter a tuple with (viewname, args/kwargs).

For the tables.py I tried:

class SpeciesTable(tables.Table):
    species =tables.Column(linkify=('main:species', {'name': name,'slug':species}))

This gave a NameError: name 'species' is not defined.

species =tables.Column(linkify=('main:species', {'name': kwargs['name'],'slug':kwargs['species']}))

This gave a NameError: name 'kwargs' is not defined.

I also tried changing the following variables to strings:

species =tables.Column(linkify=('main:species', {'name': 'name','slug':'species'}))
species =tables.Column(linkify=('main:species', {'name': 'name','slug':'object.species'}))

These attempts gave a NoReverseMatch Reverse for 'species' with keyword arguments '{'name': 'name', 'slug': 'species'}' not found. 1 pattern(s) tried: ['genus\\/(?P<name>[^/]+)\\/species\\/(?P<species>[-a-zA-Z0-9_]+)$']

Formatting it as any of the following will give a SyntaxError:

species =tables.Column(kwargs={'main:species','name': name,'slug':species})
species =tables.Column(args={'main:species','name': name,'slug':species})
species =tables.Column(kwargs:{'main:species','name': name,'slug':species})
species =tables.Column(args:{'main:species','name': name,'slug':species})

How would I add a link similar to {% url "main:species" name=name species =object.species %}? Currently there are no example in the docs to do this.

ccsv
  • 8,188
  • 12
  • 53
  • 97

2 Answers2

7

Try to think from the perspective of a row. In each row, the table needs the species of that row. The mechanism used in django-tables2 for that is an accessor. It enables you to tell django-tables2 the value you want it to use for a certain value. You cannot use variables (like name and species for that, because you want them to be retrieved from each record.

So using the accessor (usually abbreviated with A), your first example looks like this:

class SpeciesTable(tables.Table):
    species = tables.Column(linkify=('main:species', {'name': tables.A('name'),'slug': tables.A('species')}))

The concept of Accessors is usable in multiple places, also to change the value you want to render in a column.

I'd suggest defining get_absolute_url methods on your models though. This is nice because usually when you want to show a link to the model, you have an instance of it, so in templates it's a matter of {{ species.get_absolute_url }}, for the linkify argument to django-tables2 columns, you mostly can get away with linkify=True.

You are right about the docs on linkify, they certainly need improvement.

dyve
  • 5,893
  • 2
  • 30
  • 44
Jieter
  • 4,101
  • 1
  • 19
  • 31
  • 3
    I added some examples to the docs on `linkify`: https://github.com/jieter/django-tables2/commit/204a7f23860d178afc8f3aef50512e6bf96f8f6b – Jieter Sep 03 '18 at 08:36
  • This did not work `'name': tables.A('name')` because the `name` variable was being passed as a context variable from the previous page and not included in the table. – ccsv Sep 04 '18 at 01:03
  • the variable 'name' is passed by a `get_context_data` function `context["name"] = self.kwargs['name']`. The slug field is loaded from the queryset which I think you got – ccsv Sep 04 '18 at 06:34
  • 1
    I think this is a use case not supported by the current implementation of the column. You can probably get this to work quickly using a [`TemplateColumn`](https://django-tables2.readthedocs.io/en/latest/pages/api-reference.html#templatecolumn) – Jieter Sep 04 '18 at 06:59
  • Ok thanks I was wondering what I was doing wrong. It would be cool to have a dictionary of context variables passed for the URL the current format with the accessor was not really too obvious to me – ccsv Sep 04 '18 at 07:05
  • 1
    ok that worked here is the solution `species =tables.TemplateColumn('{{value}}')` – ccsv Sep 04 '18 at 11:49
1

Alternative option for the linkify parameter

After many tries I never got the linkify parameter option working the way I wanted. However, I did find a way to achieve the same result.

You can add a render_foo function. For instance, if you want to render an ID with a link one can add the render_id function to their table class. This function will then overwrite the existing rendering for the attribute ID.

More information about this can be found on: https://django-tables2.readthedocs.io/en/latest/pages/custom-data.html

models.py

# models.py

import django_tables2 as tables
from django.urls import reverse


class Person:
   name = model.CharField(default='Jeffrey Lebowski', max_length=256)
   nick_name = model.CharField(default='the Dude', max_length=256)
   hates_hearing = model.CharField(default="Where's the money Lebowski?", max_length=256)


class PersonTable(tables.Table):
    class Meta:
        model = Person
        template_name = "django_tables2/bootstrap.html"
        fields("id", "name", "nick_name", "hates_hearing", )
   
    def render_id(self, record):
        """
        This function will render over the default id column. 
        By adding <a href> HTML formatting around the id number a link will be added, 
        thus acting the same as linkify. The record stands for the entire record
        for the row from the table data.
        """
        return format_html('<a href="{}">{}</a>',
                           reverse('example-view-name',
                           kwargs={'id':, record.id}),
                           record.id)
   

For this to work one would also need to specify the view, url, and template.

views.py

# views.py

from django_tables2 import SingleTableView

class ExampleTableView(SingleTableView):
    model = Person
    table_class = PersonTable
    template_name = "app_name/example.html"
    object_list = Person.objects.all()

urls.py

# urls.py

from django.urls import path
from . import views


urlpatterns = [
    path('<int:id>', views.detail, name="example-view-name"),
]

example.html

# templates/app_name/example.html

{% load static %}

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>EXAMPLE HTML</title>
</head>
<body>
   <section>
       <div>
           <h1>EXAMPLE TABLE</h1>
           <p>Here one can find the result of the django-tables2 output</p>
           {% render_table table %}
       </div>
   </section>
</body>
<footer>
    <p>That's it.</p>
</footer>
</html>

Please note, one has to add django-tables2 to the installed apps list. Otherwise the template does not work.

Maybe this a good alternative. If someone has any comments or improvements, let me know.