В Python 3.10 произошли большие изменения, из всего, что я точно беру себе это match и улучшенную проверку типов.
def get_color(color: str) -> str:
match color:
case "black":
return "#fffff"
case "white":
return "#00000"
case _: #default
return "#green"
Проверка типов в Python 3.10
# Новый способ проверки типов
def some(value: int | float) -> int | float:
return value
# Старый способ
def some(value: Union[int, float]) -> Union[int, float]:
return value
В productiuon использую только в одном проекте, так как на хостинге из коробки ещё нет доступного Python 3.10, пришлось собрать из исходников. Не везде есть такая возможность. Ещё на всех рабочих машинах пришлось поставить Python 3.10.
В общем пользоваться можно, это примерно то же, что и при переходе на Python 3.6 из-за async, начав использовать новые фичи теряешь обратную совместимость.
Раньше я использовал активно связку jQuery для отправки Ajax запроса на сервер. Например так обрабатывал форму обратной связи или онлайн заказ товара.
Время не стоит на месте с использованием библиотеки VueJS я познакомился с библиотекой axios. И стал использовать не только в проектах VueJS но и просто с Flask и Django.
Flask + axios
Напишу подробно, с начало создаю виртуальное окружение, затем ставлю Flask.
Далее создаю директорию templates и шаблоны base.html, index.html, файл проекта app.py и дополнительные файлы app.js в директории static.
Создаю переменную окружения FLASK_ENV=development для того, что бы запускать проект в режиме отладки (DEBUG). В данном режиме приложение будет автоматический производить рестарт при изменении исходного кода.
Ещё одна переменная окружения FLASK_APP=app.py приложение Flask, запуск по команде flask run.
from flask import Flask
from flask import render_template
import json
# форма для обратной сзвязи
from forms import CallBackForm
app = Flask(__name__)
SECRET_KEY = '12345'
app.config['SECRET_KEY'] = SECRET_KEY
@app.route("/")
def index():
form = CallBackForm()
return render_template('index.html',
title="Пример отправки",
form=form)
@app.route("/callback", methods=['POST'])
def callback():
form = CallBackForm()
if form.validate_on_submit():
# функции отправить почту, записать в БД и т. д.
return json.dumps({'success': 'true', 'msg': 'Ждите звонка!'})
else:
# обработать ошибку
return json.dumps({'success': 'false', 'msg': 'Ошибка на сервере!'})
В строке 20 происходит обработка нажатия на кнопку (index.html строка 7).
Благодаря использованию формы, происходит нормальная валидация и недоступные значения успешно обрабатываются.
Форма
Файл forms.py
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
from wtforms.validators import Length
class CallBackForm(FlaskForm):
name = StringField('Имя', validators=[DataRequired()])
phone = StringField('Телефон', validators=[DataRequired(), Length(min=4)])
В сроке 9 параметр Length(min=4) указывает, если количество знаков меньше 4, валидация не пройдёт.
/* переопределить поведение кнопки "Отправить" */
$(document).ready(function () {
$("#form1").submit(function( event ) {
CallBackForm("form1", "msg");
event.preventDefault();
});
});
/* отправка данных на сервер по AJAX */
function CallBackForm(form, msg){
let from = new FormData();
from.append('name', $( "#name" ).val());
from.append('phone', $( "#phone" ).val());
from.append('csrf_token', $( "#csrf_token" ).val());
axios({
url: '/callback',
method: 'post',
data: from,
})
.then(function (response) {
$( '#' + msg ).html(response.data.msg);
if (response.data.success == 'true') {
$( "#" + form ).trigger('reset');
}
else
{
alert("Что-то пошло не так!");
console.log("Ошибка");
}
})
.catch(function (error) {
console.log(error);
});
};
В строках 12-14 добавляются данные из формы для отправки. Строка 21 записывает ответ от сервера (файл app.py строка 25 или 28) в зависимости от правильности ввода.
Бывает необходимо установить пакеты для python на машине без интернета. Способов много, я выбрал для себя один, его и использую. «Легко» и «просто» можно скачать необходимые пакеты вместе с зависимостями и установить на другой машине, если сделать следующие.
На машине с интернетом
python3 -m venv vevn
source venv/bin/active
pip install pip --upgrade
# директория для скачивания пакетов
mkdir pkg
cd pkg
# отдельно скачиваю последнею версию pip
pip download pip
# скачиваю необходимые пакеты с зависимостями
pip download -r ../requirements.txt
На машине без интернета
python3 -m venv vevn
source venv/bin/active
# устанавливаю ранее скаченный pip (версия может быть другая)
pip install pkg/pip-20.1-py2.py3-none-any.whl
# установка пакетов из списка requirements.txt, пакеты должны лежать в pkg (директория)
pip install --no-index --find-links pkg -r requirements.txt
Результат выполнения
# вывод консоли у меня
pip install --no-index --find-links pkg -r requirements.txt
Looking in links: pkg
Processing ./pkg/Flask-1.1.2-py2.py3-none-any.whl
Processing ./pkg/Flask_WTF-0.14.3-py2.py3-none-any.whl
Processing ./pkg/et_xmlfile-1.0.1.tar.gz
Processing ./pkg/openpyxl-3.0.3.tar.gz
Processing ./pkg/jdcal-1.4.1-py2.py3-none-any.whl
Processing ./pkg/pylint-2.5.2-py3-none-any.whl
Processing ./pkg/itsdangerous-1.1.0-py2.py3-none-any.whl
Processing ./pkg/Jinja2-2.11.2-py2.py3-none-any.whl
Processing ./pkg/Werkzeug-1.0.1-py2.py3-none-any.whl
Processing ./pkg/click-7.1.2-py2.py3-none-any.whl
Processing ./pkg/WTForms-2.3.1-py2.py3-none-any.whl
Processing ./pkg/mccabe-0.6.1-py2.py3-none-any.whl
Processing ./pkg/isort-4.3.21-py2.py3-none-any.whl
Processing ./pkg/toml-0.10.1-py2.py3-none-any.whl
Processing ./pkg/astroid-2.4.1-py3-none-any.whl
Processing ./pkg/MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl
Processing ./pkg/typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl
Processing ./pkg/wrapt-1.12.1.tar.gz
Processing ./pkg/lazy_object_proxy-1.4.3-cp37-cp37m-manylinux1_x86_64.whl
Processing ./pkg/six-1.14.0-py2.py3-none-any.whl
Could not build wheels for et-xmlfile, since package 'wheel' is not installed.
Could not build wheels for openpyxl, since package 'wheel' is not installed.
Could not build wheels for wrapt, since package 'wheel' is not installed.
Installing collected packages: itsdangerous, MarkupSafe, Jinja2, Werkzeug, click, Flask, WTForms, Flask-WTF, et-xmlfile, jdcal, openpyxl, mccabe, isort, toml, typed-ast, wrapt, lazy-object-proxy, six, astroid, pylint
Running setup.py install for et-xmlfile ... done
Running setup.py install for openpyxl ... done
Running setup.py install for wrapt ... done
Successfully installed Flask-1.1.2 Flask-WTF-0.14.3 Jinja2-2.11.2 MarkupSafe-1.1.1 WTForms-2.3.1 Werkzeug-1.0.1 astroid-2.4.1 click-7.1.2 et-xmlfile-1.0.1 isort-4.3.21 itsdangerous-1.1.0 jdcal-1.4.1 lazy-object-proxy-1.4.3 mccabe-0.6.1 openpyxl-3.0.3 pylint-2.5.2 six-1.14.0 toml-0.10.1 typed-ast-1.4.1 wrapt-1.12.1
Вездесущий спам — читал где то, что одно время 80% почтового трафика это был SPAM.
Сейчас уже почтового спама не так много доходит до нас (просто средства борьбы с ним стали лучше).
Я столкнулся со спамом в формах обратной связи на сайтах, да и вообще в формах которые на сайтах позволяют писать и отправлять сообщения.
С годами, образовалось большое количество сайтов, и клиенты стали жаловаться на спам, конечно при разработке были сделаны простые защиты (скрытое поле и/или 2+2 и т. д. и т. п.) так же был опыт использования reCAPTCHA v2 — но были свои нюансы.
reCAPTCHA v2 считаю, что это хорошее решение. Да и ещё оно работает из коробки с flask-wtf примерно с 2017 года. Раньше нужно было «колхозить».
Решил написать для себя простой шаблон, ничего лишнего, форма обратной связи в контактах сайта защищенная reCAPTCHA. Мне легче использовать такую выжимку из проекта, чем копаться в большом готовом проекте с кучей библиотек. Здесь нечего лишнего, бери и используй.
Что понадобиться?
Ключи от google публичный и приватный. (Я создал для хоста 127.0.0.1 возможно для теста подойдут они. А так нужно при создании указывать домен на котором будет сайт с капчей.)
Содержимое файла index.html (в директории templates). Строка 1 указывает какой шаблон будет расширен текущим. Строка 5 будет содержать сообщение об успехе или провале валидации. Собственно в 11 строке содержится recaptcha.
Следующий файл forms.py (корень проекта). Строка 8 содержит поле комментарий DataRequired обозначает обязательно для заполнения. Строка 9 recaptcha поле с «галочкой».
from flask_wtf import FlaskForm
from flask_wtf import RecaptchaField
from wtforms import TextField
from wtforms.validators import DataRequired
class ContactForm(FlaskForm):
text = TextField('Комментарий', validators=[DataRequired()])
recaptcha = RecaptchaField()
И наконец самый главный файл приложения app.py (корень проекта).
Строка 5 — ранее описанная форма.
Строка 10, 11 — ключи которые нужно получить в google используя аккаунт google.
Строка 16 — форма для передачи в шаблон и проверки валидности.
Строка 18 — если метод POST значит была нажата в форме кнопка submit и соответственно нужно проверить полученные данные на валидность.
Строка 19 — проверка данных на валидность.
Если валидация пройдена, то msg примет значение «Успех!» иначе «Ошибка валидации».
from flask import Flask
from flask import request
from flask import render_template
# форма с валидацией и капчей
from forms import ContactForm
app = Flask(__name__)
app.config['SECRET_KEY'] = "12345"
# ключи recaptcha от google
app.config['RECAPTCHA_PUBLIC_KEY'] = "6Ld74-oUAAAAAJC0UOY6PtrOrNcxQ2VQCfGAqBOC"
app.config['RECAPTCHA_PRIVATE_KEY'] = "6Ld74-oUAAAAAD2_Jl2IVKh2uCCI9OPX_7oTdLz4"
@app.route('/', methods=['GET', 'POST'])
def index():
form = ContactForm()
msg = ""
if request.method == "POST":
if form.validate_on_submit():
msg="Успех!"
# отправить почту, записать в БД
else:
msg="Ошибка валидации"
# обработать ошибку
return render_template("index.html",
title="index page",
form=form,
msg=msg)
if __name__ == '__main__':
app.run(debug=True)
Запуск проекта:
python3 app.py
В видео видно, что если ошибиться несколько раз, будет показана капча и просто «галочкой» уже не отделаться.
В заключении
Я расписал подробно шаги для того, что бы запомнить самому, делать всё на автомате. В будущем буду экономить время и не лениться добавлять валлидацию там где нужно. Это не переводная статья и не куски документации один к одному — это то что я использую в своих проектах.
В новой версии flask-wtf появиться параметр RECAPTCHA_DISABLE который позволит красиво отключать и включать проверку капчи.
А как быть, если нужно отключить проверку капчи сейчас?
Вообще это одна из проблем, почему мне не нравилось использовал данную капчу, решение пока у меня такое, создавать две одинаковые формы с reCAPTCHA и без. Пример кода ниже forms.py и app.py. Выделил строки которые поменялись.
Файл forms.py
from flask_wtf import FlaskForm
from flask_wtf import RecaptchaField
from wtforms import TextField
from wtforms.validators import DataRequired
class ContactForm(FlaskForm):
text = TextField('Комментарий', validators=[DataRequired()])
class ContactRecaptchaForm(ContactForm):
recaptcha = RecaptchaField()
Файл app.py
from flask import Flask
from flask import request
from flask import render_template
app = Flask(__name__)
app.config['SECRET_KEY'] = "12345"
# ключи recaptcha от google
app.config['RECAPTCHA_PUBLIC_KEY'] = "6Ld74-oUAAAAAJC0UOY6PtrOrNcxQ2VQCfGAqBOC"
app.config['RECAPTCHA_PRIVATE_KEY'] = "6Ld74-oUAAAAAD2_Jl2IVKh2uCCI9OPX_7oTdLz4"
app.config['RECAPTCHA_DISABLE'] = True # будет капча или нет
# форма с валидацией и капчей или без неё.
# в новой версии flask-wtf планируется сделать RECAPTCHA_DISABLE красиво
# из коробки, это временное решение
if app.config['RECAPTCHA_DISABLE'] == True:
from forms import ContactRecaptchaForm as ContactForm
else:
from forms import ContactForm
@app.route('/', methods=['GET', 'POST'])
def index():
form = ContactForm()
msg = ""
if request.method == "POST":
if form.validate_on_submit():
msg="Успех!"
# отправить почту, записать в БД
else:
msg="Ошибка валидации"
# обработать ошибку
return render_template("index.html",
title="index page",
form=form,
msg=msg)
if __name__ == '__main__':
app.run(debug=True)
Готовый код с примером Flask + reCAPTCHA v2 Google
Наконец для всех библиотек на Python3.6+ появилось всё, что мне нужно, так же свежий Python 3.6+ и 3.7+ доступен на хостинге который я использую. Виртуальное окружение в новых «питонах» создаю так: