我想允许我的网站管理员过滤管理网站上特定国家/地区的用户。所以自然要做的就是这样:
#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中处理用户及其UserProfile的方式,这会导致以下错误:
'UserAdmin.list_filter[0]' refers to field 'userprofile__country__name' that is missing from model 'User'
如何解决这个限制?
答案 0 :(得分:10)
您要找的是自定义管理员FilterSpecs 。坏消息是,对这些人的支持可能不会很快发布(你可以跟踪讨论here)。
然而,以肮脏的黑客为代价,您可以解决限制问题。在深入研究代码之前,有关如何构建FilterSpecs
的一些要点:
FilterSpec
列表时,Django会使用您在list_filter
FilterSpec
类列表,每个类与测试函数相关联。list_filter
中的每个字段,Django将使用 test 函数为该字段返回 True 的第一个FilterSpec
类。< / LI>
好的,现在考虑到这一点,请看下面的代码。它改编自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
)。
答案 1 :(得分:5)
Django 1.3修复了它。您现在可以在list_filter
中跨越关系https://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter