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
Аналогия примерно та же, что и с шаблоном кабинета, поскольку посты получаем на этапе представления для автора или пользователя, нет необходимости делать блоки с постами.
Вместо этого мы добавим блоки с кнопками и отображением статуса поста – черновик или опубликован.
В шаблоне
И блок со статусом:
Код шаблона:
Создаём новый файл
Заключение.
В коде шаблонов уже добавлены ссылки на редактирование и удаление поста автором, они пока не рабочие и, вероятно, вызовут ошибку при переходе на страницу. Про реализацию функционала редактирования и удаления я расскажу в следующем посте.
Файлы к посту, можно получить в боте по коду: 401184
Пост на сайте
Поддержать проект
#Python #Django #Гайды #шаблоны #Представления #dispatch #Декоратор #кабинет_автора
Вместо этого мы добавим блоки с кнопками и отображением статуса поста – черновик или опубликован.
В шаблоне
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
Всем привет.
Уверен, у многих выдалась непростая неделя. Самое время немножко отдохнуть за просмотром хорошего фильма. Заваривайте чаёк или чего-нибудь покрепче (кому как) и устраивайтесь поудобнее.
Данный фильм подойдёт как для одиночного, так и для семейного просмотра.
Фильм: Создатель
Год: 2023
События разворачиваются в будущем, где разворачивается война между людьми и искусственным интеллектом. Бывший спецназовец Джошуа вместе с отрядом оперативников получает задание: найти и убить архитектора продвинутого искусственного интеллекта — Создателя, который разработал оружие, способное положить конец не только войне, но и всему человечеству.
https://www.sspoisk.ru/film/4499408/?utm_referrer=www.google.com
Приятного просмотра. А когда посмотрите - расскажите в комментарии, что вы о нём думаете (без спойлеров) 🙂
Уверен, у многих выдалась непростая неделя. Самое время немножко отдохнуть за просмотром хорошего фильма. Заваривайте чаёк или чего-нибудь покрепче (кому как) и устраивайтесь поудобнее.
Данный фильм подойдёт как для одиночного, так и для семейного просмотра.
Фильм: Создатель
Год: 2023
События разворачиваются в будущем, где разворачивается война между людьми и искусственным интеллектом. Бывший спецназовец Джошуа вместе с отрядом оперативников получает задание: найти и убить архитектора продвинутого искусственного интеллекта — Создателя, который разработал оружие, способное положить конец не только войне, но и всему человечеству.
https://www.sspoisk.ru/film/4499408/?utm_referrer=www.google.com
Приятного просмотра. А когда посмотрите - расскажите в комментарии, что вы о нём думаете (без спойлеров) 🙂
🔥2
Что выведет код с изображения выше?
Anonymous Quiz
7%
[0, 0, 0, 0, 0]
49%
[0, 3, 6, 9, 12]
22%
[12, 12, 12, 12, 12]
22%
Ничего из перечисленного выше
Вчера мы опубликовали весьма непростую задачу. Правильный ответ стал откровением в том числе и для нашей редакции. Наверняка многие из вас знают Python на достаточном уровне, чтобы понимать
Об одном из таких нюансов мы и поведаем вам в разборе этой задачи.
В этот раз набралось 27 ответов. Правильно ответили всего 5 человек, это 19% от общего числа ответов. Самым популярным ответом оказался второй, набравший 52%, потому что именно такого результата ожидаешь, смотря в код.
Код задачи:
Всего две строки кода. Давайте разберёмся, что здесь происходит:
В первой строке:
1. Генерируем список из пяти
2. В переменную
Во второй строке:
3. При помощи генератора последовательно вызываем каждую из пяти созданных
4. Результат работы генератора помещаем в список и выводим на печать.
Что же тут не так? Почему правильный ответ
Всё дело в особенностях работы
Так выходит, что
Ещё одна особенность в том, что переменная
Переменная
Это же и происходит в генераторе второго списка.
Вызывая
Мы получаем:
Можно ли изменить код таким образом, чтобы получить в результате его работы
Можно. Всё достаточно просто. Необходимо в аргументы
Таким образом, каждая итерация
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)?
Автор: Иван Ашихмин
В прошлом посте, мы доработали профиль пользователя, превратив его в кабинет автора. Для этого мы написали свой декоратор и расширили шаблон страницы.
В этом посте мы разберёмся, что такое миксины (mixins), для чего они нужны, как применяются и как написать свои.
Изменим имеющееся представление страницы добавления поста, превратив его в базовый класс, от которого унаследуем два представления: добавления и изменения поста.
Также добавим представление и страницу удаления поста.
Начнём с небольшой теории.
Что такое миксины (mixins)?
🔥1
Миксины (mixins) представляют собой специальные классы, которые содержат методы и функциональность для повторного использования в других классах. Они используются для организации кода, обеспечивая переиспользование функциональности между различными классами представлений, моделей или других компонентов Django.
Миксины позволяют создавать небольшие модули функциональности, которые могут быть добавлены к классам через множественное наследование. Это позволяет разделить код на более мелкие и переиспользуемые компоненты, что улучшает читаемость, поддержку и уменьшает дублирование кода.
Как работают миксины в Django:
Определение миксина:
Миксин - это обычно класс Python, содержащий методы и атрибуты, предназначенные для использования в других классах. Например:
Использование миксина:
Для применения миксина к классу необходимо добавить его в список наследования:
Здесь
Применение миксинов в Django:
- Представления (Views): Миксины могут использоваться для добавления функциональности к представлениям Django, например, для добавления проверок доступа, кэширования или другой логики.
- Модели (Models): Миксины также могут содержать методы для работы с моделями Django, например, для добавления общих методов или свойств к нескольким моделям.
- Формы (Forms): Миксины можно использовать для повторного использования функциональности в формах Django, такой как валидация данных или обработка форм.
- Другие компоненты: Кроме того, миксины можно использовать практически в любом компоненте 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
Как видим, достаточно просто.
Миксин проверки автора поста.
Для представлений редактирования и удаления поста, необходима проверка того, что автор поста и текущий пользователь - один человек.
Создадим класс
Код будет такой же, как и предыдущий.
Полей у миксина не будет, а в методе
Код миксина:
Представления.
В директории приложения
В посте Django 36. Добавление постов пользователем мы написали следующее представление для страницы добавления поста:
Мы его изменим, уберём лишнее и создадим два наследуемых класса.
Базовое представление.
Первым делом изменим имя класса на
К имеющимся полям добавим новое поле
Удалим метод
Всё остальное оставляем как есть. Если IDE будет ругаться на некоторые методы, игнорируем. У нас в классе их нет, но они будут в наших классах наследниках.
Код класса:
Представления страниц добавления поста и редактирования поста.
Как вы могли заметить в коде выше, среди полей основного класса мы оставили поле с именем шаблона страницы. Это связано с тем, что для добавления и для редактирования поста будем использовать один и тот же шаблон. Нам нет необходимости создавать второй шаблон, т.к. по содержанию они будут идентичны. Разница лишь в том, что при добавлении поста, поля будут пустыми, а при редактировании они будут заполнены.
Создаём два класса:
-
-
Миксин проверки автора поста.
Для представлений редактирования и удаления поста, необходима проверка того, что автор поста и текущий пользователь - один человек.
Создадим класс
PermissionSameAuthorMixin.Код будет такой же, как и предыдущий.
Полей у миксина не будет, а в методе
dispatch() изменим проверку.Код миксина:
from django.http import HttpResponseForbidden
class PermissionSameAuthorMixin:
def dispatch(self, request, *args, **kwargs):
if request.user self.get_object().author:
return super().dispatch(request, *args, **kwargs)
else:
raise HttpResponseForbidden("Вы не имеете доступа к этой странице.")
Представления.
В директории приложения
user_app откроем файл views.py.В посте Django 36. Добавление постов пользователем мы написали следующее представление для страницы добавления поста:
class AddPostByAuthorView(UserPassesTestMixin, CreateView):
template_name = 'user_app/add_post.html'
form_class = forms.AddPostByAuthorForm
model = PostModel
def test_func(self):
return self.request.user.groups.filter(name='Автор').exists()
def get_success_url(self):
return reverse('user_app:user_profile', kwargs={'username': self.request.user.username})
def form_valid(self, form):
form.instance.slug = slugify(form.instance.title)
form.instance.author = self.request.user
return super().form_valid(form)
Мы его изменим, уберём лишнее и создадим два наследуемых класса.
Базовое представление.
Первым делом изменим имя класса на
PostByAuthor, убрав все наследования.К имеющимся полям добавим новое поле
group_required со значением необходимой для доступа к представлениям группы. В нашем случае группы Автор.Удалим метод
test_func, так как он нам больше не нужен.Всё остальное оставляем как есть. Если IDE будет ругаться на некоторые методы, игнорируем. У нас в классе их нет, но они будут в наших классах наследниках.
Код класса:
from django.urls import reverse_lazy
from pytils.translit import slugify
from blog.models import PostModel
from user_app import forms
class PostByAuthor:
template_name = 'user_app/add_post.html'
group_required = 'Автор'
form_class = forms.AddPostByAuthorForm
model = PostModel
def get_success_url(self):
return reverse('user_app:user_profile', kwargs={'username': self.request.user.username})
def form_valid(self, form):
form.instance.slug = slugify(form.instance.title)[:50]
form.instance.author = self.request.user
return super().form_valid(form)
Представления страниц добавления поста и редактирования поста.
Как вы могли заметить в коде выше, среди полей основного класса мы оставили поле с именем шаблона страницы. Это связано с тем, что для добавления и для редактирования поста будем использовать один и тот же шаблон. Нам нет необходимости создавать второй шаблон, т.к. по содержанию они будут идентичны. Разница лишь в том, что при добавлении поста, поля будут пустыми, а при редактировании они будут заполнены.
Создаём два класса:
-
AddPostByAuthorView, унаследованный от PostByAuthor, PermissionGroupRequiredMixin, CreateView.-
EditPostByAuthorView, унаследованный от PostByAuthor, PermissionSameAuthorMixin, UpdateView.🔥1
В первом классе мы наследуемся от нашего основного класса, миксина проверки принадлежности к группе и встроенного класса
Во втором классе мы также наследуемся от основного класса, миксина, проверяющего идентичность автора поста и пользователя, и от стандартного
В обоих классах добавляем единственное поле
Код представлений:
Представление страницы удаления поста.
Для страницы удаления поста мы напишем отдельное представление.
Создадим класс
Прописываем четыре поля:
-
-
-
-
Также переопределяем метод
Код представления:
Шаблон страницы удаления поста.
Создадим файл
В шаблоне прописываем форму с двумя кнопками: "подтвердить удаление поста" и "вернуться в профиль".
Код шаблона:
URL-паттерны.
Осталось добавить
Кнопки в кабинете автора мы добавили в прошлом посте.
Откроем файл
CreateView.Во втором классе мы также наследуемся от основного класса, миксина, проверяющего идентичность автора поста и пользователя, и от стандартного
UpdateView.В обоих классах добавляем единственное поле
extra_context, в котором прописываем заголовок страницы. Это всё, что будет в двух представлениях.Код представлений:
from django.views.generic import CreateView, UpdateView
from user_app.mixins import PermissionSameAuthorMixin, PermissionGroupRequiredMixin
class AddPostByAuthorView(PostByAuthor, PermissionGroupRequiredMixin, CreateView):
extra_context = {'title': 'Добавление поста'}
class EditPostByAuthorView(PostByAuthor, PermissionSameAuthorMixin, UpdateView):
extra_context = {'title': 'Изменить пост'}
Представление страницы удаления поста.
Для страницы удаления поста мы напишем отдельное представление.
Создадим класс
DeletePostByAuthorView, унаследованный от PermissionSameAuthorMixin и DeleteView.Прописываем четыре поля:
-
template_name - шаблон страницы.-
model - модель поста.-
group_required - требуемая группа для доступа к странице.-
extra_context - дополнительные данные передаваемые в шаблон. В нашем случае заголовок страницы.Также переопределяем метод
get_success_url(), возвращая на страницу профиля. Он такой же как и в представлениях выше.Код представления:
from django.views.generic import DeleteView
from django.urls import reverse_lazy
from blog.models import PostModel
from user_app.mixins import PermissionSameAuthorMixin
class DeletePostByAuthorView(PermissionSameAuthorMixin, DeleteView):
template_name = 'user_app/delete_post_confirm.html'
model = PostModel
group_required = 'Автор'
extra_context = {'title': 'Удалить пост?'}
def get_success_url(self):
return reverse_lazy('user_app:user_profile', kwargs={'username': self.request.user.username})
Шаблон страницы удаления поста.
Создадим файл
delete_post_confirm.html в директории с шаблонами.В шаблоне прописываем форму с двумя кнопками: "подтвердить удаление поста" и "вернуться в профиль".
Код шаблона:
{% extends 'blog/base.html' %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="form-section container mt-3">
<h2>Удаление поста {{ object }}</h2>
<p>Вы действительно хотите удалить этот пост?</p>
<form method="POST">
{% csrf_token %}
<button class="btn btn-secondary my-btn-danger me-2" type="submit">Да, удалить</button>
<a href="{% url 'user_app:user_profile' username=request.user.username %}" class="btn btn-primary my-btn-success">Отмена</a>
</form> </div>{% endblock %}URL-паттерны.
Осталось добавить
URL-паттерны представлений.Кнопки в кабинете автора мы добавили в прошлом посте.
Откроем файл
urls.py в директории приложения user_app и добавим следующие строки в список urlpatterns:🔥2
path('post/add_post/', views.AddPostByAuthorView.as_view(), name='add_post'),
path('post/edit_post/<int:pk>', views.EditPostByAuthorView.as_view(), name='edit_post'),
path('post/delete_post/<int:pk>', views.DeletePostByAuthorView.as_view(), name='delete_post'),Заключение.
В этом посте мы разобрали, что такое миксины. В Django есть ряд встроенных миксинов, таких как
LoginRequiredMixin, проверяющий зарегистрирован пользователь или нет. Написание собственных миксинов поможет расширить функционал представлений, без дублирования кода.Кроме того, мы ещё раз попробовали создать базовый класс и сделать на его основе представления. Мы сделали один большой класс и два буквально
двустрочных. Если бы мы не сделали базовый класс, у нас вместо трёх, было бы два представления, однако оба были бы идентичными.Файлы к посту, можно получить в боте по коду: 949621
Пост на сайте
Поддержать проект
#Python #Django #Гайды #шаблоны #представления #классы #наследование #Миксины #Mixin #группы
🔥5
Проекту "Код на салфетке" исполняется 6 месяцев!
Автор: Иван Ашихмин
Приветствую.
Сегодня каналу исполняется 6 месяцев!
Это большое событие для меня лично.
Начиная канал, я и не подозревал, что буду вести его не один, что в чате соберётся своё сообщество активных и интересных людей, что на канал подпишется аж 310 человек.
Благодарю всех подписчиков и причастных к каналу людей за помощь, поддержку и критику. Без вас этого всего бы не было.
Нельзя обойти стороной и тех, кто помогает с каналом и пишет посты:
- Спасибо Кристине Большаковой, за веру, поддержку, советы и идеи.
- Спасибо Андрею Лебедеву, за полезную, интересную и так легко читаемую рубрику о тонкостях Python.
- Спасибо Евгению Акопяну, за помощь в дизайне для канала, а так же за разработку нового дизайна для сайта.
- Спасибо Артуру Мурзыеву, моему хорошему другу, который ведёт рубрику пятничного кино, а может и ещё, что-то будет вести 😉.
- И другим причастным, пожелавшим остаться неизвестными, тоже огромное спасибо!
Автор: Иван Ашихмин
Приветствую.
Сегодня каналу исполняется 6 месяцев!
Это большое событие для меня лично.
Начиная канал, я и не подозревал, что буду вести его не один, что в чате соберётся своё сообщество активных и интересных людей, что на канал подпишется аж 310 человек.
Благодарю всех подписчиков и причастных к каналу людей за помощь, поддержку и критику. Без вас этого всего бы не было.
Нельзя обойти стороной и тех, кто помогает с каналом и пишет посты:
- Спасибо Кристине Большаковой, за веру, поддержку, советы и идеи.
- Спасибо Андрею Лебедеву, за полезную, интересную и так легко читаемую рубрику о тонкостях Python.
- Спасибо Евгению Акопяну, за помощь в дизайне для канала, а так же за разработку нового дизайна для сайта.
- Спасибо Артуру Мурзыеву, моему хорошему другу, который ведёт рубрику пятничного кино, а может и ещё, что-то будет вести 😉.
- И другим причастным, пожелавшим остаться неизвестными, тоже огромное спасибо!
🔥9❤1🤩1
И это только начало. Планов по развитию много, осталось только их воплотить в жизнь.
Особая благодарность поддерживающим проект на Boosty:
- Яковлев Павел
- Сергей Гордиенко
- Юрий Гаврилович
- Кристина
- MSSW
Вы помогаете оплачивать сервер и даёте мотивацию продолжать.
Огромное спасибо!
Пост на сайте
Поддержать проект
#Новости #Код_на_салфетке #пол_года
Особая благодарность поддерживающим проект на Boosty:
- Яковлев Павел
- Сергей Гордиенко
- Юрий Гаврилович
- Кристина
- MSSW
Вы помогаете оплачивать сервер и даёте мотивацию продолжать.
Огромное спасибо!
Пост на сайте
Поддержать проект
#Новости #Код_на_салфетке #пол_года
🔥6
Что? Уже? Пролетел очередной месяц учёбы...
По этому поводу готов очередной отчёт на Pikabu.
https://pikabu.ru/story/obuchenie_semnadtsatyiy_mesyats_10949513
😊Как обычно, с вас лайки)
По этому поводу готов очередной отчёт на Pikabu.
https://pikabu.ru/story/obuchenie_semnadtsatyiy_mesyats_10949513
😊Как обычно, с вас лайки)
🔥7
Приветствую, друзья!
Снова пятница, а это значит пришло время отдохнуть от дел и немного отвлечься. Давайте воспользуемся этим моментом для заслуженного отдыха, наслаждаясь просмотром отличного фильма. Приготовьте что-нибудь под ваш любимый напиток, расслабьтесь, очистите голову от мыслей и готовьтесь к приятному времяпрепровождению.
Фильм: Голодные игры: Баллада о змеях и певчих птицах
Год: 2023
До того как стать деспотичным президентом Панэма, молодой Кориолан Сноу был последней надеждой своего увядающего рода — некогда великой династии, впавшей в немилость послевоенного Капитолия. Накануне десятых ежегодных Голодных игр Сноу назначают наставником Люси Грей Бэйрд — трибута дистрикта 12. Постепенно девушка привлекает все больше внимание Панэма, становясь фавориткой грядущего соревнования. Начинается гонка со временем, которая покажет, кто певчая птица, а кто — змея.
Всем приятного просмотра и делимся своим мнением в комментариях
https://www.sspoisk.ru/film/1289334/?utm_referrer=www.google.com
Снова пятница, а это значит пришло время отдохнуть от дел и немного отвлечься. Давайте воспользуемся этим моментом для заслуженного отдыха, наслаждаясь просмотром отличного фильма. Приготовьте что-нибудь под ваш любимый напиток, расслабьтесь, очистите голову от мыслей и готовьтесь к приятному времяпрепровождению.
Фильм: Голодные игры: Баллада о змеях и певчих птицах
Год: 2023
До того как стать деспотичным президентом Панэма, молодой Кориолан Сноу был последней надеждой своего увядающего рода — некогда великой династии, впавшей в немилость послевоенного Капитолия. Накануне десятых ежегодных Голодных игр Сноу назначают наставником Люси Грей Бэйрд — трибута дистрикта 12. Постепенно девушка привлекает все больше внимание Панэма, становясь фавориткой грядущего соревнования. Начинается гонка со временем, которая покажет, кто певчая птица, а кто — змея.
Всем приятного просмотра и делимся своим мнением в комментариях
https://www.sspoisk.ru/film/1289334/?utm_referrer=www.google.com
🔥2🤮1
Что выведет код с изображения выше?
Anonymous Quiz
13%
Осталось конфет: 5
31%
Осталось конфет: 4
36%
UnboundLocalError
21%
Ничего из перечисленного выше
Вчерашняя задача была проще предыдущих, но, как нам представляется, получилась при этом не менее интересной. Она напоминает самую первую задачу из рубрики "Что выведет данный код?" и направлена на внимательность и понимание областей видимости в Python.
В викторине приняло участие 26 человек. Верный ответ оказался одним из двух наиболее популярных: 34% (9 человек) ответили, что код выведет "UnboundLocalError" - и это правильно! Столько же человек посчитало, что код выведет "Осталось конфет: 4". Увы, это неверно, и сейчас мы подробно разберём, почему...
Давайте же разберём эту задачу.
Код задачи:
В самом начале объявлена переменная
Далее прописывается простейший декоратор
Затем объявляем задекорированную функцию
В самом конце вызываем
Вернёмся к декоратору
И что же мы получаем в результате работы? Ошибку!
Почему ошибка?
Всё дело в области видимости, к которой относится переменная
Чтобы глобальная переменная
В викторине приняло участие 26 человек. Верный ответ оказался одним из двух наиболее популярных: 34% (9 человек) ответили, что код выведет "UnboundLocalError" - и это правильно! Столько же человек посчитало, что код выведет "Осталось конфет: 4". Увы, это неверно, и сейчас мы подробно разберём, почему...
Давайте же разберём эту задачу.
Код задачи:
candies = 5
def deco(func):
def wrapper():
candies -= 1
func()
return wrapper
@deco
def print_candies():
print(f'Осталось конфет: {candies}.')
print_candies()
В самом начале объявлена переменная
candies со значением 5.Далее прописывается простейший декоратор
deco. Если устройство декоратора вызывает вопросы, подробно эту тему мы рассматривали в посте "[Декораторы в питоне](https://pressanybutton.ru/post/nyuansy-python/dekoratory-v-pitone/?utm_source=telegram&utm_medium=channel&utm_campaign=post)". Сейчас же подробное описание пока пропустим.Затем объявляем задекорированную функцию
print_candies(), выводящую на экран количество конфет в переменной candies.В самом конце вызываем
print_candies(), чтобы запустить весь процесс.Вернёмся к декоратору
deco. В нём мы уменьшаем значение переменной candies на единицу и вызываем задекорированную функцию print_candies().И что же мы получаем в результате работы? Ошибку!
Почему ошибка?
Всё дело в области видимости, к которой относится переменная
candies. Переменная из верхней строчки кода (в которой лежит 5 конфет) существует в глобальном контексте: она определена вне любой из функций программы. В принципе к ней можно обратиться из любой функции, но только чтобы считать ее значение. Однако в функции wrapper() внутри декоратора мы не просто считываем значение глобальной переменной candies, но и пытаемся изменить его, уменьшив на единицу, а это уже запрещенный приём. Изменять внутри функции можно значение только тех переменных, которые существуют в их локальной области видимости. В противном случае нас ожидает исключение UnboundLocalError.Чтобы глобальная переменная
candies стала доступна для изменения внутри функции wrapper(), необходимо объявить её глобальной при помощи ключевого слова global:candies = 5
def deco(func):
def wrapper():
global candies
candies -= 1
func()
return wrapper
@deco
def print_candies():
print(f'Осталось конфет: {candies}.')
print_candies()
🔥4
Сравнение типизации Python и C
Автор: Arduinum628
Всем доброго дня! На канале начинается новая рубрика - "Сравнение языка Python 3 и C". Чем отличается высокоуровневый Python от низкоуровневого C? Что такое компилятор? Как работает интерпретатор Python? Как подружить Python и C? Как пк взаимодействует с низкоуровневыми языками? На эти и другие вопросы, связанные с данной темой, ответит новая рубрика. Все примеры я буду писать и запускать в операционной системе Kubuntu. Kubuntu - производный от Ubuntu дистрибутив Linux. Приготовьтесь к открытиям и экспериментам. Погружение в низкий уровень начинается...
Сравнение типизации
Нам известно, что Python имеет динамическую типизацию. Это означает, что типы переменных определяются во время выполнения программы и могут измениться на лету во время выполнения программы. Это даёт некоторую гибкость использования данных в Python.
Автор: Arduinum628
Всем доброго дня! На канале начинается новая рубрика - "Сравнение языка Python 3 и C". Чем отличается высокоуровневый Python от низкоуровневого C? Что такое компилятор? Как работает интерпретатор Python? Как подружить Python и C? Как пк взаимодействует с низкоуровневыми языками? На эти и другие вопросы, связанные с данной темой, ответит новая рубрика. Все примеры я буду писать и запускать в операционной системе Kubuntu. Kubuntu - производный от Ubuntu дистрибутив Linux. Приготовьтесь к открытиям и экспериментам. Погружение в низкий уровень начинается...
Сравнение типизации
Нам известно, что Python имеет динамическую типизацию. Это означает, что типы переменных определяются во время выполнения программы и могут измениться на лету во время выполнения программы. Это даёт некоторую гибкость использования данных в Python.
🔥4