【问题标题】:Django-Admin: list_filter attribute from UserProfileDjango-Admin:来自 UserProfile 的 list_filter 属性
【发布时间】:2011-01-16 03:34:08
【问题描述】:

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

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

但是,由于用户及其用户配置文件在 django 中的处理方式,这会导致以下错误:

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

如何绕过这个限制?

【问题讨论】:

    标签: django django-admin


    【解决方案1】:

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

    但是,以肮脏的 hack 为代价,您可以解决此限制。在深入研究代码之前如何构建FilterSpecs 的一些亮点:

    • 在构建FilterSpec 列表以显示在页面上时,Django 使用您在list_filter 中提供的字段列表
    • 这些字段必须是模型上的真实字段,而不是反向关系,也不是自定义属性。
    • Django 维护着一个FilterSpec 类的列表,每个类都与一个test 函数相关联。
    • 对于list_filter 中的每个字段,Django 将使用test 函数为该字段返回True 的第一个FilterSpec 类。

    好的,现在考虑到这一点,看看下面的代码。改编自a django snippet。代码的组织由您自行决定,请记住这应该由admin 应用程序导入。

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

    【讨论】:

    • 谢谢,我在一个项目中坚持使用 Django 1.2,这真的很有帮助。两个注意事项: 1)上面的“userprofile__”位是从用户配置文件类的模型名称中派生出来的。我的个人资料类称为个人资料,所以在我的情况下,我想要“profile__country__name”——我花了一分钟才弄清楚。 2) 对于一般情况,您还需要覆盖 ModelAdmin 的 lookup_allowed 方法,以防止在查找关系时出现 SuspiciousOperation 异常。 hoboes.com/Mimsy/hacks/… 在这里帮忙。
    【解决方案2】:

    Django 1.3 修复了它。您现在可以跨越 list_filter 中的关系

    https://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter

    【讨论】:

      猜你喜欢
      • 2013-10-11
      • 1970-01-01
      • 2012-03-30
      • 2012-04-06
      • 2018-06-25
      • 2011-10-17
      • 2015-01-08
      • 2016-06-02
      • 1970-01-01
      相关资源
      最近更新 更多