Фильтрация администратора Django по Null/не является Null
У меня есть простая модель в Django, как:
class Person(models.Model):
referrer = models.ForeignKey('self', null=True)
...
в ModelAdmin этой модели, как бы я позволил фильтровать его по тому, является ли referrer нулевым? По умолчанию добавление реферера в list_filter вызывает выпадающее меню, которое отображает lists запись человека, которая может быть в сотнях тысяч, эффективно предотвращая загрузку страницы. Даже если он загружается, я все равно не могу фильтровать по критериям, которые я хочу.
т. е. как бы я изменить это так что в раскрывающемся списке перечислены только варианты" все"," Null "или" не Null"?
Я видел посты которые утверждают, что выполняют что-то подобное, используя пользовательские подклассы FilterSpec, но ни один из них не объясняет, как их использовать. Те немногие, которые я видел, применяются ко всем полям во всех моделях, чего я бы не хотел. Более того, есть ноль документация для FilterSpec, что заставляет меня нервничать, потому что я не хочу инвестировать в много пользовательского кода, привязанного к некоторым переходный внутренний класс, который может исчезнуть к следующему выпуску.
6 ответов
поскольку Django 1.4 вносит некоторые изменения в фильтры, я думал, что сэкономлю кому-то время, которое я только что потратил на изменение кода из принятого ответа Cerin для работы с Django 1.4 rc1.
у меня есть модель, которая имеет TimeField (null=True) с именем "started", и я хотел фильтровать для нулевых и ненулевых значений, поэтому это prety почти такая же проблема, как OP.
Итак, вот что сработало для меня...
определенные (фактически включенные) эти в admin.py:
from django.contrib.admin.filters import SimpleListFilter
class NullFilterSpec(SimpleListFilter):
title = u''
parameter_name = u''
def lookups(self, request, model_admin):
return (
('1', _('Has value'), ),
('0', _('None'), ),
)
def queryset(self, request, queryset):
kwargs = {
'%s'%self.parameter_name : None,
}
if self.value() == '0':
return queryset.filter(**kwargs)
if self.value() == '1':
return queryset.exclude(**kwargs)
return queryset
class StartNullFilterSpec(NullFilterSpec):
title = u'Started'
parameter_name = u'started'
чем просто использовал их в ModelAdmin:
class SomeModelAdmin(admin.ModelAdmin):
list_filter = (StartNullFilterSpec, )
у меня есть более простая версия ответа frnhr, которая фактически фильтруется на __isnull
состояние.
(Django 1.4+):
from django.contrib.admin import SimpleListFilter
class NullListFilter(SimpleListFilter):
def lookups(self, request, model_admin):
return (
('1', 'Null', ),
('0', '!= Null', ),
)
def queryset(self, request, queryset):
if self.value() in ('0', '1'):
kwargs = { '{0}__isnull'.format(self.parameter_name) : self.value() == '1' }
return queryset.filter(**kwargs)
return queryset
потом еще:
class StartNullListFilter(NullListFilter):
title = u'Started'
parameter_name = u'started'
и наконец:
class SomeModelAdmin(admin.ModelAdmin):
list_filter = (StartNullListFilter, )
Я лично не люблю хлам мой admin.py
с десятками классов, поэтому я придумал такую вспомогательную функцию:
def null_filter(field, title_=None):
class NullListFieldFilter(NullListFilter):
parameter_name = field
title = title_ or parameter_name
return NullListFieldFilter
который я могу позже применить как в:
class OtherModelAdmin(admin.ModelAdmin):
list_filter = (null_filter('somefield'), null_filter('ugly_field', _('Beautiful Name')), )
фрагмент с лучшим объяснением может быть этой. Django 1.4 будет поставляться с упрощенный механизм фильтрации.
есть простой способ:
class RefererFilter(admin.SimpleListFilter):
title = 'has referer'
# Parameter for the filter that will be used in the URL query.
parameter_name = 'referer__isnull'
def lookups(self, request, model_admin):
return (
('False', 'has referer'),
('True', 'has no referer'),
)
def queryset(self, request, queryset):
if self.value() == 'False':
return queryset.filter(referer__isnull=False)
if self.value() == 'True':
return queryset.filter(referer__isnull=True)
затем просто использовал их в ModelAdmin:
class PersonAdmin(admin.ModelAdmin):
list_filter = (RefererFilter,)
там был билет подпрыгивая вокруг для этого в течение 4 лет (https://code.djangoproject.com/ticket/5833). Он пропустил веху 1.3, но достиг нового статуса функции и, по-видимому, нашел свой путь в багажник. Если вы не возражаете сбежать из багажника, вы можете использовать его сейчас. Патч предположительно совместим с 1.3, поэтому вы, вероятно, можете обойтись просто исправлением вашей текущей установки.
в итоге я использовал смесь решение проблемы здесь вместе с этот фрагмент.
однако мне пришлось немного подправить фрагмент, отбросив ограничение типа поля и добавив новый field_path, недавно добавленный в 1.3.
from django.contrib.admin.filterspecs import FilterSpec
from django.db import models
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
class NullFilterSpec(FilterSpec):
#fields = (models.CharField, models.IntegerField, models.FileField)
@classmethod
def test(cls, field):
#return field.null and isinstance(field, cls.fields) and not field._choices
return field.null and not field._choices
#test = classmethod(test)
def __init__(self, f, request, params, model, model_admin, field_path=None):
super(NullFilterSpec, self).__init__(f, request, params, model, model_admin, field_path)
self.lookup_kwarg = '%s__isnull' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
def choices(self, cl):
# bool(v) must be False for IS NOT NULL and True for IS NULL, but can only be a string
for k, v in ((_('All'), None), (_('Has value'), ''), (_('Omitted'), '1')):
yield {
'selected' : self.lookup_val == v,
'query_string' : cl.get_query_string({self.lookup_kwarg : v}),
'display' : k
}
# 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, 'isnull_filter', False), NullFilterSpec)
)