是否可以在 Django 管理中限制在另一个模型上定义关系的模型的过滤器 ManyToMany/Foreign Key?

发布于 2024-10-26 05:37:05 字数 489 浏览 4 评论 0原文

我知道,这个标题有点晦涩难懂,但我想不出更简洁的方式来表达它。问题是:

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

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

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

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

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

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.

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. As a result, I can't attach my filterspec to 'groups' in my proxy model.

Is there any other way to apply the filterspec? Alternatively, is there a better approach to filtering the items returned by the default filterspec?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

つ可否回来 2024-11-02 05:37:05

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

我采取的方法是修改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用于< code>trans,account_admin 用于我的自定义模板标记(稍后讨论)admin_list_group_filter

变量 spec.title 保存正在过滤的字段的标题。由于我正在尝试更改组过滤器的显示方式,因此我正在检查它是否等于“组”。如果是,那么我使用自定义模板标签,否则,它会回退到默认的 Django 模板标签。

第三,我们创建模板标签。我基本上只是复制了默认的 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)

我在这里唯一改变的是向名为“groups”的方法添加一个新参数,以便我可以传入之前过滤的组列表,以及向字典添加一个新键以将该列表传递到模板标签的上下文。我还将标签使用的模板更改为我们现在要创建的新模板。

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

# 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.display 保存将作为过滤依据的实例的实际名称。显然,我已经设置了一个检查来查看该值是否在我想要显示的组的过滤列表中,并且仅在存在时才渲染链接。

所以,它有点复杂,但效果非常好。就像这样,您就有了一个正是您想要的过滤器列表,而不是 Django 生成的默认过滤器。

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. There's quite a number of changes to make, though.

First, add a changelist_view method to your ModelAdmin:

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

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

The variable spec.title holds the title of the field that's being filtered on. 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.

Third, we create the template tag. I basically just copied the default Django template tag and made the necessary modifications.

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

# 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. Specifically, choice.display holds the actual name of the instance that will be filtered by. 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.

绝影如岚 2024-11-02 05:37:05

我要立即告诉你,我以前从未这样做过,所以请对此持保留态度。

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

您的自定义 ChangeList 类将简单地覆盖 get_filters,以便您可以为 group 字段映射自定义 FilterSpec

您可能感兴趣的另一件事是来自功能请求 ticket 的用于指定自定义过滤器规格的补丁。最新的补丁还不适用于 Django 1.3rc1,尽管 @bendavis78 最近发布说他正在开发一个新补丁,但根据您的 Django 版本,它可能会完全适用。

看起来它几乎没有错过进入 1.3 里程碑的机会,所以我认为一旦 Django 1.4 上的工作开始,它就会进入主干。

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.

Your custom ChangeList class would simply override get_filters, so you can map your custom FilterSpec for the group field.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文