在 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_form
和filter_form
最终都会改变ListView
的get_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.GET
的guide
字段在这里有一个非空值。例如,如果我返回隐藏过滤器表单的“简化”版本,只有 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()
我确实看到q
和guide
都在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
:<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 模板中的 django ManyToManyField