Курс по Django: https://stepik.org/a/183363
Архив проекта: 56_sitewomen.zip
На этом занятии
мы с вами вынесем общий код классов представлений в отдельный класс, который
можно воспринимать как миксин (mixin). Те из вас, кто хорошо знаком с ООП
уже знают, что такое миксины и для чего они служат. Вкратце, я напомню идею этого
паттерна на простом примере.
Предположим, у
нас имеется класс Furniture (мебель). Тогда мы можем определить его
функциональность в зависимости от набора базовых классов – миксинов. Например,
ножки (LegsMixin) и квадратная
крышка (CoverRectMixin) будут
определять квадратный стол; ножки и круглая крышка (CoverRoundMixin) определяют
круглый стол; сочетание ножек, квадратной крышки и спинки (BackMixin) дают стул. Это
наглядная идея классов миксинов, которые своими различными сочетаниями
описывают функционал дочерних классов.
В разных языках
программирования миксины реализуются по-разному. В частности, в Python, благодаря
наличию механизма множественного наследования, примеси можно добавлять в виде
отдельного базового класса:
Например, наши
классы представлений можно дополнительно унаследовать от класса DataMixin, который будет
отвечать за наполнение шаблонов стандартной информацией. Благодаря этому мы
сможем сократить дублирование кода в тексте программы.
Начнем с класса WomenHome. Классы миксинов
принято записывать первыми в списке наследования, поэтому получим такую запись:
class WomenHome(DataMixin, ListView):
...
Это объясняется
тем, что в Python, класс,
записанный первым, первым и обрабатывается. Поэтому данные базового класса DataMixin
не будут переопределять какие-либо атрибуты следующего класса ListView.
Давайте, теперь объявим
новый класс DataMixin. Где это лучше сделать? Обычно в Django все
дополнительные, вспомогательные классы определяют в отдельном файле utils.py текущего
приложения. Мы так и поступим. Создадим этот файл и в нем запишем класс DataMixin,
следующим образом:
menu = [{'title': "О сайте", 'url_name': 'about'},
{'title': "Добавить статью", 'url_name': 'add_page'},
{'title': "Обратная связь", 'url_name': 'contact'},
{'title': "Войти", 'url_name': 'login'}
]
class DataMixin:
def get_mixin_context(self, context, **kwargs):
context['menu'] = menu
context['cat_selected'] = None
context.update(kwargs)
return context
Обратите
внимание, я перенес сюда и главное меню, т.к. оно используется напрямую классом
DataMixin.
Итак, что же
делает класс DataMixin? В нем объявлен
вспомогательный метод get_mixin_context() для
формирования контекста шаблона по умолчанию. Также, при необходимости, мы можем
передавать ему именованные аргументы, которые также будут помещаться в
контекст. Благодаря этому методу, нам не придется в классах представлений
каждый раз прописывать ссылки на главное меню.
Итак, класс
миксин объявлен. Осталось в классе WomenHome изменить метод get_context_data(), следующим
образом:
def get_context_data(self, *, object_list=None, **kwargs):
return self.get_mixin_context(super().get_context_data(**kwargs),
title='Главная страница',
cat_selected=0,
)
Мы здесь
вызываем метод get_mixin_context() класса DataMixin,
указав, дополнительно параметр title и cat_selected. Получаем новый
словарь со всеми стандартными ключами и объединяем его со словарем context. После этого он
возвращается.
По аналогии,
меняем и все остальные классы представлений, где используется вызов get_context_data():
class ShowPost(DataMixin, DetailView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return self.get_mixin_context(context, title=context['post'])
class WomenCategory(DataMixin, ListView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cat = context['posts'][0].cat
return self.get_mixin_context(context,
title='Категория - ' + cat.name,
cat_selected=cat.id,
)
class TagPostList(DataMixin, ListView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tag = TagsPost.objects.get(slug=self.kwargs['tag_slug'])
return self.get_mixin_context(context, title='Тег: ' + tag.tag)
Все, переходим
на сайт и видим, что страницы отображаются также как и ранее, но теперь все
работает совместно с классом DataMixin.
Можно пойти еще
дальше и оптимизировать с помощью этого же класса DataMixin определение
словаря extra_context. Для этого в классе DataMixin мы вначале
пропишем следующие строчки:
class DataMixin:
title_page = None
extra_context = {}
def __init__(self):
if self.title_page:
self.extra_context['title'] = self.title_page
if 'menu' not in self.extra_context:
self.extra_context['menu'] = menu
def get_mixin_context(self, context, **kwargs):
if self.title_page:
context['title'] = self.title_page
context['menu'] = menu
context['cat_selected'] = None
context.update(kwargs)
return context
Здесь вводится
специальный атрибут title_page, который может
содержать заголовок страницы, а также пустой словарь extra_context. Ниже записан
инициализатор для формирования в словаре extra_context ключа menu и ключа title, если атрибут title_page не равен None, то есть,
определен в дочернем классе. Перейдем теперь в класс AddPage и модифицируем его
следующим образом:
class AddPage(DataMixin, CreateView):
model = Women
fields = ['title', 'slug', 'content', 'is_published', 'cat']
# form_class = AddPostForm
template_name = 'women/addpage.html'
success_url = reverse_lazy('home')
title_page = 'Добавление статьи'
Смотрите, мы
теперь можем пользоваться атрибутом title_page, введенным в
классе DataMixin. Содержимое
этого атрибута автоматически добавится в словарь extra_context и будет передано
в шаблон через параметр title. Все стало удобнее и нагляднее. При
этом атрибут extra_context по-прежнему можно прописывать в классе AddPage и добавлять
любые дополнительные данные в шаблон, то есть, мы нашим классом DataMixin никак не
нарушаем стандартную логику работы фреймворка Django.
Давайте внесем
аналогичные изменения в другие классы, которые используют словарь extra_context:
class UpdatePage(DataMixin, UpdateView):
...
title_page = 'Редактирование статьи'
Вот пример того,
как миксины в Django позволяют
устранять дублирование кода и добавлять новый функционал в классы представления.
Курс по Django: https://stepik.org/a/183363