【问题标题】:HTML input textbox in Django admin.py filterDjango admin.py 过滤器中的 HTML 输入文本框
【发布时间】:2011-09-27 15:18:27
【问题描述】:

我想在 Django (admin.py) 中使用 HTML 输入文本框中的文本过滤数据。我需要按他们所在的城市过滤公司,并且所有城市的列表太长。我想用一个文本输入替换过滤器中所有城市的列表。我发现了类似的东西 这里http://djangosnippets.org/snippets/2429/但是有两个问题:

  1. 作者没有发布models.py,因此很难根据我的需要更改代码(+ 没有cmets)
  2. 使用了 UserFieldFilterSpec(RelatedFilterSpec) 类:但我需要使用 AllValuesFilterSpec 而不是 RelatedFilterSpec(更多信息在文件 django/contrib/admin/filterspecs.py 中),因为城镇列表与 comapny 属于同一类(应该按城镇类别,它们应该通过外键引用公司(多对多关系),但由于某些原因,必须这样做)

models.py 的重要部分看起来像这样

class Company(models.Model):
    title = models.CharField(max_length=150,blank=False)
    city = models.CharField(max_length=50,blank=True)

还有一些来自 admin.py 的东西

class CatalogAdmin(admin.ModelAdmin):
    form = CatalogForm
    list_display = ('title','city') 
    list_filter = ['city',]

再说一次,我需要: 1. 在 Django 过滤器中显示一个文本输入,而不是列出 od 城市 2. 在该文本输入中输入城市名称后,按城市过滤数据(过滤请求可以通过一些提交按钮或通过javascript发送)

感谢您的所有帖子。

【问题讨论】:

    标签: html django input filter admin


    【解决方案1】:

    如果有人仍然需要这个。它在模板中有点hackish,但在没有一段js的情况下实现。

    filters.py:

    from django.contrib.admin import ListFilter
    from django.core.exceptions import ImproperlyConfigured
    
    
    class SingleTextInputFilter(ListFilter):
        """
        renders filter form with text input and submit button
        """
        parameter_name = None
        template = "admin/textinput_filter.html"
    
        def __init__(self, request, params, model, model_admin):
            super(SingleTextInputFilter, self).__init__(
                request, params, model, model_admin)
            if self.parameter_name is None:
                raise ImproperlyConfigured(
                    "The list filter '%s' does not specify "
                    "a 'parameter_name'." % self.__class__.__name__)
    
            if self.parameter_name in params:
                value = params.pop(self.parameter_name)
                self.used_parameters[self.parameter_name] = value
    
        def value(self):
            """
            Returns the value (in string format) provided in the request's
            query string for this filter, if any. If the value wasn't provided then
            returns None.
            """
            return self.used_parameters.get(self.parameter_name, None)
    
        def has_output(self):
            return True
    
        def expected_parameters(self):
            """
            Returns the list of parameter names that are expected from the
            request's query string and that will be used by this filter.
            """
            return [self.parameter_name]
    
        def choices(self, cl):
            all_choice = {
                'selected': self.value() is None,
                'query_string': cl.get_query_string({}, [self.parameter_name]),
                'display': _('All'),
            }
            return ({
                'get_query': cl.params,
                'current_value': self.value(),
                'all_choice': all_choice,
                'parameter_name': self.parameter_name
            }, )
    

    templates/admin/textinput_filter.html:

    {% load i18n %}
    <h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
    
    {#i for item, to be short in names#}
    {% with choices.0 as i %}
    <ul>
        <li>
            <form method="get">
                <input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
    
                {#create hidden inputs to preserve values from other filters and search field#}
                {% for k, v in i.get_query.items %}
                    {% if not k == i.parameter_name %}
                        <input type="hidden" name="{{ k }}" value="{{ v }}">
                    {% endif %}
                {% endfor %}
                <input type="submit" value="{% trans 'apply' %}">
            </form>
        </li>
    
        {#show "All" link to reset current filter#}
        <li{% if i.all_choice.selected %} class="selected"{% endif %}>
            <a href="{{ i.all_choice.query_string|iriencode }}">
                {{ i.all_choice.display }}
            </a>
        </li>
    </ul>
    {% endwith %}
    

    然后根据你在admin.py中的模型:

    class CatalogCityFilter(SingleTextInputFilter):
        title = 'City'
        parameter_name = 'city'
    
        def queryset(self, request, queryset):
            if self.value():
                return queryset.filter(city__iexact=self.value())
    
    class CatalogAdmin(admin.ModelAdmin):
        form = CatalogForm
        list_display = ('title','city') 
        list_filter = [CatalogCityFilter,]
    

    准备使用的过滤器看起来像这样。

    【讨论】:

    • 非常感谢您提供此代码 sn-p!你刚刚为我节省了几个小时的工作时间。但是,您的示例中有一个小错误:CatalogCityFilter.queryset 调用应返回查询集。
    • @devsnd 此代码在您的情况下是否引发异常?正如我从 django 资料中看到的那样:对于 self.filter_specs 中的 filter_spec:new_qs = filter_spec.queryset(request, qs) if new_qs is not None: qs = new_qs github.com/django/django/blob/master/django/contrib/admin/views/… if filter 返回 None 没有任何反应。
    • 无异常,但在返回查询集之前它不起作用,因为 filter 返回应用了过滤器的查询集的新副本。
    • @devsnd 哦,是的,很抱歉,现在我明白了。只是在现有项目中查看我的代码,而不是我的答案。一定是一些蟒蛇精灵带走了我的return :) 修复它。
    • 我已经寻找这个解决方案 2 个月了。非常感谢您的好心先生!
    【解决方案2】:

    我正在运行 Django 1.10、1.11 和 r_blacksolution 不完全适合,因为 Django 抱怨过滤器字段必须继承自 'FieldListFilter'。

    因此,从 FieldListFilter 继承的过滤器的简单更改处理了 Django 的抱怨,而不必同时为每个字段指定一个新类。

    class SingleTextInputFilter(admin.FieldListFilter):
        """
        renders filter form with text input and submit button
        """
    
        parameter_name = None
        template = "admin/textinput_filter.html"
    
        def __init__(self, field, request, params, model, model_admin, field_path):
            super().__init__(field, request, params, model, model_admin, field_path)
            if self.parameter_name is None:
                self.parameter_name = self.field.name
    
            if self.parameter_name in params:
                value = params.pop(self.parameter_name)
                self.used_parameters[self.parameter_name] = value
    
        def queryset(self, request, queryset):
            if self.value():
                return queryset.filter(imei__icontains=self.value())
    
        def value(self):
            """
            Returns the value (in string format) provided in the request's
            query string for this filter, if any. If the value wasn't provided then
            returns None.
            """
            return self.used_parameters.get(self.parameter_name, None)
    
        def has_output(self):
            return True
    
        def expected_parameters(self):
            """
            Returns the list of parameter names that are expected from the
            request's query string and that will be used by this filter.
            """
            return [self.parameter_name]
    
        def choices(self, cl):
            all_choice = {
                'selected': self.value() is None,
                'query_string': cl.get_query_string({}, [self.parameter_name]),
                'display': _('All'),
            }
            return ({
                'get_query': cl.params,
                'current_value': self.value(),
                'all_choice': all_choice,
                'parameter_name': self.parameter_name
            }, )
    

    templates/admin/textinput_filter.html(不变):

    {% load i18n %}
    <h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
    
    {#i for item, to be short in names#}
    {% with choices.0 as i %}
    <ul>
        <li>
            <form method="get">
                <input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
    
                {#create hidden inputs to preserve values from other filters and search field#}
                {% for k, v in i.get_query.items %}
                    {% if not k == i.parameter_name %}
                        <input type="hidden" name="{{ k }}" value="{{ v }}">
                    {% endif %}
                {% endfor %}
                <input type="submit" value="{% trans 'apply' %}">
            </form>
        </li>
    
        {#show "All" link to reset current filter#}
        <li{% if i.all_choice.selected %} class="selected"{% endif %}>
            <a href="{{ i.all_choice.query_string|iriencode }}">
                {{ i.all_choice.display }}
            </a>
        </li>
    </ul>
    {% endwith %}
    

    用法:

    class MyAdmin(admin.ModelAdmin):
        list_display = [your fields]
        list_filter = [('field 1', SingleTextInputFilter), ('field 2', SingleTextInputFilter), further fields]
    

    【讨论】:

    • 谢谢,您只需要删除查看特定 imei (telco?) 字段的自定义查询集函数。
    • 嗯,不:该函数是执行过滤的函数。但是你确实需要用你自己的字段名重命名“imei”......实际上,我忘了在这个例子中修复那个;)
    • 您可以通过编程方式填写字段名称,例如:return queryset.filter(**{self.field.name: self.value()})
    【解决方案3】:

    虽然这实际上不是您的问题,但这听起来是Django-Selectables 的完美解决方案,您只需几行即可添加一个 AJAX 驱动的 CharField 表单,该表单将从城市列表中选择其条目。查看上面链接中列出的示例。

    【讨论】:

    • 这真的不是我想要的。我的问题是显示有效的文本输入过滤器。自动完成功能很好,我想稍后添加。无论如何,谢谢您的回复。
    • 好的,这是我自己想出来的。我在 filterspecs.py 中创建了自己的过滤器(我知道这样做很讨厌)。如果您以这种方式尝试,请小心注册您的过滤器。您的过滤器应该在系统过滤器之前注册。比在 models.py 中将您的过滤器分配给它所属的属性。在过滤器中,我使用了一些更改发布的 url 参数的东西。按一个城市过滤由 city=Prague 完成,但如果您想按过滤器列表过滤,请使用 city__in=Prague,Wien,Dublin。有很多更好的方法可以做到这一点(查询、AJAX、..),但我只是在学习。
    【解决方案4】:

    以下是查询集函数中字段名称的修复..

    class SingleTextInputFilter(admin.FieldListFilter):
    """
    renders filter form with text input and submit button
    """
    
    parameter_name = None
    template = "admin/textinput_filter.html"
    
    def __init__(self, field, request, params, model, model_admin, field_path):
        super().__init__(field, request, params, model, model_admin, field_path)
        if self.parameter_name is None:
            self.parameter_name = self.field.name
    
        if self.parameter_name in params:
            value = params.pop(self.parameter_name)
            self.used_parameters[self.parameter_name] = value
    
    def queryset(self, request, queryset):
    
        variable_column = self.parameter_name
        search_type = 'icontains'
        filter = variable_column + '__' + search_type
    
        if self.value():
            return queryset.filter(**{filter: self.value()})
    def value(self):
        """
        Returns the value (in string format) provided in the request's
        query string for this filter, if any. If the value wasn't provided then
        returns None.
        """
        return self.used_parameters.get(self.parameter_name, None)
    
    def has_output(self):
        return True
    
    def expected_parameters(self):
        """
        Returns the list of parameter names that are expected from the
        request's query string and that will be used by this filter.
        """
        return [self.parameter_name]
    
    def choices(self, cl):
        all_choice = {
            'selected': self.value() is None,
            'query_string': cl.get_query_string({}, [self.parameter_name]),
            'display': ('All'),
        }
        return ({
            'get_query': cl.params,
            'current_value': self.value(),
            'all_choice': all_choice,
            'parameter_name': self.parameter_name
        }, )
    

    【讨论】:

      猜你喜欢
      • 2021-11-02
      • 1970-01-01
      • 2016-09-27
      • 1970-01-01
      • 2014-02-27
      • 1970-01-01
      • 2018-03-15
      • 2021-01-03
      • 2011-04-13
      相关资源
      最近更新 更多