Код на салфетке
2.22K subscribers
748 photos
15 videos
2 files
791 links
Канал для тех, кому интересно программирование на Python и не только.

Сайт: https://pressanybutton.ru/
Чат: https://t.iss.one/+Li2vbxfWo0Q4ZDk6
Заметки автора: @writeanynotes

Реклама и взаимопиар: @Murzyev1995
Сотрудничество и др.: @proDreams
Download Telegram
Хм...
😁9
Что выведет этот код? №2
🤔1
Что выведет код с картинки выше?
Anonymous Quiz
3%
([1], [2], [3, 4])
31%
TypeError
26%
([1], [2], [3, 4, 5, 6, 7])
40%
([1], [2], [3, 6, 7])
👍1
Вчера мы опубликовали ещё одну задачу из рубрики «Что выведет данный код?». Ответ, наверное, многих удивил. На этот раз задача требовала не столько внимательности, сколько знаний о специфике работы некоторых методов и операторов с причудливым сочетанием кортежей и списков в Python.

Общее количество ответов на данный момент 28, из них верных 9 - это 32%. Удивительно распределились оставшиеся ответы: 36% ответили, что будет выведена ошибка TypeError и оставшиеся 32%, что отработает только блок except.

Код задачи:
numbers = ([1], [2], [3])
try:
numbers[2] += [4, 5]
print(numbers)
except TypeError:
numbers[2].extend([6, 7])
print(numbers)



Для начала небольшое теоретическое вступление.
В посте об изменяемых и неизменяемых типах данных мы говорили о том, что ячейку, которая хранит неизменяемый объект (в частности кортеж), нельзя «вскрыть», чтобы изменить сам этот объект. На самом деле в реальности всё несколько сложнее. Кортеж мы действительно не можем изменить (например, добавить, удалить или, что особенно для нас важно, – переписать) элемент. Однако если кортеж внутри себя содержит изменяемые типы данных, например, списки, то сами эти списки могут изменяться.

Например, список [3], если он является элементом кортежа, можно слить со списком [6, 7] методом extend(). Для этого к списку [3] надо обратиться по индексу в кортеже. Само изменение происходит внутри первоначального списка, то есть в той же самой ячейке, где он и хранился. Если смотреть на ситуацию «глазами кортежа», в нём самом ничего не изменяется: он содержит ссылки на все те же ячейки по всё тем же адресам, а что там происходит внутри этих ячеек, его как бы не касается.


Ситуация с оператором += сложнее.
Сам по себе этот оператор - «синтаксический сахар» (то есть простая и удобная запись) для вызова следующего алгоритма действий:

1️⃣ Сначала вызывается метод __iadd__ для списка, который по сути работает так же, как и уже упомянутый нами метод extend(): сливает содержимое изначального списка с содержимым нового. Причем делает это точно так же – внутри ячейки, поэтому кортеж по этому поводу никак не "тревожится".

2️⃣ Перезаписыват результат слияния в ту же самую ячейку, где хранился изначальный список.

В коде это выглядело бы так:
a_list = [3]
result = a_list.__iadd__([4, 5])
a_list = result


И a_list, и result ссылаются на один и тот же объект – список, который мы только что изменили, поэтому эта перезапись - пустая формальность, но только не для кортежа. Для него это всё равно попытка перезаписать что-то новое (пусть на самом деле и хорошо забытое старое) в один из элементов, а так как кортеж – неизменяемый тип данных, такие штуки с ним не проходят. В этом месте вызывается исключение TypeError. Но важнейший нюанс заключается в том, что первая часть алгоритма, а именно применение метода __iadd__ успевает выполниться. Поэтому если мы обработаем исключение, список, который мы изменяли, успеет слиться с новым, оставаясь при этом элементом кортежа.


Вооружившись этим знанием, посмотрим, как сработает код из нашей вчерашней задачи:
1️⃣ Записываем в numbers кортеж списков ([1], [2], [3]).

2️⃣ Под try записываем попытку применить += к элементу кортежа под индексом 2. Первая часть этой задачи проходит успешно: список [3] сливается со списком [4, 5]. А вот попытка перезаписать этот список в ту же ячейку, как мы выяснили, оборачивается вызовом исключения TypeError.

3️⃣ Срабатывает обработка и мы перемещаемся в exception. Там к нашему уже измененному однажды списку [3, 4, 5] применяется "безобидный" метод extend, поэтому в конечном виде последний элемент кортежа приобретает вид [3, 4, 5, 6, 7].

4️⃣ Весь кортеж выводится на печать: ([1], [2], [3, 4, 5, 6, 7]).

Вот такой вот неожиданный ответ. Надеемся, вы почерпнули для себя что-то новое из этой задачки и ее объяснения. А если нет, то освежили свои знания. Приглашаем поделиться впечатлениями в комментариях.
🔥5👍1
С днём рождения, Ваня!
Автор: Андрей Лебедев

Наш канал, созданный не так уж давно, на сегодняшний день объединяет уже 288 человек. Кому-то это число может показаться небольшим, но среди нас уже наверняка есть люди, которые не знают, с чего или, точнее, с кого началось и на ком держится всё наше небольшое сообщество. Возможно, есть и те, кто просто не помнит таких вещей, потому что люди тут в том числе собрались пожилые. Первым - расскажем, а вторым - напомним, потому что старость надо уважать.
🔥6😱2
А началось всё с того, что один умный, инициативный, любознательный и во всех отношениях приятный мужчина в самом расцвете лет решил поделиться своими знаниями со всеми желающими, для чего создал канал, написал (и продолжает писать) сайт, запустил самый живой из всех известных мне чатов на 50 человек, объединив десятки людей с очень разной биографией из всех уголков России, а потом как будто растворился, превратившись в админа и спрятавшись за фотографией кота на салфетке. С тех пор каждое утро кот возвещает в чате о том, что “проснувся”, а затем без устали отвечает на любые вопросы - технического и образовательного характера, про мемы, музыку, отношения типа “Госпожа/миньоны” и, конечно же, про рыжих женщин и шарообразных дизайнеров. Параллельно кот учится в нескольких местах, пишет дипломы, запускает проекты, участвует в стажировках, проходит технические собеседования в IT-компаниях и обо всём этом честно докладывает всем собравшимся.

Сегодня у нашего Вани (я бы так кота, конечно, не назвал) день рождения! Я от всей души поздравляю нашего @proDreams и желаю ему просто оставаться собой, потому что нет никаких сомнений в том, что все личностные настройки у Вани уже правильно подобраны и подкручены так, чтобы состояться и как разработчик, и как просто хороший человек. Желаю только попутного ветра и чтобы никакие внешние обстоятельства все эти настройки не сбивали. Спасибо, Ваня, что ты такой и что ты у нас есть!

Передаю слово другим поздравляющим:

Поздравляю с днем рождения и желаю, чтобы в твоём кодe жизни не было никаких  багов, чтобы твоим проектам не страшны были никакие вирусы, чтобы твоя деятельность была запрограммирована только на успех, чтобы твое сердце было запаролено от любых разочарований и всякого негатива. Чтобы скрипты твоего здоровья всегда выполняли свои функции. Чтобы база данных твоих знаний никогда не устаревала и всегда только обновлялась. И чтобы список твоих доходов тянул на миллионы строк.
Артур

***

Что ж, воот и твой день рождения настал, поздравляю тебя с ним, старик) я благодарю тебя за все те помощи что ты мне давал, даже в долг без процентов на видеокарту 😁 научил многому, как ставить винду, даж родители говорили «смотри давай и учись» хввхха как вы пьяные с Яриком у меня дома хавали и винду ставили очередной раз 🤣 жди меня с пивком, с тебя покушатс, с твоим днём Sooshkoo
Анатолий

***

Хочу пожелать тебе, чтобы этот день стал особенным!

Ты замечательный! Ты молодец! Ты стараешься! У тебя обязательно все получится и ты добьешься всего, что тебе хочется!!! Пусть твой канал растет и развивается, привлекая новых участников и авторов! Ты собрал вокруг себя прекрасных людей! Успехов в стажировке и карьерного роста! Пусть все твои мечты сбываются!!! Будь счастлив и спасибо за всё, что ты делаешь!!!
Кристина

***

Привет Кот!

Сегодня твой день рождения, и мы  не можем пропустить возможность поздравить тебя :

*️⃣ Крутяцкого босса Некого проекта
*️⃣ Автора кучи умных постов
*️⃣ И создателя этого сумасшедшего чатика

Желаем тебе огромного количества баг-фри кода, интересных  проектов, звания сеньора  и возможности удаленной работы где- нибудь на Мальдивах за очень очень большую зарплату)).

Ну а так как  я не умею сама писать нормально поздравления ваша задача оставить как можно больше поздравляшек в комментариях
Ps: Тык в кнопку поддержать канал приветствуется.
Анастасия

***

Остальных приглашаем присоединиться к поздравлениям в комментариях!

Пост на сайте
Поддержать проект

#Код_на_салфетке #День_рождения #Ваня
4🔥3👍2😱1
Кто-то сейчас оптимизирует свой код или "работает и ладно"?
👍2
Ох уж эти тестировщики, всёб им сломать)
😁6🔥2
Django 38.1. Кабинет и все посты автора
Автор: Иван Ашихмин

Мы с вами реализовали практически всё, что необходимо авторам, за исключением одного важного момента - личного кабинета автора.

Этот пост будет разделён на несколько частей, в которых мы разберёмся:
- Как использовать в одном представлении несколько шаблонов при помощи декоратора.
- Как контролировать права доступа при помощи миксинов.
- Используем наследования представлений и шаблонов.
- Добавим страницы редактирования/удаления постов автором.- Перенесём функционал черновиков на сторону сайта.

И начнём мы со страницы кабинета автора. Это будет та же страница, что и профиль автора, но расширенная.
🔥2😱1
В этом посте мы напишем собственный декоратор. Если вы ещё не сталкивались с декораторами или мало знаете о них, то настоятельно рекомендую прочитать пост "Декораторы в питоне".


Представление страницы автора.
Откроем файл views.py в директории приложения user_app.

В нём у нас есть простенькое представление для профиля пользователя - UserProfileView:
class UserProfileView(TemplateView):
template_name = 'user_app/profile_page.html'

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
user = get_object_or_404(User, username=self.kwargs.get('username'))
except User.DoesNotExist:
raise Http404("Пользователь не найден")
context['user_profile'] = user
context['user_posts'] = PostModel.post_manager.filter(author=user)[:7]
context['title'] = f'Профиль пользователя {user}'
return context


Добавим следующие поля:
- author_template_name - в это поле впишем имя шаблона страницы автора.
- is_author - это поле-флаг для проверки того, что пользователь является автором.
- extended_group - имя расширенной группы, в нашем случае это группа "Автор".


Поля класса:
template_name = 'user_app/profile_page.html'  
author_template_name = 'user_app/profile_page_author.html'
is_author = False
extended_group = 'Автор'


Помните про метод dispatch(), о котором я рассказывал в посте "Django 34.2. Простой профиль пользователя – страница настроек"? В этот раз мы не будем вносить в него изменения, а обернём его в наш собственный декоратор. Таким образом, при срабатывании метода и до вызова метода get(), который вернёт пользователю необходимый шаблон, мы проверим является ли пользователь автором и определим какой шаблон ему вернётся.

Код:
@set_template(author_template_name, 'is_author')  
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)


В коде выше к методу dispatch() мы добавляем декоратор set_template, в который передаём два аргумента: шаблон автора и имя поля-флага. Об этом расскажу чуть дальше, сперва закончим с представлением.

Последнее, что у нас осталось, – это переопределённый метод get_context_data().
В этом методе нам необходимо добавить всего три строки:
1. Передачу в шаблон значение поля is_author.
2. Блок if в котором проверяем значение поля is_author.
3. Если значение is_author - True, передаём в шаблон черновики автора.


Дополнительные строки:
context['is_author'] = self.is_author  
if self.is_author:
context['drafts'] = PostModel.objects.filter(author=user, status='ЧЕ')[:7]



Полный код изменённого представления:
🔥2😱1
from user_app.decorators import set_template


class UserProfileView(TemplateView):
template_name = 'user_app/profile_page.html'
author_template_name = 'user_app/profile_page_author.html'
is_author = False
extended_group = 'Автор'

@set_template(author_template_name, 'is_author')
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
user = get_object_or_404(User, username=self.kwargs.get('username'))
except User.DoesNotExist:
raise Http404("Пользователь не найден")
context['user_profile'] = user
context['is_author'] = self.is_author
if self.is_author:
context['drafts'] = PostModel.objects.filter(author=user, status='ЧЕ')[:7]
context['user_posts'] = PostModel.post_manager.filter(author=user)[:7]
context['title'] = f'Профиль пользователя {user}'
return context



Декоратор set_template.
В директории приложения user_app создадим файл decorators.py.

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


Посмотрим на код:
from functools import wraps  

from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404


def set_template(special_template, role_field):
def decorator(view_func):
@wraps(view_func)
def wrapper(self, request, *args, **kwargs):
user = get_object_or_404(User, username=self.kwargs.get('username'))
if user self.request.user and user.groups.filter(name=self.extended_group).exists():
self.template_name = special_template
setattr(self, role_field, True)
return view_func(self, request, *args, **kwargs)

return wrapper

return decorator



А теперь разберём, что в нём происходит:
1. Создаём функцию set_template, принимающую аргументы - имя шаблона и поле-флаг.
2. Внутри создаём функцию decorator, принимающую задекорированный метод dispatch().
3. Ещё глубже, создаём функцию wrapper, принимающую те же аргументы, что и изначальный метод dispatch(). И сразу оборачиваем её во встроенный в functools декоратор - wraps. Это позволит нам получить доступ к экземпляру класса. Если этот декоратор не указать, то ключевое слово self будет недоступно.
4. В переменную user получаем объект пользователя, на чью страницу был произведён переход.
5. В блоке if проверяем, что текущий пользователь совпадает с пользователем в переменной user, а также, что он входит в группу "Автор". Если проверка проходит, то происходят следующие изменения:
5.1. Значение поля template_name заменяется на переданное в аргументе в самой внешней функции set_template.
5.2. Значение поля-флага, имя которого передано вторым аргументом, устанавливается на True.
6. Вызывается задекорированная функция.

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


Шаблон профиля пользователя.
Откроем файл profile_page.html, расположенный в директории с шаблонами приложения user_app.
🔥2😱1
Вместо того, чтобы делать полную копию шаблона профиля пользователя и вносить правки для кабинета автора, сделаем проверку на значение is_author. Если значение False, будет отображаться та же страница, если же в переменной True, то будут отображаться два блока, которые будут определены в другом файле.

Находим блок {% if user_posts %} и помещаем его в блок {% else %}, не забыв в конце добавить тег {% endif %}. Над else прописываем тег {% if is_author %}, внутрь которого помещаем два пустых блока - для черновиков и для постов:
{% if is_author %}  
{% block drafts %}
{% endblock %}
{% block public %}
{% endblock %}
{% else %}
...
{% endif %}



Шаблон кабинета автора.
Создадим новый файл profile_page_author.html.

В нём добавим наследование от profile_page.html и заполним два блока:
Много текста. Код доступен на сайте или в материалах к посту.



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


Изначальное представление:
class UserPostsView(ListView):
template_name = 'user_app/user_posts_page.html'
context_object_name = 'posts'
paginate_by = 10

def get_queryset(self):
return PostModel.post_manager.filter(author__username=self.kwargs.get('username'))

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
author = get_object_or_404(User, username=self.kwargs.get("username"))
except User.DoesNotExist:
raise Http404("Пользователь не найден")
context['author'] = author
context['title'] = f'Посты пользователя {author}'
return context



Модифицированное:
class UserPostsView(ListView):  
template_name = 'user_app/user_posts_page.html'
author_template_name = 'user_app/user_posts_page_author.html'
context_object_name = 'posts'
paginate_by = 10
is_author = False
extended_group = 'Автор'

@set_template(author_template_name, 'is_author')
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)

def get_queryset(self):
if self.is_author:
return (PostModel.objects.filter(author__username=self.kwargs.get('username'))
.order_by('-status', '-publish'))
else:
return PostModel.post_manager.filter(author__username=self.kwargs.get('username'))

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
author = get_object_or_404(User, username=self.kwargs.get("username"))
except User.DoesNotExist:
raise Http404("Пользователь не найден")
context['author'] = author
context['title'] = f'Посты пользователя {author}'
context['is_author'] = self.is_author
return context


Как видим, изменения идентичны представлению UserProfileView, за исключением метода get_queryset(). В нём мы проверяем авторство, если пользователь, открывший страницу, – автор, то мы выводим все посты пользователя. При этом используем сортировку, чтобы сперва выводились черновики, а затем все остальные посты. В противном случае мы выводим просто посты.


Шаблоны страниц для автора и пользователя.
🔥2😱1
Аналогия примерно та же, что и с шаблоном кабинета, поскольку посты получаем на этапе представления для автора или пользователя, нет необходимости делать блоки с постами.
Вместо этого мы добавим блоки с кнопками и отображением статуса поста – черновик или опубликован.

В шаблоне user_posts_page.html в удобное место добавляем блок с кнопками:
{% if is_author %}  
<div class="col-3 d-flex text-center">
{% block buttons %}
{% endblock %}
</div>
{% endif %}


И блок со статусом:
{% if is_author %}{% block status %}{% endblock %}{% endif %}



Код шаблона:
Много текста. Код доступен на сайте или в материалах к посту.


Создаём новый файл user_posts_page_author.html, в него прописываем код блоков:
{% extends 'user_app/user_posts_page.html' %}  

{% block buttons %}
<a href="{% url 'user_app:edit_post' pk=post.pk %}" class="btn btn-sm btn-primary my-btn-success w-100 me-1">Редактировать</a>
<a href="{% url 'user_app:delete_post' pk=post.pk %}" class="btn btn-sm btn-secondary my-btn-danger w-100 me-1">Удалить</a>
{% endblock %}

{% block status %}
| {{ post.get_status_display }}
{% endblock %}



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

Файлы к посту, можно получить в боте по коду: 401184

Пост на сайте
Поддержать проект

#Python #Django #Гайды #шаблоны #Представления #dispatch #Декоратор #кабинет_автора
🔥4
👀Как мне повезло с моей командой готовой работать за идею!))
😁7
Всем привет.

Уверен, у многих выдалась непростая неделя. Самое время немножко отдохнуть за просмотром хорошего фильма. Заваривайте чаёк или чего-нибудь покрепче (кому как) и устраивайтесь поудобнее.

Данный фильм подойдёт как для одиночного, так и для семейного просмотра.

Фильм: Создатель
Год: 2023

События разворачиваются в будущем, где разворачивается война между людьми и искусственным интеллектом. Бывший спецназовец Джошуа вместе с отрядом оперативников получает задание: найти и убить архитектора продвинутого искусственного интеллекта — Создателя, который разработал оружие, способное положить конец не только войне, но и всему человечеству.

https://www.sspoisk.ru/film/4499408/?utm_referrer=www.google.com

Приятного просмотра. А когда посмотрите - расскажите в комментарии, что вы о нём думаете (без спойлеров) 🙂
🔥2
Что выведет этот код? №3
Вчера мы опубликовали весьма непростую задачу. Правильный ответ стал откровением в том числе и для нашей редакции. Наверняка многие из вас знают Python на достаточном уровне, чтобы понимать lambda-выражения, но часто ли вы используете их в коде? Знаете ли о нюансах их работы?

Об одном из таких нюансов мы и поведаем вам в разборе этой задачи.

В этот раз набралось 27 ответов. Правильно ответили всего 5 человек, это 19% от общего числа ответов. Самым популярным ответом оказался второй, набравший 52%, потому что именно такого результата ожидаешь, смотря в код.

Код задачи:
funcs = [lambda x: x * i for i in range(5)]
print([f(3) for f in funcs])


Всего две строки кода. Давайте разберёмся, что здесь происходит:

В первой строке:
1. Генерируем список из пяти lambda-функций. lambda-функция умножает переданное в неё значение x на i. При этом значения i меняются от 0 до 4 по мере прохождения цикла по результатам работы функции range(5).
2. В переменную funcs помещаем сгенерированный список.

Во второй строке:
3. При помощи генератора последовательно вызываем каждую из пяти созданных lambda-функций, передавая туда 3 в качестве аргумента.
4. Результат работы генератора помещаем в список и выводим на печать.

Что же тут не так? Почему правильный ответ [12, 12, 12, 12, 12], а не [0, 3, 6, 9, 12]? Ведь переменная i у нас по очереди принимала значения от 0 до 4.

Всё дело в особенностях работы lambda-функций и цикла for.
Так выходит, что lambda-функция не запоминает значение i во время создания первого списка. Она буквально запоминает выражение x * i, а не x * <значение_i>, то есть как будто использует i в качестве заглушки без конкретного значения.

Ещё одна особенность в том, что переменная i после окончания работы цикла for не пропадает в жерновах сборщика мусора. Она остаётся доступной и содержит последнее записанное в нее на момент остановки работы цикла значение. Рассмотрим на примере:

for i in range(5):
continue

print(i)
>>> 4


Переменная i по ходу работы цикла приняла все целочисленные значения от 0 до 4. Когда работа цикла завершилась, в переменной i оказалось записано последнее из них – 4.

Это же и происходит в генераторе второго списка.
Вызывая f(3) вместо:
3 * 0
3 * 1
3 * 2
3 * 3
3 * 4

>>> [0, 3, 6, 9, 12]


Мы получаем:
3 * 4
3 * 4
3 * 4
3 * 4
3 * 4

>>> [12, 12, 12, 12, 12]


Можно ли изменить код таким образом, чтобы получить в результате его работы [0, 3, 6, 9, 12]?

Можно. Всё достаточно просто. Необходимо в аргументы lambda-функции, помимо x, передать ещё и i, установив её в качестве аргумента по умолчанию равного i:
funcs = [lambda x, i=i: x * i for i in range(5)]
print([f(3) for f in funcs])


Таким образом, каждая итерация lambda-функции будет запоминать значение i, поскольку оно теперь передано в аргументах функции.
🔥5🤯5
Django 38.2. Добавление, редактирование, удаление поста
Автор: Иван Ашихмин

В прошлом посте, мы доработали профиль пользователя, превратив его в кабинет автора. Для этого мы написали свой декоратор и расширили шаблон страницы.

В этом посте мы разберёмся, что такое миксины (mixins), для чего они нужны, как применяются и как написать свои.
Изменим имеющееся представление страницы добавления поста, превратив его в базовый класс, от которого унаследуем два представления: добавления и изменения поста.
Также добавим представление и страницу удаления поста.

Начнём с небольшой теории.


Что такое миксины (mixins)?
🔥1
Миксины (mixins) представляют собой специальные классы, которые содержат методы и функциональность для повторного использования в других классах. Они используются для организации кода, обеспечивая переиспользование функциональности между различными классами представлений, моделей или других компонентов Django.

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


Как работают миксины в Django:

Определение миксина:
Миксин - это обычно класс Python, содержащий методы и атрибуты, предназначенные для использования в других классах. Например:
class MyMixin:
def some_method(self):
# Логика метода
pass



Использование миксина:
Для применения миксина к классу необходимо добавить его в список наследования:
from django.views import View


class MyView(MyMixin, View):
def get(self, request):
# Логика представления
return HttpResponse('Hello, World!')


Здесь MyView наследуется от MyMixin и View. Теперь методы из MyMixin доступны в MyView.


Применение миксинов в Django:
- Представления (Views): Миксины могут использоваться для добавления функциональности к представлениям Django, например, для добавления проверок доступа, кэширования или другой логики.
- Модели (Models): Миксины также могут содержать методы для работы с моделями Django, например, для добавления общих методов или свойств к нескольким моделям.
- Формы (Forms): Миксины можно использовать для повторного использования функциональности в формах Django, такой как валидация данных или обработка форм.
- Другие компоненты: Кроме того, миксины можно использовать практически в любом компоненте Django, где требуется повторное использование функциональности.

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


Собственные миксины.
От теории перейдём к практике и напишем два миксина:
- Миксин проверки принадлежности пользователя к указанной группе.
- Миксин проверки того, что пользователь и автор поста - один человек.

В директории приложения user_app создадим файл mixins.py. В нём будем писать наши миксины.


Миксин проверки принадлежности к группе.
Для того, что бы проверять входит ли пользователь в необходимую группу, в данном случае в группу Автор, можно в каждом представлении прописывать метод dispatch() с проверкой. Чтобы избежать дублирования кода, мы напишем миксин с проверкой.

Создадим класс PermissionGroupRequiredMixin.

В нём пропишем поле group_required и присвоим ему значение None. Это поле с названием необходимой группы будем прописывать в представлении.

Затем создадим метод dispatch(). В методе проверяем, входит ли пользователь в указанную группу, то мы вызываем dispatch() из класса представления. Иначе поднимаем ошибку 403 HttpResponseForbidden с сообщением об отсутствии доступа.


Код миксина:
from django.http import HttpResponseForbidden


class PermissionGroupRequiredMixin:
group_required = None

def dispatch(self, request, *args, **kwargs):
if request.user.groups.filter(name=self.group_required).exists():
return super().dispatch(request, *args, **kwargs)
else:
raise HttpResponseForbidden("Вы не имеете доступа к этой странице.")
🔥1