简体   繁体   English

可以在模型中限制关系的模型中限制过滤器的Django admin中的ManyToMany / Foreign Key吗?

[英]Possible to limit filters ManyToMany/Foreign Key in Django admin for a model where the relationship is defined on the other model?

So the title is a bit obtuse, I know, but I couldn't think of a more succinct way to state it. 所以我知道标题有点晦涩难懂,但是我想不出一种更简洁的陈述方式。 Here's the issue: 这是问题所在:

I've created two proxy models for "user types", both inheriting from django.contrib.auth.User. 我为“用户类型”创建了两个代理模型,它们都继承自django.contrib.auth.User。 Each has a custom manager limiting the queryset to items belonging to a particular Group. 每个都有一个自定义管理器,将查询集限制为属于特定组的项目。 Specifically, there's a PressUser which is any user belonging to the "Press" group and StaffUser which is any user in any other group than "Press". 具体来说,有一个PressUser是属于“ Press”组的任何用户,而StaffUser是属于除“ Press”之外的任何其他组的任何用户。

The issue is that when I add 'groups' to list_filters on my StaffUsers modeladmin, the resulting filter options are every group available, including "Press", and not just groups available to StaffUsers. 问题是,当我在StaffUsers模型管理员的list_filters中添加“组”时,生成的过滤器选项是每个可用组,包括“ Press”,而不仅是可​​供StaffUsers使用的组。

I've research a bit online and came up with a custom filterspec that should produce the behavior I want, but the problem is that the User model's 'groups' attribute is actually a related_name applied from the Group model. 我已经在网上进行了一些研究,并提出了一个自定义filterspec,该规范可以产生我想要的行为,但是问题是User模型的'groups'属性实际上是从Group模型应用的related_name。 As a result, I can't attach my filterspec to 'groups' in my proxy model. 结果,我无法将我的filterspec附加到代理模型中的“组”。

Is there any other way to apply the filterspec? 还有其他方法可以应用filterspec吗? Alternatively, is there a better approach to filtering the items returned by the default filterspec? 或者,是否有更好的方法来过滤默认filterspec返回的项目?

So, I was able to solve my own problem. 因此,我能够解决自己的问题。 For those that might run into a similar situation, here are the steps: 对于可能遇到类似情况的步骤,请按以下步骤操作:

The approach I took is to modify the change_list.html template and manually filter out the items I didn't want to be included. 我采用的方法是修改change_list.html模板并手动过滤掉我不想包含的项目。 There's quite a number of changes to make, though. 但是,需要进行很多更改。

First, add a changelist_view method to your ModelAdmin: 首先,向您的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)

Basically, all we're doing here is passing in the filtered list of Groups we want to use into the context for the template. 基本上,我们在这里要做的就是将要使用的组的过滤列表传递到模板的上下文中。

Second, create a change_list.html template for your app. 其次,为您的应用程序创建一个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 %}

This one deserves a little explanation. 这个值得一点解释。 First, the template tag loads: admin_list is used for the default Django template tag responsible for rendering the filters, admin_list_filter , i18n is used for trans , and account_admin is for my custom template tag (discussed in a sec), admin_list_group_filter . 首先,将加载模板标签: admin_list用于负责渲染过滤器的默认Django模板标签, admin_list_filteri18n用于transaccount_admin用于我的自定义模板标签(将在admin_list_group_filter秒钟内讨论) admin_list_group_filter

The variable spec.title holds the title of the field that's being filtered on. 变量spec.title包含要过滤的字段的标题。 Since I'm trying to alter how the Groups filter is displayed, I'm checking if it equals 'groups'. 由于我试图更改“组”过滤器的显示方式,因此我正在检查它是否等于“组”。 If it does, then I use my custom template tag, otherwise, it falls back to the default Django template tag. 如果是这样,那么我将使用我的自定义模板标签,否则,它将使用默认的Django模板标签。

Third, we create the template tag. 第三,我们创建模板标签。 I basically just copied the default Django template tag and made the necessary modifications. 我基本上只是复制了默认的Django模板标签并进行了必要的修改。

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

The only things that I've changed here are adding a new argument to the method called 'groups' so I can pass in my filtered list of groups from before, as well as adding a new key to the dictionary to pass that list into the context for the template tag. 我在这里所做的唯一更改是在名为“ groups”的方法中添加了一个新参数,这样我就可以传入之前过滤过的组列表,并向字典中添加一个新键以将该列表传递到模板标记的上下文。 I've also changed the template the tag uses to a new one that we're about to create now. 我还将标签使用的模板更改为我们现在要创建的新模板。

Fourth, create the template for the template tag. 第四,为template标签创建模板。

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

No big surprises here. 这里没有什么大惊喜。 All we're doing is putting all the pieces together. 我们要做的就是将所有部分放在一起。 Each choice is a dictionary with all the values needed to construct the filter link. 每个choice都是一个字典,其中包含构造过滤器链接所需的所有值。 Specifically, choice.display holds the actual name of the instance that will be filtered by. 具体来说, choice.display保存将被其过滤的实例的实际名称。 Obviously enough, I've set up a check to see if this value is in my filtered list of groups I want to show, and only render the link if it is. 显然,我已经进行了检查,以查看此值是否在我要显示的组的筛选列表中,并且仅在显示链接时才显示。

So, it's a bit involved but works remarkably well. 因此,虽然有点复杂,但是效果很好。 Just like that, you have a list of filters that is exactly what you want instead of the default ones generated by Django. 就像这样,您将拥有一个正是您想要的过滤器列表,而不是Django生成的默认过滤器。

I'm going to tell you off the bat that I've never done this before myself, so take it with a grain of salt. 我要马上告诉你,我从来没有做过这件事,所以要加一点盐。

What I'd suggest would be to override get_changelist on your ModelAdmin , to return a custom ChangeList class, which you can define somewhere in your admin module. 我会建议将覆盖get_changelistModelAdmin ,以返回自定义ChangeList类,您可以在某处定义admin模块。

Your custom ChangeList class would simply override get_filters , so you can map your custom FilterSpec for the group field. 您的自定义ChangeList类将仅覆盖get_filters ,因此您可以将自定义FilterSpec映射到group字段。

Another thing that might interest you are patches from the feature request ticket for specifying custom filter specs. 您可能会感兴趣的另一件事是功能请求票证中的补丁程序,用于指定自定义过滤器规格。 The latest patch doesn't work for Django 1.3rc1 yet, although @bendavis78 recently posted that he's working on a new one, but depending on your version of Django it may apply cleanly. 最新的补丁程序不适用于Django 1.3rc1,尽管@ bendavis78最近发布了他正在开发一个新补丁程序的信息,但是取决于您的Django版本,它可能会完全适用。

It looks like it barely missed the cut to get included into the 1.3 milestone, so I figure it's going to make it into the trunk as soon as work beings on Django 1.4. 看起来几乎没错过过将其纳入1.3里程碑的工作,所以我认为一旦Django 1.4投入使用,它就会进入主流。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM