Перейти к содержимому

Что такое mixin в django

  • автор:

Миксины

и сделаем чтобы необходимые классы наследовали наш новый класс MyMixin :

Затем в классе HomeNews добавим параметр:

А также в функцию get_context_data добавим новую переменную контекста:

Тем самым мы сделаем переменную mixin_prop доступной в шаблоне.

Ограничение доступа на страницы только для авторизованных пользователей

Миксины возможно применять только для классов-контроллеров. Для функций-контроллеров используется специальный декоратор @login_required

Для того, чтобы убрать какой-либо элемент из шаблона для неавторизированных пользователей необходимо использовать параметр request.user.is_authenticated :

Но данная ссылка все равно будет доступна для любого пользователя перешедшего по ней напрямую. Чтобы это исправить, необходимо:

Импортируем мпециальный миксин в файле views.py

Затем добавляем данный класс LoginRequiredMixin в наш класс выввода который нужно закрыть для неавторизованных пользователей:

Причем очень важно чтобы данный класс наследовался первым в списке наследуемых.

Класс LoginRequiredMixin может делать с неавторизованным пользователем две вещи:

Mixins in Django and Django Rest Framework

This is a general post about what mixins are. Mixins are a design pattern found in many languages. In Python, though the language does not support mixins natively,they are implemented using Python’s multiple inheritence model.

The mixins are different from inheritence. Inheritence is useful when you want to narrow a scope in the inherited class. Mixins fill in the gap when multiple orthogonal features have to be combined together. There is no narrowing of scope, hence traditional inheritence is not useful.

Mixins paradigm:

  • mixins are classes that generally inherit from object (unless you are django core developer)
  • mixins are narrow in scope as in they have single responsibility. They do one thing and do it really well.
  • mixins provide plug-in functionality
  • although mixins work through inheritence, they DONT create a subtyping relation. This means that mixins do not adhere to Liskoff principle from SOLID. Liskoff Principle: Instances of base class can be replaced with instances of derived classes and it will make sense. This statement does not hold for mixins. That’s why we say that mixins are not creating a subtyping relation.
  • mixins are not meant to be extended : class SubMixin(BaseMixin) : DON’T DO THIS. Although Django core library is littered with things like class PermissionRequiredMixin(AccessMixin): , make it a point to not do this in code you write. Python multiple inheritence makes it a nightmare to figureout method resolution order and all sorts of subtle issues might be caused. If there are loops in method resolution order, Python will give a compilation error. If you accidentally wrote, class ComposedView(AccessMixin, PermissionRequiredMixin, BaseView) might cause MRO loops and hence compilation error because order matters.
  • mixins are not meant to be instantiated

Examples

Mixins are really good at doing modular designs. Since Python supports multiple inheritence, this is totally fine:
class ComposedView(Mixin1, BaseView)
Read this as Mixin1 extends BaseView which is then extended by ComposedView.

class ComposedView(Mixin1,Mixin2, BaseView)
ORDER MATTERS. Keep the BaseView at the root of inheritance tree and each of the other mixins extend or add ONE specific part of the functionality. More specifically, Mixin2 will add to whatever Mixin1 and BaseView have added (can even overwrite if not careful). Take precaution that when chaining mixins, different mixins don’t end up editing the same base state or else they will all overwrite eachother’s stuff. This is a care that is paramount when using mixins. YOU NEED TO BE AWARE OF EXACTLY WHAT EACH MIXIN ADDS SO THAT ONE MIXIN DOES NOT OVERWRITE ANOTHER MIXIN’S MODIFICATIONS.

Here we we have a mixin to check if a user is logged in. This mixin is written with the assumption that it will be extending a baseview which will have a dispatch method with exactly that signature. We choose to extend the dispatch method as that is one of the first methods hit in the request-response lifecycle.

Notice the super method at the end. In the scenario that the user is genuinely logged in, this super method launches the dispatch of the baseview that got extended.

Another more practical example. Lets take TemplateView whose sole purpose is to show a given html template. Just by adding the mixin, this TemplateView can only be accessed by logged in user.

The AboutView inherits from both LoginRequiredMixin and TemplateView.

Now if you wanted to add this login requirement to DetailView instead of a TemplateView, class AboutView(LoginRequiredMixin, TemplateView): becomes class AboutView(LoginRequiredMixin, DetailView):

That’s it. Your DetailView will not need anymore code change to implement the login functionality.

Writing mixins for Django Class Based Views

Mixins are a natural fit for class based views just like decorators are a natural fit for function based views.

To write mixins for a class based view, it is often good to know a little about the view lifecycle. Lets go through some lifecycle methods in order:

  1. mixins override dispatch if they need to check if user if logged in, has permission. This is one of the first methods called and view determines which request method (get, post etc) is used.
  2. mixins override get_context_data() to add new data to context. This method passes keyword arguments to the template context. So this is a way to add extra context to the views.
  3. mixins override get_template_names(). This is a method called by render_to_response and this method lists all template names.

Note: render_to_response is the original version for which there are many shortcuts like render .

So overriding get_template_names() in a mixin gives more flexibility in how to identify template names, in case your Django project has a specific way.

A VERY NICE PLACE TO SEE ALL DJANGO CLASS BASED VIEWS: http://ccbv.co.uk/

django-braces

django-braces is a collection of frequently used django mixins and they are divided in three categories.

  1. Access Mixins
  2. Form Mixins
  3. Other Mixins

Decorators

Very common in function based views are login_required(), user_passes_test(), permission_required()

In class based views, you get their equivalent mixins. LoginRequiredMixin, UserPassesTestMixin, PermissionRequiredMixin

Anti patterns in mixin use in Django

Beware of incompatible mixins, chaining too many mixins, overdoing mixins otherwise it will it will be a nightmare to figure out the execution flow. Same as we advise against 100 lines view functions and tons of decorators on functions, we also advise against scenarios like if somethin requires 6 mixins to work, you are probably doing something wrong and placing too much logic into your view layer.

Chaining about 3 mixing is OK. Like chaining PermissionRequiredMixin and LoginRequiredMixin is very common because they both work with dispatch but do not interfere with eachother. One deals with authorization, another with authentication. Maybe you can add another mixin to work on the get_context_data(). But the more mixins you add, the more logic gets added to view layer and it becomes harder to understand the logic and execution flow. 4 mixins is considered absolute pushing it.

Множественное наследование и миксины в Python

Недавно я пересмотрел три своих старых статьи о представлениях на основе классов Django (class-based views), которые написал для своего блога, обновив их до Django 3.0 (вы можете найти их здесь), и еще раз обнаружил, большое количество кода использующего классы mixin для улучшения повторного использования кода. По своему опыту я понял, что миксины не очень популярны в Python, поэтому решил изучить их лучше, тем самым освежив свои знания теории ООП.

Чтобы полностью оценить содержание поста, убедитесь, что вы усвоили два столпа ООП: делегирование, в частности, как оно реализуется посредством наследования, и полиморфизм. Этот пост подробнее расскажет о делегировании а этот пост о полиморфизме.

Множестенное наследование: благодать и проклятье

Общая концепция

Чтобы обсудить миксины, мы должны начать с одного из самых спорных вопросов в мире ООП: множественное наследование. Это естественное расширение концепции простого наследования, когда класс автоматически делегирует разрешенные метода и атрибуты другому классу (от родительского классу к дочернему).

Позвольте мне заявить еще раз, поскольку это важно для остальной части обсуждения: наследование – это просто механизм автоматического делегирования.

Делегирование было введено в ООП как способ уменьшить дублирование кода. Когда объекту нужна определенная функция, он просто делегирует ее другому классу (явно или неявно), поэтому код пишется только один раз.

Давайте рассмотрим пример сайта управления кодом (совершенно вымышленный и не вдохновленный каким-либо существующим продуктом). Давайте предположим, что мы создали следующую иерархию

которая позволяет нам помещать в pull request только определенный код, требуемый этим элементом. Вызовы методов и делегирование – это не что иное, как сообщения между объектами, поэтому иерархия делегирования – это простая сетевая система.

К сожалению, использование наследования вместо композиции часто приводит к системам, которые, как это ни парадоксально, увеличивают дублирование кода. Основная проблема заключается в том, что наследование может напрямую делегироваться только одному классу (родительскому классу), в отличие от композиции, где объект может делегировать любому количеству других классов. Это ограничение наследования означает, что у нас может быть класс, который наследует от другого, потому что ему нужны некоторые его функции, но при этом так же получает все другие функции, которые он не хочет или не должен иметь.

Давайте продолжим пример портала управления кодом и рассмотрим проблему, которая представляет собой элемент, который мы хотим сохранить в системе, но который пользователь не может просмотреть. Если мы создадим иерархию, как это

в итоге мы помещаем функции, связанные с процессом проверки, в объект, который не должен иметь их. Стандартное решение этой проблемы состоит в том, чтобы увеличить глубину иерархии наследования и получать их от нового более простого предка.

Тем не менее, этот подход перестает быть жизнеспособным, как только объект должен наследовать от данного класса, но не от родителя этого класса. Например, элемент, который должен быть рецензируемым, но не назначаемым, например, best practice, которую мы хотим добавить на сайт. Если мы хотим продолжать использовать наследование, единственным решением на этом этапе является дублирование кода, который реализует рецензируемую природу элемента (или код, который реализует назначаемую функцию) и создание двух разных иерархий классов.

Обратите внимание, что это даже не принимает во внимание, что новый reviewable item может нуждаться в атрибутах assignable item, что требует другого уровня глубины в иерархии, где мы выделяем эти функции в более общем классе. Так что, к сожалению, есть вероятность, что это только первый из многих компромиссов, которые нам придется принять, чтобы сохранить систему в стабильном состоянии, если мы не сможем изменить наш подход.

Затем в ООП было введено множественное наследование, поскольку было ясно, что объект может захотеть делегировать определенные действия данному классу, а другие действия другому, имитируя, объекты из реальной жизни, когда мы наследуем признаки от нескольких предков (родителей, бабушек и дедушек) , и т.д.).

Вышеупомянутая ситуация может быть затем решена с помощью pull request наследования как от класса, который предоставляет функции assign, так и от класса, который реализует функции reviewable.

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

Это всего лишь примеры, которые могут быть действительными или нет, в зависимости от конкретного случая, но они ясно показывают проблемы, которые могут возникнуть у нас даже при очень простой иерархии из 4 классов. Многие из этих проблем явно возникают из-за того, что мы хотели реализовать делегирование только посредством наследования, и я осмелюсь сказать, что 80% архитектурных ошибок в проектах ООП происходят из-за использования наследования вместо композиции и использования God объектов(божественных объектов), то есть классов, которые несут ответственность за слишком много различных частей системы. Всегда помните, что ООП родился с идеей взаимодействия небольших объектов посредством сообщений, поэтому соображения, которые мы учитываем для монолитных архитектур, верны и здесь.

Тем не менее, поскольку наследование и композиция реализуют два разных типа делегирования (быть и иметь), они оба ценны, и множественное наследование – это способ снять ограничение единственного поставщика, которое возникает из-за наличия только одного родительского класса.

Почему это так противоречиво?

Учитывая то, что я только что сказал, множественное наследование кажется благословением. Когда объект может наследоваться от нескольких родителей, мы можем легко распределять обязанности между различными классами и использовать только те, которые нам нужны, способствуя повторному использованию кода и избегая “божественных” объектов.

К сожалению, все не так просто. Прежде всего, мы сталкиваемся с проблемой, с которой сталкивается каждая микросервисно-ориентированная архитектура, то есть с риском перехода от “божественных” объектов (экстремальная монолитная архитектура) к почти пустым объектам (экстремально распределенный подход), обременяя программиста слишком тонким детальным контролем, который в конечном итоге приводит к созданию системы, в которой отношения между объектами настолько сложны, что становится невозможным понять влияние изменений в коде.

Однако в множественном наследовании существует более насущная проблема. Как это происходит с естественным наследованием, родители могут предоставить одну и ту же «генетическую черту» в двух разных вариантах, но у получающегося в результате этого человека будет только одна. Оставляя в стороне генетику (которая невероятно сложнее, чем программирование) и возвращаясь к ООП, мы сталкиваемся с проблемой, когда объект наследуется от двух других объектов, которые предоставляют тот же атрибут.

Итак, если ваш класс Child наследует от родителей Parent1 и Parent2, и оба предоставляют метод __init__, какой из них следует использовать вашему объекту?

Ситуация может ухудшиться, так как родители могут иметь разные входные параметры общего метода, например

Проблема может быть расширена еще дальше, введя общего предка выше Parent1 и Parent2.

Как видите, у нас уже есть проблема, когда мы представляем нескольких родителей, а общий предок просто добавляет новый уровень сложности. Класс предка может быть явно в любой точке дерева наследования (дедушка, бабушка и дедушка и т. д.), Важной частью является то, что он является общим для Parent1 и Parent2. Это так называемая проблема алмаза, так как граф наследования имеет форму ромба

Таким образом, в то время как при наследовании с одним родителем правила просты, с множественным наследованием у нас сразу возникает более сложная ситуация, в которой нет тривиального решения. Помогает ли все это реализовать множественное наследование?

Как мы вскоре увидим, есть решения этой проблемы, но этот дополнительный уровень сложности делает множественное наследование чем-то, что не очень легко вписывается в дизайн. Помните, что наследование – это механизм автоматического делегирования. По этим причинам множественное наследование часто изображается как пугающее и запутанное, и, как правило, ему уделяется время только в продвинутых курсах ООП, по крайней мере, в мире Python. Я считаю, что каждый программист на Python должен ознакомиться с ним и узнать, как им воспользоваться.

Множественное наследование: путь Python

Посмотрим, как можно решить алмазную проблему. В отличие от генетики, мы, программисты, не можем позволить себе какой-либо уровень неопределенности или случайности в наших процессах, поэтому при наличии возможной неоднозначности, как та, которая создается множественным наследованием, нам необходимо записать правило, которое будет строго соблюдаться в каждом случае. В Python это правило называется MRO (Method Resolution Order – Порядок разрешения методов), которое было введено в Python 2.3 и описано в этом документе Микеле Симионато.

Можно многое сказать о MRO и лежащем в основе алгоритме линеаризации C3, но для целей этого поста достаточно посмотреть, как он решает проблему алмазов. В случае множественного наследования Python следует обычным правилам наследования (автоматическое делегирование предку, если атрибут не присутствует локально), но порядок следования по дереву наследования теперь включает все классы, указанные в сигнатуре класса. В приведенном выше примере Python будет искать атрибуты в следующем порядке: Child, Parent1, Parent2, Ancestor.

Таким образом, как и в случае стандартного наследования, это означает, что первый класс в списке, который реализует определенный атрибут, будет выбранным поставщиком для этого разрешения. Пример может прояснить вопрос

В этом случае экземпляр c Child обеспечит rewind, open, close и flush. Когда вызывается c.rewind, выполняется код в Ancestor, так как это первый класс в списке MRO, который предоставляет этот метод. Метод open предоставляется Parent1, а close – Parent2. Если вызывается метод c.flush, код предоставляется самим классом Child, который переопределяет его, переопределяя класс, предоставленный Parent2.

Как мы видим в методе flush, Python не меняет своего поведения, когда речь идет о методе, переопределенной несколькими родителями. Первая реализация метода с таким именем выполняется, и реализация родителя не вызывается автоматически. Как и в случае стандартного наследования, мы должны определять классы с соответствующими сигнатурами методов.

Под капотом

Как множественное наследование работает внутри? Как Python создает список MRO?

У Python очень простой подход к ООП (более подробнее см. Здесь). Классы сами являются объектами, поэтому они содержат структуры данных, которые используются языком для предоставления функций, и делегирование не исключение. Когда мы запускаем метод для объекта, Python по умолчанию используется метод __getattribute__ (предоставляемый object), который использует __class__ для доступа к классу из экземпляра и __bases__ для поиска родительских классов. Последний, в частности, является кортежем, поэтому он упорядочен и содержит все классы, от которых наследуется текущий класс.

MRO создается с использованием только __bases__, но основной алгоритм не так уж простой и связан с монотонностью линеаризации результирующего класса. Но на самом деле это не так страшно, как кажется, но, скорее всего не то, что вы хотели бы прочитать во время отдыха. Но если это так, то вышеупомянутый документ Микеле Симионато содержит все подробности о линеаризации классов, которые вы всегда хотели исследовать, к примеру лежа на пляже.

Наследование и интерфейсы

Чтобы приблизиться к миксинам, нам нужно подробно обсудить наследование и, в частности, роль сигнатур методов.

В Python, когда вы переопределяете метод, предоставленный классом-предком, вы должны решить, когда и как вызывать его начальную реализацию. Это дает программисту свободу решать, нужно ли им просто дополнить метод или полностью заменить его. Помните, что единственное, что делает Python, когда класс наследует от другого, – это автоматически делегирует методы, которые не были реализованы в наследнике.

Когда класс наследует от другого, мы в идеале создаем объекты, которые сохраняют обратную совместимость с интерфейсом родительского класса, чтобы позволить их полиморфное использование. Это означает, что когда мы наследуем класс и переопределяем метод, изменяя его сигнатуру, мы делаем что-то опасное и, по крайней мере, с точки зрения полиморфизма. Посмотрите на этот пример

Обратите внимание, что Square меняет подпись __init__ и resize. Теперь, когда мы создаем экземпляры этих классов, нам нужно помнить что в Square другая подпись __init__

Обычно мы признаем, что расширенная версия класса принимает больше параметров при инициализации, так как мы не ожидаем, что она будет полиморфной в __init__. Проблемы возникают, когда мы пытаемся использовать полиморфизм в других методах, например, изменяя размер всех объектов GraphicalEntity в списке

Поскольку r1, r2 и q1 – все объекты, которые наследуются от GraphicalEntity, мы ожидаем, что они предоставят интерфейс, предоставленный этим классом, но это не удастся, потому что Square изменил сигнатуру resize. То же самое произойдет, если мы создадим их в цикле for из списка классов, но, как я уже сказал, общепринято, что дочерние классы изменяют сигнатуру метода __init__. Это не так, например, в системе на основе плагинов, где все плагины должны быть инициализированы одинаково.

Это классическая проблема в ООП. Хотя мы, как люди, воспринимаем квадрат просто как немного особенный прямоугольник, с точки зрения интерфейса эти два класса различны, и поэтому мы не должны находиться в одном и том же дереве наследования, когда имеем дело с измерениями. Это важное соображение: Rectangle и Square полиморфны в методе move, но не в __init__ и resize. Итак, вопрос в том, можем ли мы как-то разделить две природы movable и resizable.

Теперь для обсуждения интерфейсов, полиморфизма и причин, лежащих в их основе, потребуется совершенно отдельный пост, поэтому в следующих разделах я собираюсь игнорировать этот вопрос и просто считать интерфейс объекта необязательным. Таким образом, вы найдете примеры объектов, которые нарушают интерфейс родителя, и объектов, которые его хранят. Просто помните: всякий раз, когда вы изменяете сигнатуру метода, вы изменяете (неявный) интерфейс объекта, и таким образом вы останавливаете полиморфизм. Я буду обсуждать в другой раз, если я считаю это правильным или неправильным.

Миксины

MRO – это хорошее решение, которое предотвращает двусмысленность, но оставляет программистам ответственность за создание разумных деревьев наследования. Алгоритм помогает разрешать сложные ситуации, но это не значит, что мы должны их создавать. Итак, как мы можем использовать множественное наследование, не создавая системы, которые слишком сложны для понимания? Кроме того, возможно ли использовать множественное наследование для решения проблемы управления двойной (или множественной) природой объекта, как в предыдущем примере с movable и resizable формой?

Решение исходит из смешанных классов: это небольшие классы, которые предоставляют атрибуты, но не включены в стандартное дерево наследования, работая скорее как «дополнения» к текущему классу, чем как настоящие предки. Миксины происходят от языка программирования LISP, а именно от того, что можно считать первой версией Common Lisp Object System, расширением Flavors. Современные языки ООП реализуют миксины разными способами: например, в Scala есть функции, называемые traits, которые живут в своем собственном пространстве с определенной иерархией, и которые не мешают правильному наследованию классов.

Классы миксинов в Python

Python не поддерживает миксины с какой-либо выделенной функцией языка, поэтому мы используем множественное наследование для их реализации. Это явно требует большой дисциплины от программиста, поскольку это нарушает одно из основных допущений для миксинов: их ортогональность к дереву наследования. В Python так называемые миксины – это классы, которые живут в обычном дереве наследования, но они остаются небольшими, чтобы избежать создания иерархий, которые слишком сложны для понимания программистом. В частности, миксины не должны иметь общих предков, кроме объекта, с другими родительскими классами.

Давайте посмотрим на простой пример

Здесь класс ResizableMixin наследуется не от GraphicalEntity, а непосредственно от object, поэтому ResizableGraphicalEntity получает от него только метод resize. Как мы уже говорили, это упрощает дерево наследования ResizableGraphicalEntity и помогает снизить риск проблемы алмаза. Это позволяет нам свободно использовать GraphicalEntity в качестве родителя для других классов без необходимости наследовать методы, которые нам не нужны. Пожалуйста, помните, что это происходит потому, что классы предназначены для того, чтобы этого избежать, а не из-за особенностей языка: алгоритм MRO просто гарантирует, что всегда будет однозначный выбор в случае нескольких предков.

Mixins обычно не могут быть слишком общими. В конце концов, они предназначены для добавления определенных функций в классы, но эти новые функции часто взаимодействуют с другими уже существующими функциями расширенного класса. В этом случае метод resize взаимодействует с атрибутами size_x и size_y, которые должны присутствовать в объекте. Очевидно, что есть примеры чистых mixins, но, поскольку они не требуют инициализации, их область действия определенно ограничена.

Использование миксинов для взламывания наследования

Благодаря MRO программисты Python могут использовать множественное наследование для переопределения методов, которые объекты наследуют от своих родителей, что позволяет им создавать классы без дублирования кода. Давайте посмотрим на этот пример

Как вы можете видеть, класс Button расширяет класс GraphicalEntity классическим способом, используя super для вызова родительского метода __init__ перед добавлением нового атрибута состояния status. Теперь, если я хочу создать класс SquareButton, у меня есть два варианта.

Я мог бы просто переопределить __init__ в новом классе

который выполняет запрошенное задание, но тесно связывает особенность наличия одного измерения с Button. Если бы мы хотели создать круглое изображение, мы не могли бы наследовать от SquareButton, так как изображение имеет другую природу.

Второй вариант – это выделение объектов, связанных с наличием одного измерения в классе mixin, и добавление его в качестве родительского для нового класса.

Второе решение дает тот же конечный результат, но способствует повторному использованию кода, поскольку теперь класс SingleDimensionMixin можно применять к другим классам, производным от GraphicalEntity, и заставлять их принимать только один размер, тогда как в первом решении эта функция была тесно связана с предком Button класс.

Обратите внимание, что позиция миксина важна. Поскольку super следует MRO, вызываемый метод отправляется в ближайший класс линеаризации. Если вы поместите SingleDimensionMixin после Button в определении SquareButton, Python выдаст ошибку. В этом случае вызов b = SquareButton (10, 20, 200) и сигнатура метода __init__ (self, pos_x, pos_y, size_x, size_y) не будут совпадать.

Тем не менее, миксины используются не только тогда, когда вы хотите изменить интерфейс объекта. Используя super, мы можем достичь интересных конструкций, таких как

Здесь LimitSizeButton вызывает __init__ своего первого родителя, который является Button. Это, однако, делегирует вызов следующему классу в MRO перед инициализацией self.status, поэтому вызов отправляется LimitSizeMixin, который сначала выполняет некоторые изменения и в конечном итоге отправляет его исходному получателю, GraphicalEntity.

Помните, что в Python вы никогда не будете вынуждены вызывать родительскую реализацию метода, поэтому миксин здесь также может остановить механизм диспетчеризации, если это является требованием бизнес-логики нового объекта.

Реальный пример: представления на основе классов Django

Наконец, давайте перейдем к первоначальному источнику вдохновения для этого поста: кодовой базе Django. Я покажу вам, как программисты Django использовали множественные классы наследования и миксины для содействия повторному использованию кода, и теперь вы, надеюсь, поймете все причины, стоящие за ними.

Выбранный мной пример можно найти в коде универсальных представлений(generic views), в частности в двух классах: TemplateResponseMixin и TemplateView.

Как вы, возможно, знаете, класс Django View является прародителем всех представлений на основе классов и предоставляет метод диспетчеризации dispatch, который преобразует методы HTTP-запросов в вызовы функций Python (CODE). Теперь TemplateView – это представление, которое отвечает на запрос GET, отображающий шаблон с данными, поступающими из контекста, передаваемого при вызове представления. Учитывая механизм, лежащий в основе представлений Django, TemplateView должен реализовать метод get и вернуть содержимое HTTP-ответа. Код класса

Как вы можете видеть, TemplateView – это View, но он использует два миксина для добавления функций. Давайте посмотрим на TemplateResponseMixin

[Я удалил код класса, так как он не имеет решающего значения для настоящего обсуждения, вы можете увидеть полный класс здесь]

Понятно, что TemplateResponseMixin просто добавляет к любому классу два метода get_template_names и render_to_response. Последний вызывается в методе get TemplateView для создания ответа. Давайте посмотрим на упрощенную схему вызовов:

Это может показаться сложным, но попробуйте следовать коду пару раз, и вся картина начнет обретать смысл. Важно подчеркнуть, что код в TemplateResponseMixin доступен для любого класса, который хочет иметь функцию рендеринга шаблона, например DetailView (CODE), который получает возможность показа деталей одного объекта с помощью SingleObjectTemplateResponseMixin. , который наследуется от TemplateResponseMixin, переопределяя его метод get_template_names (CODE).

Как мы уже говорили, миксины не могут быть слишком общими, и здесь мы видим хороший пример миксина, предназначенного для работы с конкретными классами. TemplateResponseMixin должен применяться к классам, которые содержат self.request (CODE), и хотя это не означает исключительно классы, производные от View, ясно, что он был разработан для дополнения этого конкретного типа.

Выводы

  • Наследование предназначено для повторного использования кода, но может привести к противоположному результату.
  • Множественное наследование позволяет нам сохранять дерево наследования простым
  • Множественное наследование приводит к возможным проблемам, которые решаются в Python через MRO
  • Интерфейсы (неявные или явные) должны быть частью вашего дизайна
  • Смешанные классы используются для добавления простых изменений в классы.
  • Миксины реализованы в Python с использованием множественного наследования: они обладают большой выразительной силой, но требуют тщательного проектирования.

Заключение

Я надеюсь, что этот пост помог вам понять немного больше, как работает множественное наследование, и меньше быть напуганным этим. Я также надеюсь, что мне удалось показать вам, что классы должны быть тщательно спроектированы, и что при создании системы классов нужно учитывать многое. Еще раз, пожалуйста, помните что композиция, это мощный и часто забываемый инструмент.

Обратная связь

Не стесняйтесь связаться со мной в Twitter, если у вас есть вопросы. Страница вопросов GitHub – лучшее место для отправки исправлений.

django-rest-framework Mixins

The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as .get() and .post() , directly. This allows for more flexible composition of behavior. -Official Django rest Framework Documentation-

[Introductory] List of Mixins And Usage on Views/Viewsets

List of available mixins:

  • ListModelMixin: provides a .list() method to the view/viewset
  • RetrieveModelMixin: provides a .retrieve() method to the view/viewset
  • CreateModelMixin: provides a .create() method to the view/viewset
  • UpdateModelMixin: provides a .update() method to the view/viewset
  • DestroyModelMixin: provides a .destroy() method to the view/viewset

We can mix and match the mixins in our generic views and viewsets, in order to give them the corresponding utility that we need:

An API view with .list() .create() and .destroy() methods?
Inherit from the GenericAPIView and combine the appropriate mixins:

A viewset with only a .list() and .update() methods?
Inherit from the GenericViewSet and add the appropriate mixins:

Yes, it was that easy!!

To conclude, we can mix and match every mixin we need and utilize it to customize our views and their methods in any possible combination!

[Intermediate] Creating Custom Mixins

How to:

To define a custom mixin we just need to create a class inheriting from object .

Let’s assume that we want to define two separate views for a model named MyModel . Those views will share the same queryset and the same serializer_class . We will save ourselves some code repetition and we will put the above in a single mixin to be inherited by our views:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *