Панель поиска и панель фильтрации

Курс по Django: https://stepik.org/a/183363

Архив проекта: 40_sitewomen.zip

Продолжаем настраивать и изучать работу с админ-панелью. Давайте добавим у списка панель поиска. Делается это очень просто. Достаточно прописать атрибут search_fields в классе WomenAdmin и указать в списке поля, по которым следует выполнять поиск. Например, так:

@admin.register(Women)
class WomenAdmin(admin.ModelAdmin):
    list_display = ('title', 'time_create', 'is_published', 'cat', 'brief_info')
    list_display_links = ('title', )
    list_editable = ('is_published', )
    ordering = ['-time_create', 'title']
    actions = ['set_published', 'set_draft']
    search_fields = ['title']
...

После запуска веб-севера и перехода в админ-панель в списке постов модели Women мы увидим наверху панель поиска. Причем, поиск будет осуществляться только по полю title. Давайте в этом убедимся. Наберем фрагмент «ан» и увидим только те записи, в которых в заголовке присутствует фрагмент «ан», причем малыми буквами. То есть, поиск в админ-панели Django осуществляется в регистрозависимом виде. Если же набрать «Ан», то увидим только одну запись «Анджелина Джоли».

Давайте теперь попробуем здесь же сделать поиск по категориям. Для этого у нас есть внешний ключ в виде атрибута cat. Если прописать его в списке search_fields:

search_fields = ['title', 'cat']

то при обновлении страницы возникнет ошибка обработки такого объекта. Почему так произошло? Потому что здесь нужно указывать конкретные названия полей, а не просто внешний ключ. Например, название категории, определяется полем name. И мы с вами уже знаем, что в связанных таблицах через два подчеркивания можно прописывать названия полей, а также различные lookup’ы. Давайте попробуем здесь сделать то же самое:

search_fields = ['title', 'cat__name']

Теперь никаких ошибок нет и поиск работает по категориям. То есть, теперь мы можем выполнять поиск сразу по двум полям: по заголовкам и по категориям.

Мало того, как я только что сказал, у этих полей можно прописывать и lookup’ы. Например, давайте потребуем, чтобы искомый фрагмент искался с начала заголовка:

search_fields = ['title__startswith', 'cat__name']

Набираем в поиске «Дж» и видим только две записи. А если убрать этот lookup, то увидим три записи, так как «Дж» встречается еще у одной, но не с самого начала. Вот так можно настраивать и управлять процессом поиска в админ-панели Django.

Настройка панели фильтрации

Далее мы с вами добавим рядом со списком наших постов панель для их фильтрации по заданным критериям. В самом простом варианте для этого в классе WomenAdmin достаточно прописать атрибут:

list_filter = ['cat__name', 'is_published']

При обновлении страницы справа появится панель фильтрации постов по категориям и статусам публикации.

Но мы можем здесь добавить свой собственный фильтр. Например, давайте будем отбирать замужних и незамужних женщин. Для этого у нас имеется внешний ключ husband с типом связи один к одному. Он нам пригодится.

Первым делом нужно определить класс, который будет отвечать за логику работы фильтра. Определим его перед классом WomenAdmin следующим образом:

class MarriedFilter(admin.SimpleListFilter):
    title = 'Статус женщин'
    parameter_name = 'status'
    
    def lookups(self, request, model_admin):
        return [
            ('married', 'Замужем'),
            ('single', 'Не замужем'),
        ]
 
    def queryset(self, request, queryset):
        return queryset

Здесь обязательный атрибут title определяет название фильтра на панели, а обязательный атрибут parameter_name – название параметра в GET-запросе. Скоро вы увидите, как и где он фигурирует. Далее, идут два метода. Первый lookups() должен возвращать список из возможных значений параметра status и названий позиций в панели фильтра. Второй, queryset() отвечает за отбор записей для соответствующей выбранной позиции. Пока мы здесь просто возвращаем все записи.

Чтобы наш фильтр отображался на админ-панели, в списке list_filter класса WomenAdmin этот класс нужно указать (без кавычек):

list_filter = [MarriedFilter, 'cat__name', 'is_published']

Обновим админ-панель и справа видим отображение нашего фильтра. Если кликнуть по позиции «Замужем», то в браузере формируется следующий GET-запрос:

http://127.0.0.1:8000/admin/women/women/?status=married

Здесь, как раз фигурирует определенный нами параметр status и значение фильтруемой позиции married. Если кликнуть на другую позицию, то значение параметра status изменится:

http://127.0.0.1:8000/admin/women/women/?status=single

Нам осталось только воспользоваться этим параметром и в зависимости от его значения возвратить требуемые записи в методе queryset(). Это можно сделать следующим образом:

    def queryset(self, request, queryset):
        if self.value() == 'married':
            return queryset.filter(husband__isnull=False)
        elif self.value() == 'single':
            return queryset.filter(husband__isnull=True)

Мы здесь с помощью метода value() извлекаем значение параметра status и при значении 'married' выбираем только замужних женщин (у них поле husband принимает значение отличное от null). А при значении ‘single’ только незамужних.

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

Курс по Django: https://stepik.org/a/183363

Видео по теме