Пользовательский фильтр в Django Admin на Django 1.3 или ниже - PullRequest
73 голосов
/ 14 июня 2009

Как я могу добавить пользовательский фильтр для администратора django (фильтры, которые отображаются в правой части панели модели)? Я знаю, что легко включить фильтр, основанный на поле этой модели, но как насчет «вычисляемого» поля, такого как:

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's 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??

Ответы [ 8 ]

57 голосов
/ 18 августа 2009

Спасибо 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 и импортировать его в модель, в которую вы хотите добавить фильтр

22 голосов
/ 11 августа 2009

вы должны написать собственный FilterSpec (нигде не документированный). Посмотрите здесь пример:

http://www.djangosnippets.org/snippets/1051/

9 голосов
/ 15 июня 2011

В текущей версии разработки django есть поддержка пользовательских фильтров: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter

3 голосов
/ 18 августа 2014

Не оптимальный способ (с точки зрения использования процессора), но простой и будет работать, поэтому я делаю это так (для моей небольшой базы данных). Моя версия 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 __ () .

3 голосов
/ 19 декабря 2013

Просто sidenote: Вы можете легко использовать галочки по умолчанию в админке 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
3 голосов
/ 14 июня 2009

Вы не можете, к сожалению. В настоящее время неполевые элементы нельзя использовать в качестве записей list_filter.

Обратите внимание, что ваш административный класс не работал бы, даже если бы это было поле, поскольку кортежу из одного элемента нужна запятая: ('is_live',)

2 голосов
/ 10 октября 2011

Пользователь поставляет товары в некоторые страны бесплатно. Я хотел отфильтровать эти страны:

Все - все страны, Да - почтовые расходы отсутствуют, Нет - почтовые расходы оплачиваются.

Основной ответ на этот вопрос у меня не сработал (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 мы поставляем дикты. используется для построения строк HTTP-запроса, таких как ?postage__exact=0 для каждого из возможных фильтров. Фильтры Я думаю, являются кумулятивными, поэтому, если был предыдущий запрос «Нет», а теперь у нас есть запрос «Да», мы должны удалить Запрос «Нет». self.removes указывает, что нужно удалять для каждого запроса. Метод choices создает строки запроса, сообщает, какой фильтр был выбран, и устанавливает отображаемое имя фильтра.

1 голос
/ 10 февраля 2017

Вот ответ, и пользовательский фильтр реализован настолько просто, насколько это возможно.

Фильтр диапазона дат администратора Django

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...