Пользовательский фильтр в Django Admin на Django 1.3 или ниже
Как добавить пользовательский фильтр в django admin (фильтры, которые отображаются в правой части панели инструментов модели)? Я знаю, что легко включить фильтр, основанный на поле этой модели, но как насчет "вычисленного" поля следующим образом:
class NewsItem(models.Model):
headline = models.CharField(max_length=4096, blank=False)
byline_1 = models.CharField(max_length=4096, blank=True)
dateline = models.DateTimeField(help_text=_("date/time that appears on article"))
body_copy = models.TextField(blank=False)
when_to_publish = models.DateTimeField(verbose_name="When to publish", blank=True, null=True)
# HOW CAN I HAVE "is_live" as part of the admin filter? It a calculated state!!
def is_live(self):
if self.when_to_publish is not None:
if ( self.when_to_publish < datetime.now() ):
return """ <img alt="True" src="/media/img/admin/icon-yes.gif"/> """
else:
return """ <img alt="False" src="/media/img/admin/icon-no.gif"/> """
is_live.allow_tags = True
class NewsItemAdmin(admin.ModelAdmin):
form = NewsItemAdminForm
list_display = ('headline', 'id', 'is_live')
list_filter = ('is_live') # how can i make this work??
Ответы
Ответ 1
Спасибо gpilotino за то, что он дал мне толчок в правильном направлении для реализации этого.
Я заметил, что код вопроса использует дату-время, чтобы выяснить, когда он живет. Поэтому я использовал DateFieldFilterSpec и подклассифицировал его.
from django.db import models
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec,DateFieldFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext as _
from datetime import datetime
class IsLiveFilterSpec(DateFieldFilterSpec):
"""
Adds filtering by future and previous values in the admin
filter sidebar. Set the is_live_filter filter in the model field attribute
'is_live_filter'. my_model_field.is_live_filter = True
"""
def __init__(self, f, request, params, model, model_admin):
super(IsLiveFilterSpec, self).__init__(f, request, params, model,
model_admin)
today = datetime.now()
self.links = (
(_('Any'), {}),
(_('Yes'), {'%s__lte' % self.field.name: str(today),
}),
(_('No'), {'%s__gte' % self.field.name: str(today),
}),
)
def title(self):
return "Is Live"
# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'is_live_filter', False),
IsLiveFilterSpec))
Для использования вы можете поместить вышеуказанный код в файл filters.py и импортировать его в модель, которую вы хотите добавить в фильтр
Ответ 2
вам нужно написать собственный фильтр FilterSpec (нигде не документированный).
Вот пример:
http://www.djangosnippets.org/snippets/1051/
Ответ 3
В текущей версии разработки django есть поддержка пользовательских фильтров: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
Ответ 4
Вы не можете, к сожалению. В настоящее время элементы, не относящиеся к полю, не могут использоваться как записи list_filter.
Обратите внимание, что ваш класс администратора не работал бы, даже если это поле, поскольку кортеж с одним элементом нуждается в запятой: ('is_live',)
Ответ 5
Просто сидение: вы можете использовать галочки deafult в администраторе Django более легко:
def is_live(self):
if self.when_to_publish is not None:
if ( self.when_to_publish < datetime.now() ):
return True
else:
return False
is_live.boolean = True
Ответ 6
Не оптимальный способ (по ЦП), но простой и будет работать, поэтому я делаю это так (для моей небольшой базы данных). Моя версия Django - 1.6.
В admin.py:
class IsLiveFilter(admin.SimpleListFilter):
title = 'Live'
parameter_name = 'islive'
def lookups(self, request, model_admin):
return (
('1', 'islive'),
)
def queryset(self, request, queryset):
if self.value():
array = []
for element in queryset:
if element.is_live.__call__() == True:
q_array.append(element.id)
return queryset.filter(pk__in=q_array)
...
class NewsItemAdmin(admin.ModelAdmin):
form = NewsItemAdminForm
list_display = ('headline', 'id', 'is_live')
list_filter = (IsLiveFilter)
Основная идея здесь - получить доступ к настраиваемым полям в QuerySet с помощью функции __ call __().
Ответ 7
Пользователь поставляет товары в некоторые страны бесплатно. Я хотел отфильтровать эти страны:
Все - все страны, Да - бесплатная доставка по расписанию, Нет - платная почтовая оплата.
Главный ответ для этого вопроса не работал у меня (Django 1.3). Я думаю, потому что в __init__
не было параметра field_path
. Также он подклассифицирован DateFieldFilterSpec
. Поле postage
- это FloatField
from django.contrib.admin.filterspecs import FilterSpec
class IsFreePostage(FilterSpec):
def __init__(self, f, request, params, model, model_admin, field_path=None):
super(IsFreePostage, self).__init__(f, request, params, model,
model_admin, field_path)
self.removes = {
'Yes': ['postage__gt'],
'No': ['postage__exact'],
'All': ['postage__exact', 'postage__gt'] }
self.links = (
('All', {}),
('Yes', {'postage__exact': 0}),
('No', {'postage__gt': 0}))
if request.GET.has_key('postage__exact'):
self.ttl = 'Yes'
elif request.GET.has_key('postage__gt'):
self.ttl = 'No'
else:
self.ttl = 'All'
def choices(self, cl):
for title, param_dict in self.links:
yield {'selected': title == self.ttl,
'query_string': cl.get_query_string(param_dict,
self.removes[title]),
'display': title}
def title(self):
return 'Free Postage'
FilterSpec.filter_specs.insert(0,
(lambda f: getattr(f, 'free_postage', False), IsFreePostage))
В self.links мы поставляем dicts. используется для построения строк HTTP-запросов, таких как ?postage__exact=0
для каждого из возможных фильтров. Фильтры, я думаю, являются кумулятивными, поэтому, если был предыдущий запрос "Нет", и теперь у нас есть запрос "Да", мы должны удалить
Запрос "Нет". self.removes
указывает, что нужно удалить для каждого запроса. Метод choices
строит строки запроса, указывает, какой фильтр был выбран, и задает отображаемое имя фильтра.
Ответ 8
Вот ответ и как можно более простой пользовательский фильтр, это может помочь
Фильтр диапазона дат Django