Django-Admin:UserProfile中的list_filter属性 [英] Django-Admin: list_filter attribute from UserProfile

查看:104
本文介绍了Django-Admin:UserProfile中的list_filter属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想允许我的网站的管理员过滤来自管理网站上特定国家/地区的用户。所以自然要做的就是这样的:

I want to allow the admins of my site to filter users from a specific country on the Admin Site. So the natural thing to do would be something like this:

#admin.py
class UserAdmin(django.contrib.auth.admin.UserAdmin):
    list_filter=('userprofile__country__name',)

#models.py
class UserProfile(models.Model)
    ...
    country=models.ForeignKey('Country')

class Country(models.Model)
    ...
    name=models.CharField(max_length=32)

但是,由于用户和他们的UserProfiles在django中处理,导致以下错误:是指'模型'User'
中缺少的'userprofile__country__name'字段'UserAdmin.list_filter [0]'的字段'

But, because of the way Users and their UserProfiles are handled in django this leads to the following error:

'UserAdmin.list_filter[0]' refers to field 'userprofile__country__name' that is missing from model 'User'

如何解决这个限制?

推荐答案

您正在寻找的是自定义管理FilterSpecs 。坏消息是,对这些的支持可能不会很快出货(您可以跟踪讨论这里)。

What you are looking for is custom admin FilterSpecs. The bad news is, the support for those might not supposed to ship soon (you can track the discussion here).

但是,以肮脏的黑客为代价,您可以解决限制。在代码中潜入之前构建了 FilterSpecs 的一些亮点:

However, at the price of a dirty hack, you can workaround the limitation. Some highlights on how FilterSpecs are built before diving in the code :


  • 构建时在页面上显示的 FilterSpec 的列表,Django使用您在 list_filter
  • 中提供的字段列表
  • 这些字段需要是模型上的真实字段,而不是反向关系,也不是自定义属性。

  • Django维护一个 FilterSpec 类,每个与测试相关联。

  • 对于 list_filter ,Django将使用测试函数返回的第一个 FilterSpec True 对于该领域。

  • When building the list of FilterSpec to display on the page, Django uses the list of fields you provided in list_filter
  • Those fields needs to be real fields on the model, not reverse relationship, nor custom properties.
  • Django maintains a list of FilterSpec classes, each associated with a test function.
  • For each fields in list_filter, Django will use the first FilterSpec class for which the test function returns True for the field.

好的,现在考虑到这一点,看看下面的代码。它是从一个django片段进行的。代码的组织由您自行决定,请记住,应由 admin 应用程序导入。

Ok, now with this in mind, have a look at the following code. It is adapted from a django snippet. The organization of the code is left to your discretion, just keep in mind this should be imported by the admin app.

from myapp.models import UserProfile, Country
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin

from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _

class ProfileCountryFilterSpec(ChoicesFilterSpec):
    def __init__(self, f, request, params, model, model_admin):
        ChoicesFilterSpec.__init__(self, f, request, params, model, model_admin)

        # The lookup string that will be added to the queryset
        # by this filter
        self.lookup_kwarg = 'userprofile__country__name'
        # get the current filter value from GET (we will use it to know
        # which filter item is selected)
        self.lookup_val = request.GET.get(self.lookup_kwarg)

        # Prepare the list of unique, country name, ordered alphabetically
        country_qs = Country.objects.distinct().order_by('name')
        self.lookup_choices = country_qs.values_list('name', flat=True)

    def choices(self, cl):
        # Generator that returns all the possible item in the filter
        # including an 'All' item.
        yield { 'selected': self.lookup_val is None,
                'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
                'display': _('All') }
        for val in self.lookup_choices:
            yield { 'selected' : smart_unicode(val) == self.lookup_val,
                    'query_string': cl.get_query_string({self.lookup_kwarg: val}),
                    'display': val }

    def title(self):
        # return the title displayed above your filter
        return _('user\'s country')

# Here, we insert the new FilterSpec at the first position, to be sure
# it gets picked up before any other
FilterSpec.filter_specs.insert(0,
  # If the field has a `profilecountry_filter` attribute set to True
  # the this FilterSpec will be used
  (lambda f: getattr(f, 'profilecountry_filter', False), ProfileCountryFilterSpec)
)


# Now, how to use this filter in UserAdmin,
# We have to use one of the field of User model and
# add a profilecountry_filter attribute to it.
# This field will then activate the country filter if we
# place it in `list_filter`, but we won't be able to use
# it in its own filter anymore.

User._meta.get_field('email').profilecountry_filter = True

class MyUserAdmin(UserAdmin):
  list_filter = ('email',) + UserAdmin.list_filter

# register the new UserAdmin
from django.contrib.admin import site
site.unregister(User)
site.register(User, MyUserAdmin)

显然不是一个灵丹妙药,但它会做这个工作,等待一个更好的解决方案来。例如,将子类 ChangeList 并覆盖 get_filters

It's clearly not a panacea but it will do the job, waiting for a better solution to come up.(for example, one that will subclass ChangeList and override get_filters).

这篇关于Django-Admin:UserProfile中的list_filter属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆