Django admin.py 过滤器中的 HTML 输入文本框

Posted

技术标签:

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

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

    作者没有发布models.py,因此很难根据我的需要更改代码(+ 没有cmets) 使用了 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发送)

感谢您的所有帖子。

【问题讨论】:

【参考方案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_black 的 solution 不完全适合,因为 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
    , )

【讨论】:

以上是关于Django admin.py 过滤器中的 HTML 输入文本框的主要内容,如果未能解决你的问题,请参考以下文章

在 Django admin 中,如何按组过滤用户?

Django 1.6 到 1.11 Admin.py 迁移

Django 命令行工具django-admin.py与manage.py

Django Admin后台管理

django-admin 和django-admin.py的区别

国际化时django.po中的msgstr =“”为中文时,django-admin.py compilemessages 出错:无效的多字节序列