Фильтрация по категориям и поиск по ключевым словам

В этой главе вы научитесь обрабатывать параметры URL-запроса, реализовывать навигацию с фильтрацией по категориям и функциональность поиска по ключевым словам.


Параметры URL-запроса

Параметры запроса — это часть URL после вопросительного знака, например/?category=django&q=入门。

В функциях представления Django используйтеrequest.GETПолучите эти параметры.

Пример

def index(request):
    # request.GET — объект, подобный словарю
    # .get('key', 'Default value') безопасно получает параметр и возвращает значение по умолчанию, если он не существует
    category_slug = request.GET.get('category', '')   # Фильтр по категории
    keyword = request.GET.get('q', '')                 # Ключевые слова для поиска
    print(f'Category: {category_slug}, Search: {keyword}')

Цепочная фильтрация ORM и Q-объекты

Фильтрация и поиск требуют динамического формирования условий запроса.

Пример

# Путь к файлу: blog/views.py, обновите функцию index
from django.shortcuts import render
from django.db.models import Q           # Q-объекты используются для объединения сложных условий запроса.
from .models import Post, Category

def index(request):
    """Главная страница блога: отображение списка статей, поддержка фильтрации по категориям + поиска по ключевым словам"""
    posts = Post.objects.all().order_by('-created_at')

    # 1. Получить параметры запроса
    category_slug = request.GET.get('category', '')
    keyword = request.GET.get('q', '')

    # 2. Фильтрация по категории
    if category_slug:
        # category__slug: доступ к полю slug таблицы категорий через внешний ключ (двойное подчёркивание)
        posts = posts.filter(category__slug=category_slug)

    # 3. Поиск по ключевому слову (заголовок или краткое содержание)
    if keyword:
        # Q(title__icontains=...) | Q(summary__icontains=...): комбинация OR
        # icontains: запрос на вхождение без учёта регистра
        posts = posts.filter(
            Q(title__icontains=keyword) | Q(summary__icontains=keyword)
        )

    # 4. Получить все категории (для отображения навигации фильтра)
    categories = Category.objects.all()

    context = {
        'posts': posts,
        'categories': categories,
        'category_slug': category_slug,   # Передаётся в шаблон для подсветки текущего выбора
        'keyword': keyword,               # Вывести поисковые ключевые слова
        'title': 'EXAMPLE Blog - Главная',
    }
    return render(request, 'blog/index.html', context)

Объяснение методов запросов ORM

МетодыЗначениеПример
field__exactТочное совпадениеtitle__exact='Django'
field__iexactТочное совпадение без учёта регистраtitle__iexact='django'
field__containsСодержит (с учётом регистра)title__contains='入门'
field__icontainsСодержит (без учёта регистра)title__icontains='django'
field__gte / lteБольше или равно / Меньше или равноcreated_at__gte='2024-01-01'
field__in=[...]В спискеcategory__in=[1, 2, 3]

Двойное подчёркивание__является основным соглашением Django ORM: используется для доступа к полям связанных моделей или указания, как запрашивать поля. Напримерcategory__slugобозначает переход по внешнему ключу к полю slug таблицы Category.


Обновите шаблон главной страницы

Добавьте навигацию с фильтрацией по категориям и поле поиска над списком статей.

Пример

<!-- Путь к файлу: blog/templates/blog/index.html -->
{% extends 'blog/base.html' %}

{% block title %}{{ title }}{% endblock %}

{% block content %}
<h2 class="section-title">Последние статьи</h2>

<!-- Поле поиска -->
<div class="search-bar">
    <!-- Форма method="get": после отправки параметр появляется в URL (/search/?q=xxx) -->
    <form method="get" action="{% url 'index' %}">
        <input
           type="text"
           name="q"
           value="{{ keyword }}"
           placeholder=Поиск по заголовкам или описаниям статей...
           class="search-input"
       />
        {% if keyword %}
        <a href="{% url 'index' %}" class="clear-btn">✕</a>
        {% endif %}
    </форма>
</div>

<!-- Навигация фильтра категорий -->
<div class="category-bar">
    <!-- Кнопка 'Все': без параметра категории -->
    <a href="{% url 'index' %}"
      class="{% if not category_slug %}active{% endif %}">Все</a>

    {% for cat in categories %}
    <a href="{% url 'index' %}?category={{ cat.slug }}{% if keyword %}&q={{ keyword }}{% endif %}"
      class="{% if category_slug == cat.slug %}active{% endif %}">
        {{ cat.name }}
    </a>
    {% endfor %}
</div>

<!-- Статистика результатов фильтра -->
<p class="result-info">
Всего {{ posts.count }} записей
{% if keyword %}, поиск "{{ keyword }}"{% endif %}
</p>

{% if posts %}
    <div class="article-grid">
        {% for post in posts %}
        <a href="{% url 'post_detail' post.pk %}" class="card-link">
            <div class="article-card">
                <div class="card-content">
                    <span class="card-category">{{ post.category.name }}</span>
                    <h3>{{ post.title }}</h3>
                    <p>{{ post.summary|truncatechars:80 }}</p>
                    <span class="card-date">{{ post.created_at|date:"Y-m-d" }}</span>
                </div>
            </div>
        </a>
        {% endfor %}
    </div>
{% else %}
    <p class="empty-tip">Подходящих статей не найдено</p>
{% endif %}
{% endblock %}

Стили навигации фильтра и поля поиска

Пример

/* Добавьте к стилю в base.html */
.search-bar {
    margin-bottom: 20px;
}

.search-input {
    width: 100%;
    padding: 12px 16px;
    border: 2px solid #eee;
    border-radius: 8px;
    font-size: 15px;
    outline: none;
    transition: border-color 0.2s;
}

.search-input:focus {
    border-color: #2c3e50;
}

.clear-btn {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: #999;
    text-decoration: none;
    font-size: 18px;
}

.category-bar {
    display: flex;
    gap: 10px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.category-bar a {
    padding: 6px 16px;
    border: 1px solid #ddd;
    border-radius: 20px;
    text-decoration: none;
    color: #333;
    font-size: 14px;
    transition: all 0.2s;
}

.category-bar a.active {
    background: #2c3e50;
    color: #fff;
    border-color: #2c3e50;
}

.category-bar a:hover {
    border-color: #2c3e50;
}

.result-info {
    color: #999;
    font-size: 14px;
    margin-bottom: 16px;
}

.card-link {
    text-decoration: none;
    color: inherit;
}

Ссылки на кнопках категорий также сохраняютcategoryиkeywordПараметры:?category={{ cat.slug }}&q={{ keyword }}. Таким образом, когда пользователи сначала выполняют поиск, а затем нажимают категорию, условие поиска не теряется, и оба условия действуют одновременно.


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

Запустите сервер и проверьте следующие пункты:

  • Нажмите кнопку категории, чтобы отображались только статьи соответствующей категории.
  • Введите ключевые слова, чтобы фильтровать заголовки и краткие описания в реальном времени.
  • Совмещение категории и поиска: отфильтрованные результаты удовлетворяют обоим условиям одновременно
  • Кнопка "Все" сбрасывает фильтр категории и восстанавливает отображение всех статей.

Итоги главы

В этой главе вы освоили методы обработки параметров URL-запроса в Django: использование request.GET.get() для получения параметров, цепочную фильтрацию ORM + объекты Q для реализации сложных запросов и динамическое объединение параметров URL в шаблонах для реализации ссылок фильтрации.

Фильтрация по категориям и функция поиска позволяют пользователям быстро находить интересующие статьи.

Другие расширения