Курс по Django: https://stepik.org/a/183363
Архив проекта: 52_sitewomen.zip
На предыдущем
занятии мы с вами заменили функцию index() на класс
представления WomenHome, который
наследовался от класса TemplateView. Однако можно
сделать еще лучше и использовать другой базовый класс ListView созданный
специально для отображения произвольных списков:
https://docs.djangoproject.com/en/4.2/ref/class-based-views/
Как мы помним, у
нас на главной странице как раз отображается список статей.
Вначале
импортируем класс ListView:
from django.views.generic import ListView
И укажем его в
качестве базового для класса WomenHome:
class WomenHome(ListView):
...
Если сейчас
запустить тестовый веб-сервер и перейти на главную страницу, то увидим ошибку.
Она связана с тем, что класс ListView предполагает получение данных из
таблиц БД. И для этого (в самом простом варианте) в классе WomenHome нужно
определить специальный атрибут:
class WomenHome(ListView):
model = Women
(Все остальные
строчки класса WomenHome я поставил в комментарии.)
Фактически, строчка
model = Women выбирает все
записи из таблицы women и попытается отобразить их в виде
списка, используя шаблон с именем:
<имя
приложения>/<имя модели>_list.html
Именно эту
ошибку «TemplateDoesNotExist» мы видим, при обновлении главной страницы, так
как фреймворк не находит шаблон по умолчанию:
women/women_list.html
Конечно, мы
могли бы создать такой шаблон, но у нас уже есть свой собственный для этих
целей – women/index.html. Чтобы его указать в классе представлений,
используется атрибут template_name, которому
присваиваем путь к нужному шаблону:
class WomenHome(ListView):
model = Women
template_name = 'women/index.html'
Если теперь
обновить главную страницу, то увидим пустой список так, словно у нас нет ни
одной статьи. Почему это произошло? Дело в том, что мы в шаблоне обращаемся к переменным
со своими именами, которые определили в функции представления index. Например, post содержал список
всех записей. Спрашивается, что же теперь нужно прописать вместо post? По умолчанию,
данные из модели Women, указанной в классе представлений,
помещаются в коллекцию object_list. Если мы ее запишем вместо post, то должно все
заработать. И, действительно, обновляя главную страницу, видим список всех
постов.
Если в шаблоне
вместо object_list мы хотим использовать другое обозначение (имя), то в классе WomenHome следует
прописать атрибут context_object_name с указанием
другого имени переменной:
context_object_name = 'posts'
И, далее, в
шаблоне index.html снова можем
писать posts.
Осталось в index.html передать остальные
данные. Для этого воспользуемся атрибутом extra_context:
class WomenHome(ListView):
model = Women
template_name = 'women/index.html'
context_object_name = 'posts'
extra_context = {
'title': 'Главная страница',
'menu': menu,
# 'posts': Women.published.all().select_related('cat'),
'cat_selected': 0,
}
Я напомню, что с
помощью атрибута extra_context мы можем
передавать в шаблон только те данные, которые можно сформировать в момент
определения самого класса. Если же данные зависят от параметров GET-запроса, то для
этого следует переопределять метод get_context_data(). Для класса ListView
он
имеет следующий вид:
class WomenHome(ListView):
model = Women
template_name = 'women/index.html'
context_object_name = 'posts'
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Главная страница'
context['menu'] = menu
context['cat_selected'] = 0
return context
Здесь
дополнительно определен параметр object_list.
Все, главная
страница приобрела почти тот же самый вид. Почему почти? Потому что строчка model = Women, как я уже
говорил, выбирает все записи из таблицы women, а мы это
делали с помощью команды:
Women.published.all().select_related('cat')
Чтобы это
поправить вместо атрибута model следует переопределить специальный
метод get_queryset() следующим
образом (атрибут model следует удалить из класса):
def get_queryset(self):
return Women.published.all().select_related('cat')
Вот так,
переопределяя метод get_queryset(), можно задавать свой алгоритм
выборки записей из модели.
На первый взгляд
может показаться, что мы особо не сократили программный код. Это действительно
так. Объясняется очень просто. У нас с вами была достаточно простая реализация в
функции представления index() и, как следствие, не имеем ничего
нового в классе представления WomenHome. Но дальше мы увидим, как,
например, пагинация (разбивка на страницы) может быть легко реализована в
классе буквально несколькими дополнительными атрибутами. Вообще, преимущества
классов представлений перед функциями все больше и больше проявляются с ростом
сложности проекта. Это происходит за счет большей модульности программного кода
(благодаря классам и механизму наследования) и вынесения типовых решений в
базовые классы. Эти моменты я постараюсь вам продемонстрировать в рамках этого
курса.
Создаем класс представлений для категорий
Итак, мы с вами
создали класс представления для главной страницы. Давайте повторим этот процесс
и пропишем аналогичный класс для отдельных категорий. Делается это очень
просто. Сначала объявим класс WomenCategory с тем же базовым классом ListView,
указав те же самые атрибуты и методы:
class WomenCategory(ListView):
template_name = 'women/index.html'
context_object_name = 'posts'
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
cat = context['posts'][0].cat
context['title'] = 'Категория - ' + cat.name
context['menu'] = menu
context['cat_selected'] = cat.id
return context
def get_queryset(self):
return Women.published.filter(cat__slug=self.kwargs['cat_slug']).select_related('cat')
Обратите
внимание, как записана выборка записей. Здесь первым параметром указано имя cat__slug – это способ
обращения к слагу таблицы category через объект cat модели Women. Далее,
указываем, что поле slug у категории должно быть равно параметру cat_slug, который мы
берем из словаря kwargs объекта класса WomenCategory. Ключ cat_slug автоматически
формируется по шаблону маршрута (файл women/urls.py), в котором мы
должны вместо функции указать класс представления:
path('category/<slug:cat_slug>/', WomenCategory.as_view(), name='category'),
В принципе, это
все. Если теперь перейти на страницу сайта и выбирать категории, то будут
отображаться статьи выбранной рубрики. Но, если указать несуществующий слаг, то
увидим пустую страницу, а нам бы хотелось увидеть ошибку 404 – страница не
найдена. Для этого в классе WomenCategory достаточно
указать атрибут:
который
указывает генерировать исключение 404 если список статей пуст. Так мы сохраняем
общий функционал нашего сайта.
В качестве
самостоятельного задания сделайте замену функции show_tag_postlist() классом
представления с именем TagPostList, унаследованным
от базового класса ListView.
Курс по Django: https://stepik.org/a/183363