在 Django 模板中,如何指定本身就是属性的字典键?

Posted

技术标签:

【中文标题】在 Django 模板中,如何指定本身就是属性的字典键?【英文标题】:In a Django template, how to specify a dictionary key which is itself an attribute? 【发布时间】:2018-10-14 03:35:09 【问题描述】:

我正在处理一个带有ListView 的Django 项目,该项目既有搜索表单(在视图上下文中称为search_form),也有一个过滤表单filter_form。界面如下所示:

search_formfilter_form 最终都会改变ListViewget_queryset 方法返回的内容。我想让它在您第一次应用过滤器然后进行搜索时,它会搜索过滤后的结果。

目前,“反向”功能已经实现:当你先搜索再过滤时,它会过滤搜索结果。这是因为filter_form 中隐藏了input 元素:

<form action=% url 'dashboard:families' % method="GET" data-behavior="filters">
                <input type="hidden" name="q" value=" request.GET.q.strip "/>

                <div class="input-field col s2">
                   filter_form.guide 
                  <label class="active">Guide</label>
                  % if filter_form.is_guide_filled %
                    <a href="" class="clear"><i class="material-icons tiny">clear</i></a>
                  % endif %
                </div>

为了比较,搜索栏有以下模板,_search.html

<form action="% url action %" method="get" class="left search col s6 hide-on-small-and-down" novalidate>
  <div class="input-field">
    <input id="search" placeholder=" placeholder "
        autocomplete="off" type="search" name="q"
        value=" search_form.q.value.strip|default:'' "
        data-query=" search_form.q.value.strip|default:'' ">
    <label for="search" class="active"><i class="material-icons search-icon">search</i></label>
    <i data-behavior="search-clear"
        class="material-icons search-icon"
        % if not search_form.q.value %style="display: none;"% endif %>close</i>
  </div>
</form>

在主列表视图index.html 中,搜索模板如下所示:

% block search_form %
  % with action='dashboard:families' placeholder='Search Families' %
    % include '_search.html' %
  % endwith %
% endblock %

为了使搜索表单保留过滤器,我注意到仅对于 guide 字段,以下工作:

<input type="hidden" name="guide" value=" request.GET.guide "/>

我想将其概括为包括所有过滤器。我尝试了以下方法:

  % if filter_form %
    % for field in filter_form %
      % with field_name=field.name %
        <input type="hidden" name=field_name value=" request.GET.field_name"/>
      % endwith %
    % endfor %
  % endif %

但是,如果我尝试这样做,我会在查询字符串中得到“field_name”:

我从DTL docs 了解到,点表示法实现了字典查找、属性查找和列表索引查找。如果我要尝试类似的东西

request.GET.field.name

它可能会尝试在request.GET 类字典对象中查找“字段”,但找不到任何东西。在常规 Python 中,我基本上想做的是

request.GET[field.name]

我认为我可以使用 with 块来完成这项工作,但显然这不起作用。关于如何实现这一点的任何建议?

更新

如果我将input 元素的name 属性指定为"field_name" 而不仅仅是field_name,就像这样,

<input type="hidden" name="field_name" value=" request.GET.field_name"/>

问题是value 被设置为空字符串而不是所需的值,这导致guide 字段的ValueError (这是一个ModelChoiceField 需要一个整数作为输入) :

为什么在这种情况下属性查找不起作用?

更新 2

回应 Lemayzeur 的评论,完整的追溯是:

Traceback:

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/two_factor/views/mixins.py" in dispatch
  82.         return super(OTPRequiredMixin, self).dispatch(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/contrib/auth/mixins.py" in dispatch
  56.         return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/contrib/auth/mixins.py" in dispatch
  92.         return super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py" in get
  74.         return super().get(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/base.py" in get
  111.         return super().get(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/base.py" in get
  74.         return super().get(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/list.py" in get
  160.         self.object_list = self.get_queryset()

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py" in get_queryset
  122.             queryset = queryset.filter(lucy_guide__in=guide)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/query.py" in filter
  784.         return self._filter_or_exclude(False, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/query.py" in _filter_or_exclude
  802.             clone.query.add_q(Q(*args, **kwargs))

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in add_q
  1250.         clause, _ = self._add_q(q_object, self.used_aliases)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in _add_q
  1276.                     allow_joins=allow_joins, split_subq=split_subq,

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in build_filter
  1206.             condition = lookup_class(lhs, value)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/lookups.py" in __init__
  24.         self.rhs = self.get_prep_lookup()

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_lookups.py" in get_prep_lookup
  56.                 self.rhs = [target_field.get_prep_value(v) for v in self.rhs]

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_lookups.py" in <listcomp>
  56.                 self.rhs = [target_field.get_prep_value(v) for v in self.rhs]

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py" in get_prep_value
  966.         return int(value)

Exception Type: ValueError at /dashboard/families
Exception Value: invalid literal for int() with base 10: ''

如果我在视图的 get_queryset() 方法中设置跟踪,就在堆栈跟踪中的 122 之前,我看到 guide 确实是一个包含空字符串的空列表,就像在 self.request.GET 中一样:

> /Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py(121)get_queryset()
    120 
--> 121         if guide:
    122             queryset = queryset.filter(lucy_guide__in=guide)

ipdb> guide
['']

ipdb> self.request.GET
<QueryDict: 'q': ['Christine'], 'status': [''], 'next_outreach': [''], 'country': [''], 'vip': [''], 'app': [''], 'guide': [''], 'package': ['']>

除了提交空值的困难之外,我实际上希望request.GETguide 字段在这里有一个非空值。例如,如果我返回隐藏过滤器表单的“简化”版本,只有 guide 字段:

<form action="% url action %" method="get" class="left search col s6 hide-on-small-and-down" novalidate>
  <div class="input-field">
    <input id="search" placeholder=" placeholder "
        autocomplete="off" type="search" name="q"
        value=" search_form.q.value.strip|default:'' "
        data-query=" search_form.q.value.strip|default:'' ">
    <label for="search" class="active"><i class="material-icons search-icon">search</i></label>
    <i data-behavior="search-clear"
        class="material-icons search-icon"
        % if not search_form.q.value %style="display: none;"% endif %>close</i>
  </div>
  <input type="hidden" name="guide" value=" request.GET.guide "/>
</form>

然后我选择一个过滤器,并在搜索栏中输入一个搜索词:

然后,当我在视图的 get 方法之后设置跟踪时,如下所示:

def get(self, request, *args, **kwargs):
    import ipdb; ipdb.set_trace()

我确实看到qguide 都在request.GET 中:

ipdb> request.GET
<QueryDict: 'q': ['Christine'], 'guide': ['6']>

所以在request.GET.field_name 的“一般”表单中,我还希望request.GET 看起来像这样,其他字段也有空列表。似乎 Django 模板语言的点符号正试图从字面上对 'field_name' 进行字典或属性查找,但没有找到任何东西;也许我应该按照Django template how to look up a dictionary value with a variable 中的描述编写一个自定义过滤器来执行field.name 的字典查找?

【问题讨论】:

我认为你应该有name=field_name而不是name=field_name&lt;input type="hidden" name=field_name value="我们使用 访问模板中的变量 谢谢,这似乎修复了“键”,但我仍然为guide 字段获得ValueError:显然request.GET.field_name 不等于request.GET.guide 显然,它将为空,因为当您设置表单时,'loop 没有它们的值...。如果您从表单中设置了值,请尝试 field.value 在我们有回溯的图片中,哪个动作触发了这个视图? 【参考方案1】:

所以它基本上归结为在 GET 请求中不提交具有空字符串值的键。这似乎在 HTML 中本机不受支持;你需要一些 JS 魔法来实现这一点。看到这个帖子:How to prevent submitting the HTML form's input field value if it empty

但是,纯粹的 Django 解决方案是修改您的过滤器字典以排除为空的键。我不确定你是如何在 Django 中过滤这个的,但假设你已经覆盖了 get_queryset 方法;你总是可以这样做的:

def get_queryset(self):
    qs = super(YourView, self).get_queryset()
    filters = k, v for k, v in request.GET.items() if v != ''  # Be as generic/specific as needed here for exclusion
    qs = qs.filter(**filters)  # Fire your filtering logic here; this is a sample
    return qs

【讨论】:

这完全是他的要求,看看问题的更新..你在回答中问了一个问题,这应该是一个评论 啊;完全错过了更新;我的错。当然,会在 cmets 中提问。 除了从表单中过滤掉空值的问题之外,在所描述的情况下,我实际上并不希望 guide 字段首先为空;查看我更新的问题。【参考方案2】:

我终于通过编写自定义get 过滤器解决了这个问题,如Django template how to look up a dictionary value with a variable 中所述:

from django import template

register = template.Library()


@register.filter
def get(dictionary, key):
    return dictionary.get(key)

我更新_search.html如下:

% load get %

<form action="% url action %" method="get" class="left search col s6 hide-on-small-and-down" novalidate>
  <div class="input-field">
    <input id="search" placeholder=" placeholder "
        autocomplete="off" type="search" name="q"
        value=" search_form.q.value.strip|default:'' "
        data-query=" search_form.q.value.strip|default:'' ">
    <label for="search" class="active"><i class="material-icons search-icon">search</i></label>
    <i data-behavior="search-clear"
        class="material-icons search-icon"
        % if not search_form.q.value %style="display: none;"% endif %>close</i>
  </div>
  % if filter_form %
    % for field in filter_form %
      <input type="hidden" name=" field.name " value=" request.GET|get:field.name "/>
    % endfor %
  % endif %
</form>

现在,如果我尝试搜索过滤后的结果,它会按预期工作:

请注意,这也适用于未应用的过滤器 - 这些过滤器的值是 None 而不是空字符串 - 无需在表单中过滤掉它们。

【讨论】:

以上是关于在 Django 模板中,如何指定本身就是属性的字典键?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django 模板中使用 href 属性

为啥django不处理PUT方法带过来的表单

如何访问 django 模板中的 django ManyToManyField

如何在 Django 表单的输入中添加额外的属性?

如何在 Django 模板中使用这个 for 循环? [关闭]

渲染后如何获取模板(Django)