Курс по Django: https://stepik.org/a/183363
Архив проекта: 51_sitewomen.zip
Во всех
предыдущих занятиях мы с вами использовали функции представления. Это довольно
простой подход и используется, в основном, когда нужно реализовать простую
логику обработки запросов. Однако во фреймворке Django помимо функций
можно использовать и классы представлений (представления, основанные на
классах):
CBV – Class-Based Views
https://docs.djangoproject.com/en/4.2/ref/class-based-views/
Чаще всего
именно их применяют на практике, так как объектно-ориентированный подход,
зачастую, позволяет заметно упростить код, сделать его более понятным и
читабельным.
Все классы
представлений в Django наследуются от базового класса View, а дочерние
специализируются на различных задачах обработки и формирования результата. Все
их мы с вами последовательно разберем на последующих занятиях, а начнем с
базового класса View.
Если перейти по
ссылке:
https://docs.djangoproject.com/en/4.2/topics/class-based-views/intro/
то можно
подробно почитать о порядке использования классов представлений, в том числе и
класса View. Мы видим, что
он поддерживает два метода get() и post() для обработки
GET и POST-запросов
соответственно. Давайте с этого и начнем – с внедрения класса View в наш проект.
У нас имеется
подходящая функция представления addpage(), которая содержит обработку GET и POST-запросов.
Определим после нее класс представления с именем AddPage, унаследованный
от класса View:
class AddPage(View):
def get(self, request):
pass
def post(self, request):
pass
В этом классе мы
объявили два метода get() и post(), которые будут срабатывать при GET и POST-запросах
соответственно. Каждый метод должен иметь дополнительный параметр request с данными
запроса. Сам же базовый класс View необходимо
импортировать из следующей ветки:
from django.views import View
Если перейти
внутрь этого класса, то можно увидеть список из всех поддерживаемых им методов.
А также специальный метод as_view(), который
используется для привязки этого класса к маршрутам URL-адресов.
Но для начала мы
с вами определим реализации методов get() и post(). Для этого
достаточно скопировать соответствующие строчки из функции addpage(). Получим
следующую реализацию класса:
class AddPage(View):
def get(self, request):
form = AddPostForm()
return render(request, 'women/addpage.html', {'menu': menu, 'title': 'Добавление статьи', 'form': form})
def post(self, request):
form = AddPostForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('home')
return render(request, 'women/addpage.html', {'menu': menu, 'title': 'Добавление статьи', 'form': form})
То есть, по
методу get() мы просто
отдаем пустую форму, а по методу post() создаем форму с принятыми данными,
если данные корректны, то сохраняем их в БД и делаем перенаправление на главную
страницу, иначе, возвращаем форму с ранее введенными значениями и списком
ошибок ее заполнения.
Как видите, все
достаточно очевидно и просто. Всю логику по вызову нужных методов (get, post и др.) базовый
класс View берет на себя.
Нам остается только разнести код по этим методам и связать класс AddPage с маршрутом
добавления новой статьи. Для этого перейдем в файл women/urls.py и в списке urlpatterns
для маршрута addpage пропишем класс AddPage:
urlpatterns = [
path('', views.index, name='home'),
path('about/', views.about, name='about'),
path('addpage/', views.AddPage.as_view(), name='add_page'),
...
]
Все, мы с вами
только что заменили функцию представления addpage() на класс
представления AddPage. После запуска тестового
веб-сервера и перехода на страницу:
http://127.0.0.1:8000/addpage/
видим
срабатывание метода get() и отображение пустой формы для добавления статьи
в окне браузера. Если заполнить эту форму и выполнить отправку данных, то мы
попадаем во второй метод post(), где эти данные после проверки
сохраняются в БД.
Вот минимальный
функционал, который предоставляет нам базовый класс View. То есть, по
сути, он нам позволяет разнести код программы по отдельным методам, что гораздо
удобнее описания всей логики в пределах одной функции.
Класс TemplateView
Во второй части
занятия покажу пример использования еще одного класса представления TemplateView. Само название
уже говорит нам, что он служит для обработки шаблонов и отправки результата
пользователю. Например, у нас есть функция index(), которая
возвращает список постов, используя шаблон index.html. Давайте
заменим эту функцию классом TemplateView. Для этого ниже
мы определим новый класс WomenHome следующим
образом:
class WomenHome(TemplateView):
template_name = 'women/index.html'
Дополнительно
должны импортировать базовый класс из ветки:
from django.views.generic import TemplateView
Обратите
внимание на специальный атрибут template_name. Он служит для
указания шаблона, который будет использоваться при обращении к данному классу.
Давайте прямо в
таком виде свяжем маршрут главной страницы с классом WomenHome и посмотрим,
что получится (в файле women/urls.py):
urlpatterns = [
path('', views.WomenHome.as_view(), name='home'),
...
]
Видим отображение
пустого шаблона без списка статей и пунктов меню. Почему так произошло?
Очевидно по той причине, что мы в шаблон не передаем никаких дополнительных данных.
Как выполнить эту передачу? Если перейти в базовый класс TemplateView, то видим здесь
реализацию метода get(), в котором формируется контекст (набор данных) с помощью
метода get_context_data() c последующей
передачей результата в шаблон. Причем этот метод определен в классе миксина ContextMixin.
И в нем мы видим дополнительный атрибут extra_context. Само название атрибута
говорит о возможности определения через него дополнительного контекста
(дополнительных данных).
Возвращаемся в
класс WomenHome и определим в
нем этот атрибут extra_context следующим образом:
class WomenHome(TemplateView):
template_name = 'women/index.html'
extra_context = {
'title': 'Главная страница',
'menu': menu,
'posts': Women.published.all().select_related('cat'),
'cat_selected': 0,
}
(Я просто
скопировал определение словаря data из функции index.) После
обновления главной страницы увидим ее в прежнем виде.
Также в методе as_view() мы можем
передавать дополнительные аргументы для классов представлений. Например,
используя именованный параметр extra_context, можно
передавать в класс любые статические данные, которые будут доступны в шаблоне.
По сути, все данные из extra_context будут
автоматически присвоены одноименному атрибуту и передаваться в шаблон. Поэтому,
строчка:
WomenHome.as_view(extra_context={'title': "Главная страница сайта"})
передает в
шаблон параметр title с указанным значением. Но я это
все-таки это уберу (было сделано исключительно в учебных целях), оставлю как
было.
Обратите еще внимание
на то, что словарь extra_context можно
использовать именно для заранее сформированных данных, которые, затем, уже
никак не меняются. Если же мы собираемся передавать данные, которые формируются
динамически в момент поступления запроса, например, менять параметр cat_selected
в зависимости от параметра cat_id GET-запроса:
http://127.0.0.1:8000/category/?cat_id=2
то через атрибут
extra_context сделать это уже
не получится. Для этого потребуется переопределять метод get_context_data базового
класса. В принципе, с помощью метода get_context_data() можно
передавать и статические и динамические данные, то есть, любую информацию в
шаблон. Поэтому нередко можно встретить использование этого метода. В нашем
примере им можно воспользоваться так:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Главная страница'
context['menu'] = menu
context['posts'] = Women.published.all().select_related('cat')
context['cat_selected'] = int(self.request.GET.get('cat_id', 0))
return context
Если теперь
попробовать поменять значения cat_id в GET-запросе, то
будем видеть подсветку различных разделов. Правда, само содержимое будет
оставаться прежним, т.к. мы этот функционал здесь не прописывали. Я лишь хотел
показать отличия между методом get_context_data() и атрибутом extra_context.
Вот так
достаточно просто можно использовать базовый класс представления View и его
специализированный дочерний класс TemplateView. Конечно, в
реальной практике эти классы применяются не часто, так как они описывают
достаточно общий функционал. На последующих занятиях мы познакомимся с более
специализированными классами и увидим преимущества от их использования.
Курс по Django: https://stepik.org/a/183363