Что выведет код с картинки выше?
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% ответили, что будет выведена ошибка
Код задачи:
Для начала небольшое теоретическое вступление.
В посте об изменяемых и неизменяемых типах данных мы говорили о том, что ячейку, которая хранит неизменяемый объект (в частности кортеж), нельзя «вскрыть», чтобы изменить сам этот объект. На самом деле в реальности всё несколько сложнее. Кортеж мы действительно не можем изменить (например, добавить, удалить или, что особенно для нас важно, – переписать) элемент. Однако если кортеж внутри себя содержит изменяемые типы данных, например, списки, то сами эти списки могут изменяться.
Например, список
Ситуация с оператором += сложнее.
Сам по себе этот оператор - «синтаксический сахар» (то есть простая и удобная запись) для вызова следующего алгоритма действий:
1️⃣ Сначала вызывается метод
2️⃣ Перезаписыват результат слияния в ту же самую ячейку, где хранился изначальный список.
В коде это выглядело бы так:
И
Вооружившись этим знанием, посмотрим, как сработает код из нашей вчерашней задачи:
1️⃣ Записываем в
2️⃣ Под
3️⃣ Срабатывает обработка и мы перемещаемся в
4️⃣ Весь кортеж выводится на печать:
Вот такой вот неожиданный ответ. Надеемся, вы почерпнули для себя что-то новое из этой задачки и ее объяснения. А если нет, то освежили свои знания. Приглашаем поделиться впечатлениями в комментариях.
Общее количество ответов на данный момент 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 человек. Кому-то это число может показаться небольшим, но среди нас уже наверняка есть люди, которые не знают, с чего или, точнее, с кого началось и на ком держится всё наше небольшое сообщество. Возможно, есть и те, кто просто не помнит таких вещей, потому что люди тут в том числе собрались пожилые. Первым - расскажем, а вторым - напомним, потому что старость надо уважать.
Автор: Андрей Лебедев
Наш канал, созданный не так уж давно, на сегодняшний день объединяет уже 288 человек. Кому-то это число может показаться небольшим, но среди нас уже наверняка есть люди, которые не знают, с чего или, точнее, с кого началось и на ком держится всё наше небольшое сообщество. Возможно, есть и те, кто просто не помнит таких вещей, потому что люди тут в том числе собрались пожилые. Первым - расскажем, а вторым - напомним, потому что старость надо уважать.
🔥6😱2
А началось всё с того, что один умный, инициативный, любознательный и во всех отношениях приятный мужчина в самом расцвете лет решил поделиться своими знаниями со всеми желающими, для чего создал канал, написал (и продолжает писать) сайт, запустил самый живой из всех известных мне чатов на 50 человек, объединив десятки людей с очень разной биографией из всех уголков России, а потом как будто растворился, превратившись в админа и спрятавшись за фотографией кота на салфетке. С тех пор каждое утро кот возвещает в чате о том, что “проснувся”, а затем без устали отвечает на любые вопросы - технического и образовательного характера, про мемы, музыку, отношения типа “Госпожа/миньоны” и, конечно же, про рыжих женщин и шарообразных дизайнеров. Параллельно кот учится в нескольких местах, пишет дипломы, запускает проекты, участвует в стажировках, проходит технические собеседования в IT-компаниях и обо всём этом честно докладывает всем собравшимся.
Сегодня у нашего Вани (я бы так кота, конечно, не назвал) день рождения! Я от всей души поздравляю нашего @proDreams и желаю ему просто оставаться собой, потому что нет никаких сомнений в том, что все личностные настройки у Вани уже правильно подобраны и подкручены так, чтобы состояться и как разработчик, и как просто хороший человек. Желаю только попутного ветра и чтобы никакие внешние обстоятельства все эти настройки не сбивали. Спасибо, Ваня, что ты такой и что ты у нас есть!
Передаю слово другим поздравляющим:
Поздравляю с днем рождения и желаю, чтобы в твоём кодe жизни не было никаких багов, чтобы твоим проектам не страшны были никакие вирусы, чтобы твоя деятельность была запрограммирована только на успех, чтобы твое сердце было запаролено от любых разочарований и всякого негатива. Чтобы скрипты твоего здоровья всегда выполняли свои функции. Чтобы база данных твоих знаний никогда не устаревала и всегда только обновлялась. И чтобы список твоих доходов тянул на миллионы строк.
Артур
***
Что ж, воот и твой день рождения настал, поздравляю тебя с ним, старик) я благодарю тебя за все те помощи что ты мне давал, даже в долг без процентов на видеокарту 😁 научил многому, как ставить винду, даж родители говорили «смотри давай и учись» хввхха как вы пьяные с Яриком у меня дома хавали и винду ставили очередной раз 🤣 жди меня с пивком, с тебя покушатс, с твоим днём Sooshkoo
Анатолий
***
Хочу пожелать тебе, чтобы этот день стал особенным!
Ты замечательный! Ты молодец! Ты стараешься! У тебя обязательно все получится и ты добьешься всего, что тебе хочется!!! Пусть твой канал растет и развивается, привлекая новых участников и авторов! Ты собрал вокруг себя прекрасных людей! Успехов в стажировке и карьерного роста! Пусть все твои мечты сбываются!!! Будь счастлив и спасибо за всё, что ты делаешь!!!
Кристина
***
Привет Кот!
Сегодня твой день рождения, и мы не можем пропустить возможность поздравить тебя :
*️⃣ Крутяцкого босса Некого проекта
*️⃣ Автора кучи умных постов
*️⃣ И создателя этого сумасшедшего чатика
Желаем тебе огромного количества баг-фри кода, интересных проектов, звания сеньора и возможности удаленной работы где- нибудь на Мальдивах за очень очень большую зарплату)).
Ну а так как я не умею сама писать нормально поздравления ваша задача оставить как можно больше поздравляшек в комментариях
Ps: Тык в кнопку поддержать канал приветствуется.
Анастасия
***
Остальных приглашаем присоединиться к поздравлениям в комментариях!
Пост на сайте
Поддержать проект
#Код_на_салфетке #День_рождения #Ваня
Сегодня у нашего Вани (я бы так кота, конечно, не назвал) день рождения! Я от всей души поздравляю нашего @proDreams и желаю ему просто оставаться собой, потому что нет никаких сомнений в том, что все личностные настройки у Вани уже правильно подобраны и подкручены так, чтобы состояться и как разработчик, и как просто хороший человек. Желаю только попутного ветра и чтобы никакие внешние обстоятельства все эти настройки не сбивали. Спасибо, Ваня, что ты такой и что ты у нас есть!
Передаю слово другим поздравляющим:
Поздравляю с днем рождения и желаю, чтобы в твоём кодe жизни не было никаких багов, чтобы твоим проектам не страшны были никакие вирусы, чтобы твоя деятельность была запрограммирована только на успех, чтобы твое сердце было запаролено от любых разочарований и всякого негатива. Чтобы скрипты твоего здоровья всегда выполняли свои функции. Чтобы база данных твоих знаний никогда не устаревала и всегда только обновлялась. И чтобы список твоих доходов тянул на миллионы строк.
Артур
***
Что ж, воот и твой день рождения настал, поздравляю тебя с ним, старик) я благодарю тебя за все те помощи что ты мне давал, даже в долг без процентов на видеокарту 😁 научил многому, как ставить винду, даж родители говорили «смотри давай и учись» хввхха как вы пьяные с Яриком у меня дома хавали и винду ставили очередной раз 🤣 жди меня с пивком, с тебя покушатс, с твоим днём Sooshkoo
Анатолий
***
Хочу пожелать тебе, чтобы этот день стал особенным!
Ты замечательный! Ты молодец! Ты стараешься! У тебя обязательно все получится и ты добьешься всего, что тебе хочется!!! Пусть твой канал растет и развивается, привлекая новых участников и авторов! Ты собрал вокруг себя прекрасных людей! Успехов в стажировке и карьерного роста! Пусть все твои мечты сбываются!!! Будь счастлив и спасибо за всё, что ты делаешь!!!
Кристина
***
Привет Кот!
Сегодня твой день рождения, и мы не можем пропустить возможность поздравить тебя :
*️⃣ Крутяцкого босса Некого проекта
*️⃣ Автора кучи умных постов
*️⃣ И создателя этого сумасшедшего чатика
Желаем тебе огромного количества баг-фри кода, интересных проектов, звания сеньора и возможности удаленной работы где- нибудь на Мальдивах за очень очень большую зарплату)).
Ну а так как я не умею сама писать нормально поздравления ваша задача оставить как можно больше поздравляшек в комментариях
Ps: Тык в кнопку поддержать канал приветствуется.
Анастасия
***
Остальных приглашаем присоединиться к поздравлениям в комментариях!
Пост на сайте
Поддержать проект
#Код_на_салфетке #День_рождения #Ваня
❤4🔥3👍2😱1
Django 38.1. Кабинет и все посты автора
Автор: Иван Ашихмин
Мы с вами реализовали практически всё, что необходимо авторам, за исключением одного важного момента - личного кабинета автора.
Этот пост будет разделён на несколько частей, в которых мы разберёмся:
- Как использовать в одном представлении несколько шаблонов при помощи декоратора.
- Как контролировать права доступа при помощи миксинов.
- Используем наследования представлений и шаблонов.
- Добавим страницы редактирования/удаления постов автором.- Перенесём функционал черновиков на сторону сайта.
И начнём мы со страницы кабинета автора. Это будет та же страница, что и профиль автора, но расширенная.
Автор: Иван Ашихмин
Мы с вами реализовали практически всё, что необходимо авторам, за исключением одного важного момента - личного кабинета автора.
Этот пост будет разделён на несколько частей, в которых мы разберёмся:
- Как использовать в одном представлении несколько шаблонов при помощи декоратора.
- Как контролировать права доступа при помощи миксинов.
- Используем наследования представлений и шаблонов.
- Добавим страницы редактирования/удаления постов автором.- Перенесём функционал черновиков на сторону сайта.
И начнём мы со страницы кабинета автора. Это будет та же страница, что и профиль автора, но расширенная.
🔥2😱1
В этом посте мы напишем собственный декоратор. Если вы ещё не сталкивались с декораторами или мало знаете о них, то настоятельно рекомендую прочитать пост "Декораторы в питоне".
Представление страницы автора.
Откроем файл
В нём у нас есть простенькое представление для профиля пользователя -
Добавим следующие поля:
-
-
-
Поля класса:
Помните про метод
Код:
В коде выше к методу
Последнее, что у нас осталось, – это переопределённый метод
В этом методе нам необходимо добавить всего три строки:
1. Передачу в шаблон значение поля
2. Блок
3. Если значение
Дополнительные строки:
Полный код изменённого представления:
Представление страницы автора.
Откроем файл
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
Аналогия примерно та же, что и с шаблоном кабинета, поскольку посты получаем на этапе представления для автора или пользователя, нет необходимости делать блоки с постами.
Вместо этого мы добавим блоки с кнопками и отображением статуса поста – черновик или опубликован.
В шаблоне
И блок со статусом:
Код шаблона:
Создаём новый файл
Заключение.
В коде шаблонов уже добавлены ссылки на редактирование и удаление поста автором, они пока не рабочие и, вероятно, вызовут ошибку при переходе на страницу. Про реализацию функционала редактирования и удаления я расскажу в следующем посте.
Файлы к посту, можно получить в боте по коду: 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