Skip to main content
 首页 » 编程设计

python之Django-tables2 KeyError when field has {curly brackets}

2024年10月01日31dflying

我有一个带有 Artist 和 Track 类的模型,当 Track 的标题字段有大括号时,我得到一个 KeyError 异常。 (例如轨道标题“Beat It {Freestyle}”)。看起来 django_tables2 正在将我的轨道标题字段值传递给 string.format()。我如何在 django_tables2 中防止这种情况发生?

模型.py

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) 

表格.py

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') 

View .py

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) 

profile_detail.html

<form method='POST' id="add-track-form" action='{% url "playlists:playlist_addtrack" %}'>{% csrf_token %} 
... 
        {% render_table table "django_tables2/bootstrap3.html" %} 
... 
</form> 

bootstrap3.html

% 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 %} 

回溯:

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' 

请您参考如下方法:

考虑到调用 str.format 时字符串中的大括号不能静默传递,除非您控制格式化的完成方式(此处不能),您可以将大括号替换为另一个字符,如双括号 ((..)) 或双括号以期待 str.format;然而,后者更脆弱,因为字段值取决于以后对 str.format 的调用:

>>> '{{Freestyle}}'.format() 
'{Freestyle}'