在 Django 模板中,如何指定本身就是属性的字典键? [英] In a Django template, how to specify a dictionary key which is itself an attribute?

查看:25
本文介绍了在 Django 模板中,如何指定本身就是属性的字典键?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个带有 ListView 的 Django 项目,它有一个搜索表单,在视图的上下文中称为 search_form,还有一个过滤表单,filter_form.界面如下:

search_formfilter_form 最终都会改变 ListView

据我了解

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

更新 2

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

回溯:内部文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/exception.py"41. response = get_response(request)_get_response 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/base.py"187. response = self.process_exception_by_middleware(e, request)_get_response 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/base.py"185.响应=wrapped_callback(请求,*callback_args,**callback_kwargs)查看文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/base.py"68. 返回 self.dispatch(request, *args, **kwargs)调度中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/two_factor/views/mixins.py"82. 返回 super(OTPRequiredMixin, self).dispatch(request, *args, **kwargs)调度中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/contrib/auth/mixins.py"56. return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)调度中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/contrib/auth/mixins.py"92. 返回 super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs)调度中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/base.py"88. 返回处理程序(请求,*args,**kwargs)获取文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py"74. 返回 super().get(request, *args, **kwargs)获取文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/base.py"111. 返回 super().get(request, *args, **kwargs)获取文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/base.py"74. 返回 super().get(request, *args, **kwargs)获取文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/list.py"160. self.object_list = self.get_queryset()get_queryset 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py"122. queryset = queryset.filter(lucy_guide__in=guide)过滤器中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/query.py"784. 返回 self._filter_or_exclude(False, *args, **kwargs)_filter_or_exclude 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/query.py"802.clone.query.add_q(Q(*args, **kwargs))add_q 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py"1250. 子句,_ = self._add_q(q_object, self.used_aliases)_add_q 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py"1276.allow_joins=allow_joins,split_subq=split_subq,build_filter 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py"1206.条件=lookup_class(lhs, value)__init__ 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/lookups.py"24. self.rhs = self.get_prep_lookup()get_prep_lookup 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_lookups.py"56. self.rhs = [target_field.get_prep_value(v) for v in self.rhs]<listcomp>中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_lookups.py"56. self.rhs = [target_field.get_prep_value(v) for v in self.rhs]get_prep_value 中的文件/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py"966.返回整数(值)异常类型:/dashboard/families 处的 ValueError异常值:以 10 为基数的 int() 的无效文字:''

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

>/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py(121)get_queryset()120-->121 如果指导:第122话ipdb>指导['']ipdb>self.request.GET<QueryDict: {'q': ['Christine'], 'status': [''], 'next_outreach': [''], 'country': [''], 'vip': [''], 'app': [''], 'guide': [''], 'package': ['']}>

除了提交空值的困难之外,我实际上希望 request.GETguide 字段在此处具有非空值.例如,如果我返回到隐藏过滤器表单的简化"版本,只有 guide 字段:

<input type="hidden" name="guide" value="{{ request.GET.guide }}"/></表单>

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

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

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

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

ipdb>请求.GET<QueryDict: {'q': ['Christine'], 'guide': ['6']}>

所以在带有 request.GET.field_name 的通用"表单中,我也希望 request.GET 看起来像这样,还有空列表用于其他领域.似乎 Django 模板语言的点符号试图从字面上对 'field_name' 进行字典或属性查找,但没有找到任何东西;也许我应该编写一个自定义过滤器,如

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

I'm working on a Django project with a ListView that has both a search form, known in the view's context as search_form, and a filter form, filter_form. The interface looks like this:

Both the search_form and the filter_form ultimately change what is returned by the ListView's get_queryset method. I would like to make it such that when you first apply a filter and then do a search, it will search the filtered results.

At the moment, the 'reverse' functionality has already been implemented: when you first search and then filter, it will filter the search results. This is because of a hidden input element in the filter_form:

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

For comparison, the search bar has the following template, _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>

In the main list view, index.html, the search template is included like so:

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

In order to make the search form persist the filters, I've noticed that for the guide field only, the following works:

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

I would like to generalize this to include all the filters. I've tried the following:

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

However, if I try this I literally get "field_name" in the querystring:

As I understand from the DTL docs, the dot-notation implements dictionary lookup, attribute lookup, and list-index lookup. If I were to try something like

request.GET.field.name

it would probably try to look up "field" in the request.GET dictionary-like object and not find anything. In regular Python, what I'd essentially like to do is

request.GET[field.name]

I thought I could make this work with a with block, but apparently this doesn't work. Any advice on how to implement this?

Update

If I specify the name attribute for the input element as "{{field_name}}" instead of just field_name, like so,

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

The problem is that the value is being set to an empty string instead of the desired value, which leads to a ValueError for the guide field (which is a ModelChoiceField expecting an integer as input):

Why is the attribute lookup not working in this case?

Update 2

Responding to Lemayzeur's comment, the full traceback is:

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

If I set a trace in the view's get_queryset() method, just before 122 in the stack trace, I see that guide is indeed an empty list containing an empty string, as it is in 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': ['']}>

Aside from the difficulties in submitting the empty value, I actually would expect the guide field of request.GET to have a non-empty value here. For example, if I return to the 'simplified' version of the hidden filter form, with only the guide field:

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

Then I select a filter, and enter a search term in the search bar:

Then when I set a trace just after the view's get method, like so:

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

I do see that both q and guide are in request.GET:

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

So in the 'general' form with request.GET.field_name, I would also expect the request.GET to look like this, with also empty lists for the other fields. It seems like the Django Template Language's dot notation is trying to literally do a dictionary or attribute lookup for 'field_name' and not finding anything; perhaps I should write a custom filter as described in Django template how to look up a dictionary value with a variable to perform the dictionary lookup of field.name?

解决方案

I finally solved this by writing a custom get filter as described in 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)

I updated _search.html as follows:

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

Now, if I try to search a filtered result, it works as expected:

Note that this also works fine for the filters that are not applied - these have the value None instead of an empty string - without any need to filter them out in the form.

这篇关于在 Django 模板中,如何指定本身就是属性的字典键?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆