Ниже представлен последний выпуск серии исследований классовых основ представлений (CBV) Django, где мы подробно рассматриваем, как эффективно использовать ListView
для реализации таких часто используемых функций, как пагинация, поиск и сортировка. Если в предыдущих 7 выпусках мы рассмотрели основы CBV и использование Mixin, то в этом выпуске мы обобщим это и посмотрим, как чисто и эффективно справляться с общими требованиями, с которыми мы сталкиваемся в реальной веб-разработке.
Вы можете найти ссылку на предыдущий выпуск ниже!
“Расширяйте Django ListView для реализации мощных функций списков и улучшайте пользовательский опыт!”
1. ListView: больше возможностей
ListView
— это очень полезное обобщенное представление для отображения списков данных. Однако в реальных веб-приложениях, помимо простого отображения списка, необходимо реализовать функции пагинации для эффективного управления большими объемами данных, поиск для быстрого нахождения нужной информации и сортировку по предпочтениям пользователя.
В этом разделе мы рассмотрим, как расширить ListView
и элегантно реализовать эти практические требования, используя преимущества CBV.
2. Применение пагинации (Pagination)
Отображение большого объема данных за один раз негативно сказывается на пользовательском опыте. Пагинация помогает разбить данные на несколько страниц, снижая нагрузку на загрузку и упрощая поиск необходимой информации.
2.1 Настройка базовой пагинации
ListView
предоставляет встроенную функцию пагинации. Вам просто нужно установить свойство paginate_by
в классе представления.
Пример views.py
from django.views.generic import ListView
from .models import Article
class ArticleListView(ListView):
model = Article
template_name = 'articles/article_list.html'
context_object_name = 'articles'
paginate_by = 10 # Отображение 10 объектов Article на одной странице
paginate_by = 10
: при таком установкеListView
автоматически разбивает результаты на страницы по 10 объектов и передает объекты, связанные с пагинацией (paginator
,page_obj
,is_paginated
), в контекст шаблона.
2.2 Реализация UI пагинации в шаблоне
В шаблоне можно использовать page_obj
из контекста для отображения ссылок на номера страниц и реализации кнопок предыдущей/следующей страницы.
Пример articles/article_list.html
<ul>
{% for article in articles %}
<li>{{ article.title }} - {{ article.created_at }}</li>
{% empty %}
<li>Пока нет опубликованных статей.</li>
{% endfor %}
</ul>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">« В начало</a>
<a href="?page={{ page_obj.previous_page_number }}">Предыдущая</a>
{% endif %}
<span class="current">
Страница {{ page_obj.number }} / {{ paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}&search_term={{ search_form.search_term.value|default:'' }}">Следующая</a>
<a href="?page={{ paginator.num_pages }}&search_term={{ search_form.search_term.value|default:'' }}">Последняя »</a>
{% endif %}
</span>
</div>
page_obj
: объект, содержащий данные текущей страницы. Предоставляет свойства, такие какhas_previous
,has_next
,previous_page_number
,next_page_number
,number
и др.paginator
: объект, содержащий информацию о всех страницах. Предоставляет свойства, такие какnum_pages
(общее количество страниц).
2.3 Сохранение URL-соединения
Создавая ссылки пагинации, мы можем увидеть использование параметра запроса ?page=2
. ListView
автоматически распознает этот параметр и отображает данные соответствующей страницы.
3. Добавление функции поиска (Search)
Быстрый поиск нужной информации на веб-сайте очень важен. Давайте добавим функцию поиска, расширив ListView
.
3.1 Определение формы (Form)
Сначала определим простую форму для ввода поискового запроса.
Пример forms.py
from django import forms
class ArticleSearchForm(forms.Form):
search_term = forms.CharField(label='Поисковый запрос', required=False)
3.2 Изменение ListView
Мы переопределим метод get_queryset()
у ListView
для реализации функции поиска.
Пример views.py
from django.views.generic import ListView
from django.db.models import Q
from .models import Article
from .forms import ArticleSearchForm
class ArticleListView(ListView):
model = Article
template_name = 'articles/article_list.html'
context_object_name = 'articles'
paginate_by = 10
def get_queryset(self):
queryset = super().get_queryset()
self.form = ArticleSearchForm(self.request.GET) # Получение данных формы из GET-запроса
if self.form.is_valid():
search_term = self.form.cleaned_data.get('search_term')
if search_term:
# Фильтрация Article, включающего поисковый запрос в заголовок или в содержимое
queryset = queryset.filter(
Q(title__icontains=search_term) | Q(content__icontains=search_term)
)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['search_form'] = self.form # Передача формы в шаблон
return context
get_queryset()
: Переопределите этот метод, чтобы изменить основной набор данных.- Создайте экземпляр формы и, если она проходит проверку, извлеките поисковый запрос из
cleaned_data
. - Используя
Q объект
, фильтруйте объектыArticle
, включающие поисковый запрос в заголовок (title
) или в содержимое (content
) (__icontains
проверяет наличие без учета регистра). get_context_data()
: добавьте экземпляр формы в контекст, чтобы отобразить форму в шаблоне.
3.3 Отображение формы поиска и сохранение результатов в шаблоне
Добавим форму поиска в шаблон и настроим URL, чтобы можно было осуществлять пагинацию, сохраняя поисковые результаты.
Пример articles/article_list.html
<form method="get">
{{ search_form }}
<button type="submit">Поиск</button>
</form>
<ul>
{% for article in articles %}
<li>{{ article.title }} - {{ article.created_at }}</li>
{% empty %}
<li>Результатов поиска не найдено.</li>
{% endfor %}
</ul>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1&search_term={{ search_form.search_term.value|default:'' }}">« В начало</a>
<a href="?page={{ page_obj.previous_page_number }}&search_term={{ search_form.search_term.value|default:'' }}">Предыдущая</a>
{% endif %}
<span class="current">
Страница {{ page_obj.number }} / {{ paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}&search_term={{ search_form.search_term.value|default:'' }}">Следующая</a>
<a href="?page={{ paginator.num_pages }}&search_term={{ search_form.search_term.value|default:'' }}">Последняя »</a>
{% endif %}
</span>
</div>
- Установите метод формы на
get
, чтобы передать поисковый запрос как параметр URL. - При создании ссылки на страницу включите текущий поисковый запрос (
search_form.search_term.value
) как параметр URL, чтобы пагинация осуществлялась с сохранением поисковых результатов. Используйте фильтр|default:''
, чтобы использовать пустую строку, если поискового запроса нет.
4. Добавление функции сортировки (Ordering)
Давайте добавим функцию сортировки, чтобы пользователи могли видеть данные в соответствии с выбранными критериями.
4.1 Добавление формы для выбора сортировки (по желанию)
Можно также добавить всплывающее меню формы для выбора критерия сортировки. Либо можно обойтись только URL-параметрами. Здесь мы объясним, как использовать URL-параметры.
4.2 Изменение ListView
Модифицируем метод get_queryset()
, чтобы применить order_by()
.
Пример views.py (с функцией поиска)
from django.views.generic import ListView
from django.db.models import Q
from .models import Article
from .forms import ArticleSearchForm
class ArticleListView(ListView):
model = Article
template_name = 'articles/article_list.html'
context_object_name = 'articles'
paginate_by = 10
def get_queryset(self):
queryset = super().get_queryset()
self.form = ArticleSearchForm(self.request.GET)
ordering = self.request.GET.get('ordering', '-created_at') # Сортировка по умолчанию: по последним
if self.form.is_valid():
search_term = self.form.cleaned_data.get('search_term')
if search_term:
queryset = queryset.filter(
Q(title__icontains=search_term) | Q(content__icontains=search_term)
)
queryset = queryset.order_by(ordering)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['search_form'] = self.form
context['ordering'] = self.request.GET.get('ordering', '-created_at') # Передача текущего критерия сортировки в шаблон
return context
self.request.GET.get('ordering', '-created_at')
: получение значения параметраordering
из URL-запроса. Если значения нет, по умолчанию используется-created_at
(по последним).queryset = queryset.order_by(ordering)
: сортировка набора данных по полученному значениюordering
. Если перед значением-
, сортировка будет обратной.
4.3 Предоставление ссылок сортировки в шаблоне
В шаблоне предоставьте ссылки для сортировки по каждому полю.
Пример articles/article_list.html
<form method="get">
{{ search_form }}
<button type="submit">Поиск</button>
</form>
<table>
<thead>
<tr>
<th><a href="?ordering=title&search_term={{ search_form.search_term.value|default:'' }}">Заголовок</a></th>
<th><a href="?ordering=-created_at&search_term={{ search_form.search_term.value|default:'' }}">Дата создания</a></th>
</tr>
</thead>
<tbody>
{% for article in articles %}
<tr>
<td>{{ article.title }}</td>
<td>{{ article.created_at }}</td>
</tr>
{% empty %}
<tr><td colspan="2">Результатов поиска не найдено.</td></tr>
{% endfor %}
</tbody>
</table>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1&search_term={{ search_form.search_term.value|default:'' }}&ordering={{ ordering }}">« В начало</a>
<a href="?page={{ page_obj.previous_page_number }}&search_term={{ search_form.search_term.value|default:'' }}&ordering={{ ordering }}">Предыдущая</a>
{% endif %}
<span class="current">
Страница {{ page_obj.number }} / {{ paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}&search_term={{ search_form.search_term.value|default:'' }}&ordering={{ ordering }}">Следующая</a>
<a href="?page={{ paginator.num_pages }}&search_term={{ search_form.search_term.value|default:'' }}&ordering={{ ordering }}">Последняя »</a>
{% endif %}
</span>
</div>
- Для каждого заголовка таблицы предоставьте ссылки на критерий сортировки, включая параметр
ordering
. - При создании ссылок на страницы не забывайте включать как текущий поисковый запрос, так и текущий критерий сортировки (
ordering
) в параметры URL, чтобы пагинация происходила с сохранением состояния сортировки.
5. Объединение функций с помощью Mixin
Используя ранее изученные Mixin, можно более эффективно управлять такими функциями, как пагинация, поиск и сортировка. Например, можно отделить функцию поиска в Mixin.
Пример mixins.py
from django.db.models import Q
from .forms import ArticleSearchForm
class ArticleSearchMixin:
def get_queryset(self):
queryset = super().get_queryset()
self.form = ArticleSearchForm(self.request.GET)
if self.form.is_valid():
search_term = self.form.cleaned_data.get('search_term')
if search_term:
queryset = queryset.filter(
Q(title__icontains=search_term) | Q(content__icontains=search_term)
)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['search_form'] = getattr(self, 'form', ArticleSearchForm()) # Используется стандартная форма, если формы нет
return context
Пример views.py (с применением Mixin)
from django.views.generic import ListView
from .models import Article
from .mixins import ArticleSearchMixin
class ArticleListView(ArticleSearchMixin, ListView):
model = Article
template_name = 'articles/article_list.html'
context_object_name = 'articles'
paginate_by = 10
def get_queryset(self):
queryset = super().get_queryset()
ordering = self.request.GET.get('ordering', '-created_at')
queryset = queryset.order_by(ordering)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['ordering'] = self.request.GET.get('ordering', '-created_at')
return context
ArticleSearchMixin
создан для отделения логики поиска.ArticleListView
наследует какArticleSearchMixin
, так иListView
, объединяя функции поиска и представления списков.
6. Преимущества и ограничения CBV (Завершение серии)
В этой серии из 8 выпусков мы рассмотрели причины перехода от функций-основ представления (FBV) к классовым основам представления (CBV), основные структуры и методы использования CBV, использование различных обобщенных представлений и расширение функций с помощью Mixin.
Основные преимущества CBV:
- Переиспользование кода: С помощью обобщенных представлений и Mixin можно сократить количество повторяющегося кода и добиться более эффективной разработки.
- Структурированный код: Код, основанный на классах, четко разделяет и упрощает управление логикой.
- Расширяемость: Наследование и использование Mixin позволяют легко расширять существующие функции и добавлять новые возможности.
- Поддерживаемость: Код модульный, что облегчает его поддержку и минимизирует влияние изменений на весь код.
- Увеличение производительности разработки: Использование различных обобщенных представлений и Mixin, предоставляемых Django, позволяет быстро разрабатывать приложения.
Ограничения и аспекты, которые следует учитывать в CBV:
- Кривая обучения на начальном уровне: Понимание структуры классов может потребоваться для изучения CBV по сравнению с FBV.
- Чрезмерное наследование: Использование слишком большого количества Mixin может сделать код трудным для понимания. Важно использовать Mixin в разумных пределах.
- Перегрузка при простых логиках: Для очень простых страниц или функций FBV могут быть более лаконичными. Нужно выбирать подходящий способ представления в зависимости от ситуации.
В заключение, CBV Django является мощным и полезным инструментом для разработки веб-приложений, требующих сложных и различных функций. Понимание и правильное использование преимуществ CBV позволит вам писать более эффективный и удобный для поддержки код.
Спасибо, что читали серию исследований классовых основ представлений (CBV)! Надеюсь, эта серия помогла вам понять CBV Django и применить его в реальных проектах.
Посмотрите предыдущие статьи
- Классовая основа представлений (CBV) Исследовательская серия #1 – Причины перехода от FBV к CBV и подход разработчика
- Классовая основа представлений (CBV) Исследовательская серия #2 – Понимание базовых классов представлений Django
- Классовая основа представлений (CBV) Исследовательская серия #3 – Упрощенная обработка форм с помощью FormView
- Классовая основа представлений (CBV) Исследовательская серия #4 – Использование ListView и DetailView
- Классовая основа представлений (CBV) Исследовательская серия #5 – Реализация CRUD с помощью CreateView, UpdateView и DeleteView
- Классовая основа представлений (CBV) Исследовательская серия #6 – Использование TemplateView и RedirectView
- Классовая основа представлений (CBV) Исследовательская серия #7 – Использование Mixin и управление правами
“Расширите функции списков, чтобы сделать их более полными и удобными для пользователя, и
повышайте уровень своих навыков разработки Django, овладевая CBV!”
Комментариев нет.