可以限制Django admin中的过滤器ManyToMany / Foreign Key用于在另一个模型上定义关系的模型吗?

时间:2011-03-25 16:21:04

标签: python django django-admin django-admin-filters

所以标题有点迟钝,我知道,但我想不出更简洁的说法。这是问题所在:

我为“用户类型”创建了两个代理模型,两者都继承自django.contrib.auth.User。每个人都有一个自定义管理器,将查询集限制为属于特定组的项目。具体来说,有一个PressUser,它是属于“Press”组和StaffUser的任何用户,是“按”之外的任何其他组中的任何用户。

问题在于,当我在StaffUsers modeladmin上向list_filters添加'groups'时,生成的过滤器选项是每个可用的组,包括“Press”,而不仅仅是StaffUsers可用的组。

我在网上进行了一些研究并提出了一个自定义滤镜规范,可以产生我想要的行为,但问题是用户模型的'groups'属性实际上是从Group模型应用的related_name。因此,我无法将filterpec附加到代理模型中的“groups”。

还有其他方法可以应用filterspec吗?或者,是否有更好的方法来过滤默认filterspec返回的项目?

2 个答案:

答案 0 :(得分:3)

所以,我能够解决自己的问题。对于那些可能遇到类似情况的人来说,步骤如下:

我采取的方法是修改change_list.html模板并手动过滤掉我不想包含的项目。但是,要做出很多改变。

首先,向您的ModelAdmin添加changelist_view方法:

# myproject/account/admin.py

class StaffUserAdmin(models.ModelAdmin):
    ...
    def changelist_view(self, request, extra_context=None):
        groups = Group.objects.exclude(name__in=['Press',]).values_list('name')
        extra_context = {
            'groups': [x[0] for x in groups],
        }
        return super(StaffUserAdmin, self).changelist_view(request,
            extra_context=extra_context)

基本上,我们在这里所做的就是将我们想要使用的已过滤的组列表传递到模板的上下文中。

其次,为您的应用创建一个change_list.html模板。

# myproject/templates/admin/auth/staffuser/change_list.html

{% extends "admin/change_list.html" %}

{% load admin_list %}
{% load i18n %}
{% load account_admin %}

{% block filters %}

    {% if cl.has_filters %}
    <div id="changelist-filter">
        <h2>{% trans 'Filter' %}</h2>
        {% for spec in cl.filter_specs %}
            {% ifequal spec.title 'group' %}
                {% admin_list_group_filter cl spec groups %}
            {% else %}
                {% admin_list_filter cl spec %}
            {% endifequal %}
        {% endfor %}
    </div>
    {% endif %}

{% endblock filters %}

这个值得一点解释。首先,加载模板标记:admin_list用于负责呈现过滤器的默认Django模板标记,admin_list_filteri18n用于trans,{{1我的自定义模板标记(在一秒内讨论),account_admin

变量admin_list_group_filter包含要过滤的字段的标题。由于我试图改变组过滤器的显示方式,我正在检查它是否等于'组'。如果是,那么我使用自定义模板标签,否则,它会回退到默认的Django模板标签。

第三,我们创建模板标签。我基本上只是复制了默认的Django模板标签并进行了必要的修改。

spec.title

我在这里改变的唯一事情就是在方法中添加一个名为'groups'的新参数,这样我就可以从之前传入我过滤的组列表,并在字典中添加一个新的键来传递列出模板标签的上下文。我还将标签使用的模板更改为我们即将创建的新模板。

第四,为模板标签创建模板。

# myproject/account/templatetags/account_admin.py

from django.template import Library

register = Library()

def admin_list_group_filter(cl, spec, groups):
    return {'title': spec.title, 'choices' : list(spec.choices(cl)), 'groups': groups }
admin_list_group_filter = register.inclusion_tag('admin/auth/group_filter.html')(admin_list_group_filter)

这里没什么大惊喜。我们所做的就是将所有部分组合在一起。每个# myproject/templates/admin/auth/group_filter.html {% load i18n %} <h3>{% blocktrans with title as filter_title %} By {{ filter_title }} {% endblocktrans %}</h3> <ul> {% for choice in choices %} {% if choice.display in groups %} <li{% if choice.selected %} class="selected"{% endif %}> <a href="{{ choice.query_string|iriencode }}">{{ choice.display }}</a></li> {% endif %} {% endfor %} </ul> 都是一个字典,其中包含构建过滤器链接所需的所有值。具体来说,choice保存将被过滤的实例的实际名称。显然,我已经设置了一个检查,看看这个值是否在我要显示的已过滤的组列表中,并且只显示链接。

所以,它有点参与,但效果非常好。就像那样,你有一个过滤器列表,它正是你想要的,而不是Django生成的默认过滤器。

答案 1 :(得分:1)

我要告诉你,我从来没有在自己面前做过这件事,所以带上一粒盐。

我建议您覆盖ModelAdmin上的get_changelist,以返回自定义ChangeList类,您可以在admin模块中的某个位置定义该类。

您的自定义ChangeList课程会覆盖get_filters,因此您可以为FilterSpec字段映射自定义group

您可能感兴趣的另一件事是功能请求ticket中的补丁,用于指定自定义过滤器规范。最新补丁对Django 1.3rc1不起作用,虽然@ bendavis78最近发布他正在开发一个新的补丁,但根据你的Django版本,它可以干净利用。

看起来它几乎没有错过切入以进入1.3里程碑,所以我认为它会在Django 1.4工作时立即进入后备箱。