代码之家  ›  专栏  ›  技术社区  ›  Kurt Peek

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

  •  1
  • Kurt Peek  · 技术社区  · 7 年前

    我正在和一个 ListView 它有两个搜索表单,在视图上下文中称为 search_form ,和过滤器窗体, filter_form .界面如下所示:

    enter image description here

    两者 search\u表单 以及 filter\u表单 最终更改 列表视图 get_queryset 方法我希望这样,当您首先应用过滤器,然后进行搜索时,它将搜索过滤后的结果。

    目前,“反向”功能已经实现:当您首先搜索然后过滤时,它将过滤搜索结果。这是因为 input 中的元素 filter\u表单 :

    <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\u name”:

    enter image description here

    据我所知 DTL docs ,点表示法实现字典查找、属性查找和列表索引查找。如果我想试试

    request.GET.field.name
    

    它可能会在 request.GET 像字典一样的对象,什么也找不到。在常规Python中,我基本上想做的是

    request.GET[field.name]
    

    我想我可以用 with 阻止,但显然这不起作用。关于如何实现这一点,有什么建议吗?

    使现代化

    如果我指定 name 的属性 输入 元素组件 "{{field_name}}" 而不仅仅是 field_name ,就像这样,

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

    问题是 value 正在设置为空字符串而不是所需的值,这将导致 ValueError 对于 指导 字段(它是 ModelChoiceField 输入应为整数):

    enter image description here

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

    更新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之前,我看到 指导 确实是一个包含空字符串的空列表,如中所示 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': ['']}>
    

    除了提交空值的困难之外,我实际上希望 指导 字段 要求收到 在此处具有非空值。例如,如果我返回到隐藏筛选器表单的“简化”版本,则仅使用 指导 字段:

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

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

    enter image description here

    然后当我在视图 get 方法,如下所示:

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

    我确实看到了 q 指导 都在 要求收到 :

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

    因此,在“一般”形式中 request.GET.field_name ,我也希望 要求收到 如下所示,其他字段的列表也是空的。Django模板语言的点表示法似乎正试图对 'field_name' 什么也没找到;也许我应该编写一个自定义过滤器,如中所述 Django template how to look up a dictionary value with a variable 执行字典查找的步骤 field.name ?

    2 回复  |  直到 7 年前
        1
  •  1
  •   rtindru    7 年前

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

    然而,一个纯粹的Django解决方案是修改过滤器dict以排除null键。我不确定您在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
    
        2
  •  0
  •   Kurt Peek    7 年前

    我最终通过写一个习惯来解决这个问题 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>
    

    现在,如果我尝试搜索筛选的结果,它将按预期工作:

    enter image description here

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