I have a model with Artist and Track class and when the title field of a Track has curly brackets, I get a KeyError Exception. (E.g. Track Title "Beat It {Freestyle}"). Looks like django_tables2 is passing my Track title field value to string.format(). How do I prevent that in django_tables2?
class Artist (models.Model):
name = models.CharField(max_length=100)
slug = AutoSlugField(populate_from='name', unique=True, editable=True, slugify=custom_slugify)
class Track (models.Model):
artist = models.ForeignKey(Artist, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="Artist")
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="Submitted by", default=1)
title = models.CharField(max_length=100, verbose_name="Title")
timestamp = models.DateTimeField(default=timezone.now)
class TrackTable(tables.Table):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
self.artistlink = kwargs.pop('artistlink', None)
super(TrackTable, self).__init__(*args, **kwargs)
def render_artist(self, value):
if self.artistlink == True:
return format_html('<a href="/music/by/{}">{}</a>', value.slug, value)
else:
return value
def render_user(self, value):
return format_html('<a href="/users/{}">{}</a>', value, value)
download = DownloadColumn(empty_values=(), orderable=False)
track_id = tables.CheckBoxColumn(accessor="pk",
attrs = {"th__input": {"id": "checkAll"}},
orderable=False)
class Meta:
model = Track
attrs = {"class": "table table-bordered table-condensed table-striped", "id": "track-table",}
fields = ('track_id', 'artist', 'title', 'user', 'download')
def profile_detail(request, slug=None):
alpha_list = ['0-9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
playlist_list = Playlist.objects.filter(user=request.user.id)
track_list = Track.objects.filter(artist__slug=slug)
ltr = slug[0].upper()
artist = get_object_or_404(Artist, slug=slug)
table = TrackTable(track_list, user=request.user, artistlink=False)
RequestConfig(request, paginate={"per_page": 15}).configure(table)
context = {
"playlist_list": playlist_list,
"track_list": track_list,
"table": table,
"alpha_list": alpha_list,
"artist": artist,
}
return render(request, "profile_detail.html", context)
<form method='POST' id="add-track-form" action='{% url "playlists:playlist_addtrack" %}'>{% csrf_token %}
...
{% render_table table "django_tables2/bootstrap3.html" %}
...
</form>
% load querystring from django_tables2 %}
{% load trans blocktrans from i18n %}
{% load bootstrap3 %}
{% if table.page %}
<div class="table-responsive">
{% endif %}
{% block table %}
<table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %}>
{% block table.thead %}
<thead>
<tr>
{% for column in table.columns %}
{% if column.orderable %}
<th {{ column.attrs.th.as_html }}><a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a></th>
{% else %}
<th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
{% endif %}
{% endfor %}
</tr>
</thead>
{% endblock table.thead %}
{% block table.tbody %}
<tbody>
{% for row in table.page.object_list|default:table.rows %} {# support pagination #}
{% block table.tbody.row %}
<tr class="{% cycle "odd" "even" %}">
{% for column, cell in row.items %}
<td {{ column.attrs.td.as_html }}>{{ cell }}</td>
{% endfor %}
</tr>
{% endblock table.tbody.row %}
{% empty %}
{% if table.empty_text %}
{% block table.tbody.empty_text %}
<tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
{% endblock table.tbody.empty_text %}
{% endif %}
{% endfor %}
</tbody>
{% endblock table.tbody %}
{% block table.tfoot %}
<tfoot></tfoot>
{% endblock table.tfoot %}
</table>
</div> <!-- table-responsive -->
{% endblock table %}
traceback:
Template error:
In template /Users/will/bpmdata/bpmdb/templates/django_tables2/bootstrap3.html, error at line 29
Freestyle 19 : {% endif %}
20 : {% endfor %}
21 : </tr>
22 : </thead>
23 : {% endblock table.thead %}
24 : {% block table.tbody %}
25 : <tbody>
26 : {% for row in table.page.object_list|default:table.rows %} {# support pagination #}
27 : {% block table.tbody.row %}
28 : <tr class="{% cycle "odd" "even" %}">
29 : {% for column, cell in row.items %}
30 : <td {{ column.attrs.td.as_html }}>{{ cell }}</td>
31 : {% endfor %}
32 : </tr>
33 : {% endblock table.tbody.row %}
34 : {% empty %}
35 : {% if table.empty_text %}
36 : {% block table.tbody.empty_text %}
37 : <tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
38 : {% endblock table.tbody.empty_text %}
39 : {% endif %}
Traceback:
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
41. response = get_response(request)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/core/handlers/base.py" in _legacy_get_response
249. response = self._get_response(request)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/will/bpmdata/bpmdb/profiles/views.py" in profile_detail
136. return render(request, "profile_detail.html", context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/shortcuts.py" in render
30. content = loader.render_to_string(template_name, context, request, using=using)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/loader.py" in render_to_string
68. return template.render(context, request)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/backends/django.py" in render
66. return self.template.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
207. return self._render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in _render
199. return self.nodelist.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
990. bit = node.render_annotated(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/loader_tags.py" in render
177. return compiled_parent._render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in _render
199. return self.nodelist.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
990. bit = node.render_annotated(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/loader_tags.py" in render
72. result = block.nodelist.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
990. bit = node.render_annotated(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/defaulttags.py" in render
322. return nodelist.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
990. bit = node.render_annotated(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django_tables2/templatetags/django_tables2.py" in render
154. return template.render(context.flatten())
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/backends/django.py" in render
66. return self.template.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
207. return self._render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in _render
199. return self.nodelist.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
990. bit = node.render_annotated(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/loader_tags.py" in render
63. result = self.nodelist.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
990. bit = node.render_annotated(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/loader_tags.py" in render
63. result = self.nodelist.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
990. bit = node.render_annotated(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/defaulttags.py" in render
216. nodelist.append(node.render_annotated(context))
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/loader_tags.py" in render
63. result = self.nodelist.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render
990. bit = node.render_annotated(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/base.py" in render_annotated
957. return self.render(context)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/template/defaulttags.py" in render
172. values = list(values)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django_tables2/rows.py" in items
229. yield (column, self.get_cell(column.name))
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django_tables2/rows.py" in get_cell
180. default=self.table.columns[name].default
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django_tables2/rows.py" in _get_and_render_with
155. return render_func(bound_column, value)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django_tables2/rows.py" in _call_render
189. self._optional_cell_arguments(bound_column, value)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django_tables2/utils.py" in call_with_appropriate
511. return fn(**kwargs)
File "/Users/will/bpmdata/bpmdb/profiles/tables.py" in render
42. return format_html(referral_url, artist=artist1, title=record.title)
File "/Users/will/.virtualenvs/bpmdb/lib/python3.5/site-packages/django/utils/html.py" in format_html
100. return mark_safe(format_string.format(*args_safe, **kwargs_safe))
Exception Type: KeyError at /music/by/michael-jackson/
Exception Value: 'Freestyle'
Considering that curly braces in strings cannot pass silently when str.format
is called, except you control how the formatting is done (which you can't here), you can either replace curly braces with another character like a double parens ((..))
or double up the braces in anticipation of str.format
; the latter is however more fragile since the field value is dependent on a later call to str.format
:
>>> '{{Freestyle}}'.format()
'{Freestyle}'