Django-администратор: list_filter приписывают от UserProfile

Я хочу позволить администраторам моего сайта фильтровать пользователей из определенной страны на Администраторском Сайте. Таким образом, естественная вещь сделать была бы чем-то вроде этого:

#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, который это приводит к следующей ошибке:

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

Как я обхожу это ограничение?

7
задан tback 12 February 2010 в 12:48
поделиться

1 ответ

То, что вы ищете, - это настраиваемые параметры фильтра для администратора . Плохая новость в том, что поддержка для них, возможно, не появится в ближайшее время (вы можете отслеживать обсуждение здесь ).

Однако ценой грязного взлома вы можете обойти это ограничение. Некоторые основные моменты построения FilterSpec перед погружением в код:

  • При построении списка FilterSpec для отображения на странице Django использует список полей, которые вы предоставили в list_filter
  • Эти поля должны быть реальными полями модели, а не обратными отношениями или настраиваемыми свойствами.
  • Django поддерживает список классов FilterSpec , каждый из которых связан с функцией test .
  • Для каждого поля в list_filter Django будет использовать первый класс FilterSpec , для которого функция test возвращает True для поля. .

Хорошо, теперь, имея это в виду, взглянем на следующий код. Он адаптирован из фрагмента кода django . Организация кода оставлена ​​на ваше усмотрение, только имейте в виду, что это должно быть импортировано приложением 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 ).

10
ответ дан 6 December 2019 в 14:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: