Что выведет код с изображения выше?
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
age = 20
print('Возраст пользователя ' + str(age) + 'лет')
В примере выше мы преобразуем данные переменной
age в строку, складываем строку и выводим результат. Как же обстоят дела с типизацией в С? В языке С типизация является статической. Это значит, что типы переменных будут определяться на этапе компиляции. Что же такое, собственно, компиляция? Компиляция - это процесс преобразования исходного кода программы в машинный код или байт-код. Этот процесс выполняет программа-компилятор. В этой статье я покажу вам, как запускать С код. Однако более подробно про компиляцию и компилятор мы поговорим в следующих статьях, а пока давайте вернёмся к типизации языка С. К сожалению, в С нет возможности преобразования типа на лету, но есть удобный способ форматирования данных для вывода в консоль.int age = 20;
printf('Возраст пользователя %d лет', age);
В данном примере мы строго указываем тип при создании переменной, а также указываем спецификатор формата
%d (целое число int типа), который укажет, какой тип данных ожидать при выводе. Функция printf выведет данные в консоль. Я думаю, читатель заметит схожесть вставки данных из переменных с оператором % из Python для форматирования строк. Давайте посмотрим как это выглядит в Python.age: int = 20
print('Возраст пользователя %d лет' % age)
Главным отличием будет то, что в Python при форматировании строки
% произойдёт преобразование типа int в str на лету, в то время как в примере с языком С форматирование происходит для вывода в консоль и при этом тип данных при выводе не меняется. Также, начиная с Python 3.6 и выше, можно указать типы переменных в синтаксисе, если это необходимо для улучшения читаемости кода (как в примере выше). В версии Python 3.5 это делалось в комментарии age = 20 # type: int. Это называется "аннотация типов". После этого вы сможете изменить тип переменной, но некоторые IDE будут подсвечивать вам подсказки при неправильной работе с типом данных. Например, PyCharm поддерживает аннотации. На работу программы это влиять не будет, типизация в Python по-прежнему будет динамической. Однако это будет работать на уровне соглашения между разработчиками. Если в переменной указан тип int, значит, разработчики не будут менять тип данных для переменной.Оба вида типизации обладают своими преимуществами и недостатками. Преимущество динамической типизации в Python над статической в С том, что она позволяет писать код быстрее и делает его более гибким. Почему так?Преобразование типов данных происходит на лету, а сам синтаксис получается короче. Соответственно, C не такой гибкий и быстрый в написании кода. Преимущество статической типизации С над динамической Python в том, что при компиляции программы мы видим все ошибки в коде сразу. Также статическая типизация выигрывает у динамической типизации в скорости выполнения программы из-за того, что при динамической типизации приходится тратить дополнительное время для проверки типов во время выполнения программы.
Собственно говоря, я упоминал о выводе данных в консоль, но как вывести результат нашей программы, написанной на С? Достаточно ли этих двух строк для работы программы?
Для тестирования нашей программы нам необходим компилятор. Я буду использовать компилятор gcc, который уже был предустановлен в моей операционной системе Kubuntu. Посмотрим версию нашего компилятора, чтобы убедиться что он присутствует в операционной системе.
В terminal
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
🔥2👍1
Вывод выше показывает, что у нас установлена версия компилятора 11.4.0. Это означает, что мы можем приступить к сборке программы и увидеть результат? Ответ "да", но только если мы добавим дополнительные строки в нашу программу.
Итак, что мы добавили за строки? Строка
Давайте с вами скомпилируем программу для получения исполняемого файла. Для этого нам нужно, находясь в terminal, перейти в папку с нашим файлом
В terminal
Рассмотрим команду подробнее. Мы говорим
Повторно запустим команду выше и посмотрим на результат. При успешной компиляции мы не видим никакого сообщения в terminal. Но мы видим, что рядом с нашим
В terminal
Поздравляю, мы написали нашу первую программу на C, скомпилировали и запустили её. Моя рубрика сравнивает Python и C, и, раз я затронул в этой статье тему компиляции, в следующей статье я раскрою тему выполнения программ на обоих языках. Вы узнаете, чем компилятор С отличается от интерпретатора Python. Дальше будет ещё интереснее :)
Заключение
1. При объявлении переменной в С мы явно указываем тип в отличие от Python, но зато в Python есть аннотация для типов данных;
2. В отличие от Python язык С не способен преобразовывать типы данных на лету для вставки данных, но есть удобный способ форматирования данных для вывода в консоль;
3. Язык с динамической типизацией быстрее в разработке и код более гибкий;
#include <stdio.h>
int main(){
int age = 20;
printf('Возраст пользователя %d лет', age);
return 0;
}
Итак, что мы добавили за строки? Строка
#include <stdio.h> подключает стандартную библиотеку для С, в которой содержится функционал для ввода/вывода в том числе функция printf, которую мы используем в нашем коде. Функция main является обязательной и в случае успеха возвращает 0 по умолчанию (С99 и новее). Также можно прописывать return 0, что не будет ошибкой, особенно для более старых версий. Я решил оставить вариант с return 0, так как он более универсален. Наш файл с кодом мы должны сохранить в файл с расширением .c. Пусть это будет age_print.c.Давайте с вами скомпилируем программу для получения исполняемого файла. Для этого нам нужно, находясь в terminal, перейти в папку с нашим файлом
age_print.c и дать команду компиляции.В terminal
$ gcc age_print.c -o age_print
age_print.c: In function ‘main’:
age_print.c:5:12: warning: character constant too long for its type
5 | printf('Возраст пользователя %d лет', age);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
age_print.c:5:12: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast [-Wint-conversion]
5 | printf('Возраст пользователя %d лет', age);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| int
In file included from age_print.c:1:
/usr/include/stdio.h:356:43: note: expected ‘const char * restrict’ but argument is of type ‘int’
356 | extern int printf (const char *__restrict __format, ...);
Рассмотрим команду подробнее. Мы говорим
gcc компилятору скомпилировать исходный код с именем age_print.c и создать исполняемый файл age_print. На название файла указывает флаг -o. Под командой в terminal мы видим вывод. Что же это за вывод такой? Это предупреждения отладчика. В нашем случае я специально указал не те кавычки, чтобы показать вам, что произойдёт, когда показываются предупреждения при компиляции. Более подробно о компиляции вы узнаете в следующих статьях. При замене на "" кавычки эти предупреждения пропадут. Исправим наш код и добавим перенос \n строки для более красивого вывода результата работы программы.#include <stdio.h>
int main(){
int age = 20;
printf("Возраст пользователя %d лет\n", age);
return 0;
}
Повторно запустим команду выше и посмотрим на результат. При успешной компиляции мы не видим никакого сообщения в terminal. Но мы видим, что рядом с нашим
age_print.c появился исполняемый файл age_print. Файл появился в результате успешной компиляции без ошибок.В terminal
$ ./age_print
Возраст пользователя 20 лет
Поздравляю, мы написали нашу первую программу на C, скомпилировали и запустили её. Моя рубрика сравнивает Python и C, и, раз я затронул в этой статье тему компиляции, в следующей статье я раскрою тему выполнения программ на обоих языках. Вы узнаете, чем компилятор С отличается от интерпретатора Python. Дальше будет ещё интереснее :)
Заключение
1. При объявлении переменной в С мы явно указываем тип в отличие от Python, но зато в Python есть аннотация для типов данных;
2. В отличие от Python язык С не способен преобразовывать типы данных на лету для вставки данных, но есть удобный способ форматирования данных для вывода в консоль;
3. Язык с динамической типизацией быстрее в разработке и код более гибкий;
🔥3❤1
4. Язык со статической типизацией быстрее выполняется и все ошибки кода видны сразу при компиляции;
5. Посмотрели как скомпилировать код и запустить программу на С.
Файлы к посту, можно получить в боте по коду: 224220
Пост на сайте
Поддержать проект
#Python #Типизация #Сравнение_Python_и_C #Компилятор #C
5. Посмотрели как скомпилировать код и запустить программу на С.
Файлы к посту, можно получить в боте по коду: 224220
Пост на сайте
Поддержать проект
#Python #Типизация #Сравнение_Python_и_C #Компилятор #C
🔥4
Приветствую.
Второго ноября мы объявили о запуске нашего "Тайного Котямбы".
И вот, спустя почти два месяца, мы готовы предоставить отчёт!
Всего в обмене подарками приняло участие 9 человек. Подарки отправили все участники, но, к сожалению, не все на данную минуту смогли их получить. Ввиду погодных условий (в какой-то момент там было очень холодно - под -50) в Иркутск временно не летали самолеты, а когда залетали, в местных почтовых отделениях произошел логистический коллапс. Посылка для Ивана уже добралась до отделения, и на днях он ее получит. Отзыв мы, разумеется, добавим.
А вот посылка для Эльвиры, к сожалению, застряла, но Иван обещал поспособствовать скорейшему разрешению ситуации.
Отчёты в порядке получения:
Отчёт Кристины
Отчёт Насти (РБ)
Отчёт Юрия
Мой отчёт
Отчёт Ильи
Отчёт Антона
Отчёт Насти (МСК)
Отчёт Ивана
Отчёт Эльвиры
Надеюсь, подарочки принесли всем участникам новогоднее настроение и радость.
Давайте в следующем году повторим?
Второго ноября мы объявили о запуске нашего "Тайного Котямбы".
И вот, спустя почти два месяца, мы готовы предоставить отчёт!
Всего в обмене подарками приняло участие 9 человек. Подарки отправили все участники, но, к сожалению, не все на данную минуту смогли их получить. Ввиду погодных условий (в какой-то момент там было очень холодно - под -50) в Иркутск временно не летали самолеты, а когда залетали, в местных почтовых отделениях произошел логистический коллапс. Посылка для Ивана уже добралась до отделения, и на днях он ее получит. Отзыв мы, разумеется, добавим.
А вот посылка для Эльвиры, к сожалению, застряла, но Иван обещал поспособствовать скорейшему разрешению ситуации.
Отчёты в порядке получения:
Отчёт Кристины
Отчёт Насти (РБ)
Отчёт Юрия
Мой отчёт
Отчёт Ильи
Отчёт Антона
Отчёт Насти (МСК)
Отчёт Ивана
Отчёт Эльвиры
Надеюсь, подарочки принесли всем участникам новогоднее настроение и радость.
Давайте в следующем году повторим?
🔥4🎅1
🎄Приветствую и поздравляю всех с наступающим новым годом!🎄
По случаю такого события команда проекта "Код на салфетке" объявляет "Новогодний балаган"!
Общий созвон в нашем уютном чатике "Кот на салфетке", на котором можно будет пообщаться обо всём в свободной форме.
Созвон состоится 31-го декабря в 17:00 по МСК.
Ссылка на чат: https://t.iss.one/pressanybutton_chat
🎄Приходите, мы рады всем!🎄
По случаю такого события команда проекта "Код на салфетке" объявляет "Новогодний балаган"!
Общий созвон в нашем уютном чатике "Кот на салфетке", на котором можно будет пообщаться обо всём в свободной форме.
Созвон состоится 31-го декабря в 17:00 по МСК.
Ссылка на чат: https://t.iss.one/pressanybutton_chat
🎄Приходите, мы рады всем!🎄
🔥6😱1
Приветствую вас, дорогие участники!
Очередная неделя пролетела как МКС вокруг земного шара. Вечер пятницы... Что может быть прекрасней, чем когда наконец можно отложить все дела до понедельника? Ну или хотя бы на пару часов. Ведь именно такая длительность нашего следующего фильма. Заваривайте чаёк и устраивайтесь поудобнее. Погнали!
Фильм: Мятежная Луна, часть 1
Год: 2022
Перевод: Профессиональный, многоголосый
Жестокий тиран угрожает жителям мирной колонии на краю галактики, и их единственной надеждой становится таинственная незнакомка.
Ставьте реакции и делитесь своим мнением. Приятного просмотра
https://www.sspoisk.ru/film/4538172/?utm_referrer=www.google.com
Очередная неделя пролетела как МКС вокруг земного шара. Вечер пятницы... Что может быть прекрасней, чем когда наконец можно отложить все дела до понедельника? Ну или хотя бы на пару часов. Ведь именно такая длительность нашего следующего фильма. Заваривайте чаёк и устраивайтесь поудобнее. Погнали!
Фильм: Мятежная Луна, часть 1
Год: 2022
Перевод: Профессиональный, многоголосый
Жестокий тиран угрожает жителям мирной колонии на краю галактики, и их единственной надеждой становится таинственная незнакомка.
Ставьте реакции и делитесь своим мнением. Приятного просмотра
https://www.sspoisk.ru/film/4538172/?utm_referrer=www.google.com
🔥3
🎅Новогоднее поздравление
Авторы: Иван Ашихмин, Андрей Лебедев
Приветствую.
Вот и закончился 2023 год. Для меня это выдался непростой год. Много учёбы, какие-то проекты, дела, вписывался практически во всё и изрядно так устал) В следующем году пора бы заняться контролем времени, защитить диплом в GB, успешно пройти стажировку и наконец-то начать искать работу)
Хочу пожелать Вам в новом году успехов в учёбе, побольше перспективных проектов, найти или сменить работу на более интересную и конечно же здоровья (особенно ментального, выгорание штука такая, неприятная). И самое главное - не говнокодьте! Пишите красивый, эффективно работающий код и всё будет хорошо!)
Хочу передать слово коллегам:
Андрей Лебедев:
Авторы: Иван Ашихмин, Андрей Лебедев
Приветствую.
Вот и закончился 2023 год. Для меня это выдался непростой год. Много учёбы, какие-то проекты, дела, вписывался практически во всё и изрядно так устал) В следующем году пора бы заняться контролем времени, защитить диплом в GB, успешно пройти стажировку и наконец-то начать искать работу)
Хочу пожелать Вам в новом году успехов в учёбе, побольше перспективных проектов, найти или сменить работу на более интересную и конечно же здоровья (особенно ментального, выгорание штука такая, неприятная). И самое главное - не говнокодьте! Пишите красивый, эффективно работающий код и всё будет хорошо!)
Хочу передать слово коллегам:
Андрей Лебедев:
🔥5❤2
Не знаю, получилось ли у меня в этом году освоить новые знания и стать лучше как разработчик. Иногда кажется, что я забыл больше, чем узнал. Но в этом году я присоединился к замечательной команде канала «Код на салфетке», а также к проекту, который был инициирован моими друзьями. Я благодарен им за эти возможности и за неповторимую атмосферу на канале и во всех связанных с ним чатиках.
Всем нашим читателям и единомышленникам хочу пожелать в новом году приблизиться к достижению своих целей. И помните: как бы сложно ни было, вы не одни. Рядом есть люди, готовые помочь. Нужно только попросить.
Артур Мурзыев:
С Новым Годом, дорогие подписчики! Желаю вам процветания, всех благ в любых аспектах вашей жизни и успехов во всех делах!
Напишите в комментариях как прошёл год для Вас и какие планы на новый 2024 год!
Пост на сайте
Поддержать проект
#Код_на_салфетке #новый_год #поздравление
Всем нашим читателям и единомышленникам хочу пожелать в новом году приблизиться к достижению своих целей. И помните: как бы сложно ни было, вы не одни. Рядом есть люди, готовые помочь. Нужно только попросить.
Артур Мурзыев:
С Новым Годом, дорогие подписчики! Желаю вам процветания, всех благ в любых аспектах вашей жизни и успехов во всех делах!
Напишите в комментариях как прошёл год для Вас и какие планы на новый 2024 год!
Пост на сайте
Поддержать проект
#Код_на_салфетке #новый_год #поздравление
❤4🔥4👍1
Статистика сайта за месяц 04.12-04.01
Автор: Иван Ашихмин
Прошёл ещё месяц и даже сменился год! Что же у нас по статистике сайта?
Статистика продолжает расти! Хотя и была просадка перед новым годом. Доля поисковых запросов растёт, что не может не радовать (всё ещё ищу того, кто поможет с SEO 😭). На втором месте практически нога в ногу идут переходы из Dzen и прямые переходы на сайт по домену. Четвёртое и пятое место делят внутренние переходы и переходы из паблика в VK.
В сухом итоге:
- Посетители: Было 785, стало 1040. Прирост 32.48%.
- Переходы из Dzen: Было 13.1%, стало 8.69%. Падение 33.66%.
- Переходы из Telegram-канала: Было 18%, ушли из топа.
- Переходы из поиска: Было 58.3%, стало 69.4%. Прирост 19.03%.
- Переходы из VK сообщества: Было 0%, стало 3.06%.
Автор: Иван Ашихмин
Прошёл ещё месяц и даже сменился год! Что же у нас по статистике сайта?
Статистика продолжает расти! Хотя и была просадка перед новым годом. Доля поисковых запросов растёт, что не может не радовать (всё ещё ищу того, кто поможет с SEO 😭). На втором месте практически нога в ногу идут переходы из Dzen и прямые переходы на сайт по домену. Четвёртое и пятое место делят внутренние переходы и переходы из паблика в VK.
В сухом итоге:
- Посетители: Было 785, стало 1040. Прирост 32.48%.
- Переходы из Dzen: Было 13.1%, стало 8.69%. Падение 33.66%.
- Переходы из Telegram-канала: Было 18%, ушли из топа.
- Переходы из поиска: Было 58.3%, стало 69.4%. Прирост 19.03%.
- Переходы из VK сообщества: Было 0%, стало 3.06%.
🔥5
Приветствую, друзья!
Вижу, что все живы после новогоднего празднества))
Бывает так, что можно устать просто отдыхая. В голове пустота, а в какой-то момент и вовсе становится скучно. И чтобы скрасить ваш холодный зимний вечер, я иду к вам с фильмом, как Дед Мороз с конфетами)
Не нужны ни стишки, ни песни. Просто расслабьтесь и наслаждайтесь просмотром)
Фильм: Скотт Пилигрим против всех
Год: 2010
Скотт Пилигрим нашел девушку своей мечты. Но на его пути стоит более сложная задача: чтобы встречаться с ней, Скотту придется победить ее семерых бывших и очень злых парней
Приятного просмотра)
https://www.sspoisk.ru/film/105948/?utm_referrer=www.google.com
Вижу, что все живы после новогоднего празднества))
Бывает так, что можно устать просто отдыхая. В голове пустота, а в какой-то момент и вовсе становится скучно. И чтобы скрасить ваш холодный зимний вечер, я иду к вам с фильмом, как Дед Мороз с конфетами)
Не нужны ни стишки, ни песни. Просто расслабьтесь и наслаждайтесь просмотром)
Фильм: Скотт Пилигрим против всех
Год: 2010
Скотт Пилигрим нашел девушку своей мечты. Но на его пути стоит более сложная задача: чтобы встречаться с ней, Скотту придется победить ее семерых бывших и очень злых парней
Приятного просмотра)
https://www.sspoisk.ru/film/105948/?utm_referrer=www.google.com
🔥2
Django 39. Капча и подтверждение регистрации по email
Автор: Иван Ашихмин
Когда мы с вами работали над формой регистрации, мы упустили одну важную деталь – ботов. Если ничего не предпринимать, боты будут регистрироваться на сайте в большом количестве, нагружая базу данных заведомо неактуальной информацией, плодя спам в комментариях под постами и доставляя немало других мелких и не очень неприятностей.
Для решения этой проблемы мы сделаем следующее:
1. Добавим на страницу регистрации Google reCAPTCHA, для явной защиты от ботов и массовых регистраций. Да, это не самая лучшая защита от ботов, но 99% всё же будут отсеяны.
2. Добавим обязательное подтверждение регистрации по email. Этим мы отсеем оставшихся ботов.
Google reCAPTCHA.
Для того, чтобы у нас был доступ к сервису reCAPTCHA, необходимо получить ключи доступа.
Автор: Иван Ашихмин
Когда мы с вами работали над формой регистрации, мы упустили одну важную деталь – ботов. Если ничего не предпринимать, боты будут регистрироваться на сайте в большом количестве, нагружая базу данных заведомо неактуальной информацией, плодя спам в комментариях под постами и доставляя немало других мелких и не очень неприятностей.
Для решения этой проблемы мы сделаем следующее:
1. Добавим на страницу регистрации Google reCAPTCHA, для явной защиты от ботов и массовых регистраций. Да, это не самая лучшая защита от ботов, но 99% всё же будут отсеяны.
2. Добавим обязательное подтверждение регистрации по email. Этим мы отсеем оставшихся ботов.
Google reCAPTCHA.
Для того, чтобы у нас был доступ к сервису reCAPTCHA, необходимо получить ключи доступа.
Откроем сайт и авторизуемся: https://www.google.com/recaptcha/admin/
Выбираем "Создать" и заполняем три поля:
- Ярлык - вводим название сайта.
- Тип reCAPTCHA - выбираем "С помощью заданий (v2)".
- Домены - вводим домен без
Нажимаем кнопку "Отправить". Откроется страница с ключами доступа, не закрывайте её, далее они нам понадобятся.
Django reCAPTCHA.
Для добавления капчи на страницу регистрации мы воспользуемся готовой библиотекой
Установим библиотеку, выполнив команду:
Также добавим
Откроем файл
Там же создадим два параметра настроек:
-
-
Добавляем поле в форму.
Откроем файл
Добавим в него всего одно поле:
Этого достаточно. Теперь, открыв страницу регистрации, мы увидим новое поле с капчей.
Если вам нужны расширенные настройки, то документация к библиотеке доступна по ссылке: https://github.com/django-recaptcha/django-recaptcha
Подтверждение регистрации по email.
Для реализации отправки письма с подтверждением регистрации нам потребуется:
- Написать класс отправки письма. Это же будет заготовкой для внедрения фоновой отправки писем используя Celery.
- Изменить наш класс представления для регистрации и добавить два новых.
- Сделать три простых шаблона.
Обратите внимание, что для работы отправки почты необходимо прописать почтовый сервер в файле
Класс отправки почты.
Начнём с класса, реализующего методы для отправки почты. Это также будет являться заготовкой для внедрения Celery, позволяющего выполнять задачи в фоне, но об этом в другой раз.
В директории приложения
Создадим класс
Первым методом будет конструктор класса
В нём будет четыре поля:
-
-
-
-
Вторым методом будет
В нём создадим три переменные:
Выбираем "Создать" и заполняем три поля:
- Ярлык - вводим название сайта.
- Тип reCAPTCHA - выбираем "С помощью заданий (v2)".
- Домены - вводим домен без
https или www.Нажимаем кнопку "Отправить". Откроется страница с ключами доступа, не закрывайте её, далее они нам понадобятся.
Django reCAPTCHA.
Для добавления капчи на страницу регистрации мы воспользуемся готовой библиотекой
Django reCAPTCHA.Установим библиотеку, выполнив команду:
pip install django-recaptcha
Также добавим
django-recaptcha4.0.0 в requirements.txt.Откроем файл
settings.py и добавим django_recaptcha в INSTALLED_APPS.Там же создадим два параметра настроек:
-
RECAPTCHA_PUBLIC_KEY - в этом параметре указываем первый полученный на сайте ключ "Ключ сайта".-
RECAPTCHA_PRIVATE_KEY - в этом параметре указываем второй полученный на сайте ключ "Секретный ключ".RECAPTCHA_PUBLIC_KEY = ""
RECAPTCHA_PRIVATE_KEY = ""
Добавляем поле в форму.
Откроем файл
forms.py в директории приложения user_app и найдём наш класс RegistrationForm.Добавим в него всего одно поле:
from django_recaptcha.fields import ReCaptchaField
captcha = ReCaptchaField()
Этого достаточно. Теперь, открыв страницу регистрации, мы увидим новое поле с капчей.
Если вам нужны расширенные настройки, то документация к библиотеке доступна по ссылке: https://github.com/django-recaptcha/django-recaptcha
Подтверждение регистрации по email.
Для реализации отправки письма с подтверждением регистрации нам потребуется:
- Написать класс отправки письма. Это же будет заготовкой для внедрения фоновой отправки писем используя Celery.
- Изменить наш класс представления для регистрации и добавить два новых.
- Сделать три простых шаблона.
Обратите внимание, что для работы отправки почты необходимо прописать почтовый сервер в файле
settings.py. Подробнее об этом можно прочитать в посте "Django 12. Настройка отправки почты"Класс отправки почты.
Начнём с класса, реализующего методы для отправки почты. Это также будет являться заготовкой для внедрения Celery, позволяющего выполнять задачи в фоне, но об этом в другой раз.
В директории приложения
user_app создадим новый файл tasks.py.Создадим класс
SendEmail.Первым методом будет конструктор класса
__init__, принимающий аргумент user.В нём будет четыре поля:
-
self.user - в него помещаем переданный в конструктор аргумент user. Это объект пользователя.-
self.current_site - в него из класса Sites получаем домен сайта.-
self.token - используя функцию default_token_generator и метод make_token, на основе пользователя создаём токен.-
self.uid - в этом поле мы кодируем числовое значение id пользователя в base64.class SendEmail:
def __init__(self, user: User):
self.user = user
self.current_site = Site.objects.get_current().domain
self.token = default_token_generator.make_token(self.user)
self.uid = urlsafe_base64_encode(str(self.user.pk).encode())
Вторым методом будет
send_activate_email.В нём создадим три переменные:
👍2
-
-
-
Ниже, обращаясь ко встроенному в класс пользователя методу
Полный код класса:
Под классом создаём функцию
Именно эту функцию будем вызывать из представления для отправки почты, а в будущем она будет выполняться как задача Celery.
В функции в переменную
Следующей строчкой вызываем метод
Изменение представления регистрации.
Откроем файл
В нём нам необходимо изменить метод
Изначально он был такой:
В этом методе, сохраняем зарегистрированного пользователя.
Теперь же нам необходимо его доработать, а именно:
- В модели пользователя есть поле
- Вызвать функцию отправки почты, передав в неё объект зарегистрированного пользователя.
reset_password_url - в ней, используя функцию reverse_lazy, получаем путь для активации, передавая uid и token.-
subject - в ней создаём строку с текстом для темы письма.-
message - в ней создаём строку с телом письма. Позволяется использовать HTML-разметку, также используя функцию render_to_string, можно создать HTML-файл с шаблоном письма и отправлять его.Ниже, обращаясь ко встроенному в класс пользователя методу
email_user, отправляем ему письмо, которое передаёт в него тему и сообщение.def send_activate_email(self):
reset_password_url = reverse_lazy(
"user_app:signup_confirm", kwargs={"uidb64": self.uid, "token": self.token}
)
subject = f"Активация аккаунта на сайте {self.current_site}"
message = (
f"Благодарим за регистрацию на сайте {self.current_site}.\n"
"Для активации учётной записи, пожалуйста перейдите по ссылке:\n"
f"https://{self.current_site}{reset_password_url}\n"
)
self.user.email_user(subject=subject, message=message)
Полный код класса:
from django.contrib.auth.models import User
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import Site
from django.urls import reverse_lazy
from django.utils.http import urlsafe_base64_encode
class SendEmail:
def __init__(self, user: User):
self.user = user
self.current_site = Site.objects.get_current().domain
self.token = default_token_generator.make_token(self.user)
self.uid = urlsafe_base64_encode(str(self.user.pk).encode())
def send_activate_email(self):
reset_password_url = reverse_lazy(
"user_app:signup_confirm", kwargs={"uidb64": self.uid, "token": self.token}
)
subject = f"Активация аккаунта на сайте {self.current_site}"
message = (
f"Благодарим за регистрацию на сайте {self.current_site}.\n"
"Для активации учётной записи, пожалуйста перейдите по ссылке:\n"
f"https://{self.current_site}{reset_password_url}\n"
)
self.user.email_user(subject=subject, message=message)
Под классом создаём функцию
activate_email_task, принимающую пользователя в аргументах.Именно эту функцию будем вызывать из представления для отправки почты, а в будущем она будет выполняться как задача Celery.
В функции в переменную
send_email помещаем экземпляр класса SendEmail.Следующей строчкой вызываем метод
send_activate_email.def activate_email_task(user: User):
send_email = SendEmail(user=user)
send_email.send_activate_email()
Изменение представления регистрации.
Откроем файл
views.py в директории приложения user_app и найдём наш класс для регистрации CustomRegistrationView.В нём нам необходимо изменить метод
form_valid.Изначально он был такой:
def form_valid(self, form):
form.save()
return super().form_valid(form)
В этом методе, сохраняем зарегистрированного пользователя.
Теперь же нам необходимо его доработать, а именно:
- В модели пользователя есть поле
is_active, определяющее активирован/активен пользователь или нет. По умолчанию он установлен в True, нам необходимо после регистрации присваивать False.- Вызвать функцию отправки почты, передав в неё объект зарегистрированного пользователя.
И тут проявляется забавная особенность. Если оставить
1. Регистрация пользователя - первое время изменения.
2. Деактивация пользователя и сохранение - второе время изменения.
3. Отправка письма с токеном на основе времени изменения из пункта 2.
4. Вызов
Проще говоря, пользователю приходит невалидная ссылка для активации аккаунта. Это решается изменением последнего шага на
Код метода:
Также необходимо изменить метод
Представление страницы активации и завершения регистрации.
Сделаем два представления:
1. На первое будем перенаправлять после регистрации. Оно будет просто выводить страницу с текстом, не более.
2. На второе пользователь будет попадать перейдя по ссылке из письма. В нём будет логика валидации токена и активации пользователя.
Начнём с первого представления. Создадим класс
В нём пропишем два поля:
1.
2.
Код представления:
Второе представление будет объёмнее. Создадим класс
В классе создаём метод
В начале идёт блок
Если декодировать не удастся или пользователь с таким
Далее блок
Если условие выполняется:
- Активируем пользователя, установив значение
- Сохраняем пользователя.
- Используя функцию
- Возвращаем работу функции
Если условие не было выполнено:
- Возвращаем работу функции
Код представления:
return super().form_valid(form) в конце метода, то активация работать не будет. Оказывается, генерация токена берёт за основу время изменения пользователя и получается следующая последовательность:1. Регистрация пользователя - первое время изменения.
2. Деактивация пользователя и сохранение - второе время изменения.
3. Отправка письма с токеном на основе времени изменения из пункта 2.
4. Вызов
super().form_valid(form), который в свою очередь тоже сохраняет объект - третье время изменения.Проще говоря, пользователю приходит невалидная ссылка для активации аккаунта. Это решается изменением последнего шага на
return HttpResponseRedirect(self.get_success_url()).Код метода:
def form_valid(self, form):
user: User = form.save()
user.is_active = False
user.save()
activate_email_task(user)
return HttpResponseRedirect(self.get_success_url())
Также необходимо изменить метод
get_success_url, а именно изменить маршрут на страницу после регистрации с сообщением об отправке почты.def get_success_url(self):
return reverse_lazy("user_app:signup_done")
Представление страницы активации и завершения регистрации.
Сделаем два представления:
1. На первое будем перенаправлять после регистрации. Оно будет просто выводить страницу с текстом, не более.
2. На второе пользователь будет попадать перейдя по ссылке из письма. В нём будет логика валидации токена и активации пользователя.
Начнём с первого представления. Создадим класс
CustomRegistrationDoneView, унаследованный от TemplateView.В нём пропишем два поля:
1.
template_name - файл шаблона страницы.2.
extra_context - заголовок страницы.Код представления:
class CustomRegistrationDoneView(TemplateView):
template_name = "user_app/signup_done.html"
extra_context = {"title": "Регистрация завершена, активируйте учётную запись."}
Второе представление будет объёмнее. Создадим класс
CustomRegistrationConfirmView, унаследованный от базового класса View.В классе создаём метод
get, принимающий в агрументах request, uidb64, token.В начале идёт блок
try-except, в котором мы декодируем uidb64 в int и получаем соответствующего пользователя в переменную user.Если декодировать не удастся или пользователь с таким
id не будет найден, то поднимется исключение, в обработке которого в переменную user присваиваем None.Далее блок
if-else. В условии проверяем, что user не является None и, что токен соответствует пользователю. Если условие выполняется:
- Активируем пользователя, установив значение
is_active, равное True.- Сохраняем пользователя.
- Используя функцию
login, авторизуем пользователя, передавая в функцию request и user.- Возвращаем работу функции
render, передав в неё request, путь до шаблона, уведомляющего об успешной активации, и контекст, содержащий заголовок страницы.Если условие не было выполнено:
- Возвращаем работу функции
render, передав в неё request, путь до шаблона, уведомляющего об ошибке активации, и контекст, содержащий заголовок страницы.Код представления: