Flask 2.0.X и axios — AJAX проще простого

Раньше я использовал активно связку jQuery для отправки Ajax запроса на сервер. Например так обрабатывал форму обратной связи или онлайн заказ товара.

Время не стоит на месте с использованием библиотеки VueJS я познакомился с библиотекой axios. И стал использовать не только в проектах VueJS но и просто с Flask и Django.

Flask + axios

Напишу подробно, с начало создаю виртуальное окружение, затем ставлю Flask.

mkdir flask-axios
cd flask-axios
python3 -m venv venv
source venv/bin/activate
pip install pip --upgrade
pip install Flask==2.0.1

Далее создаю директорию templates и шаблоны base.html, index.html, файл проекта app.py и дополнительные файлы app.js в директории static.

Создаю переменную окружения FLASK_ENV=development для того, что бы запускать проект в режиме отладки (DEBUG). В данном режиме приложение будет автоматический производить рестарт при изменении исходного кода.

Ещё одна переменная окружения FLASK_APP=app.py приложение Flask, запуск по команде flask run.

mkdir -p templates static/js
touch templates/{base,index}.html
touch static/js/callback.js
touch {app,forms}.py

export FLASK_ENV=development
export FLASK_APP=app.py

Сам проект на Flask

Файл app.py

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, валидация не пройдёт.

Верстка

Файл base.html в директории templates

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask 2.0.X axios simple - {{ title }}</title>
        <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
        {% block scripts %}
        {% endblock scripts %}
    </head>
    <body>
        {% block content %}
        {% endblock content %}
    </body>
</html>

В сроке 6 и 7 подключаю через cnd — JQuery и Axios.

Не стал для усложнения переписывать проект без JQuery.

Верстка

Файл index.html в директории templates

{% extends "base.html" %}
{% block content %}
    <form action="/callback" id="form1" method="post">
        {{ form.csrf_token }}
        {{ form.name.label }} {{ form.name() }}
        {{ form.phone.label }} {{ form.phone() }}
        <input type="submit" value="Отправить"/>
    </form>
    <div id="msg"></div>
{% endblock %}

{% block scripts%}
    {{ super() }}
    <script src="{{ url_for('static', filename='js/callback.js') }}"></script>
{% endblock %}

В строке 4 csrf_token иначе не пройдёт валидацию.

JavaScript

Файл callback.html в директории static/js

/* переопределить поведение кнопки "Отправить" */
$(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) в зависимости от правильности ввода.

Готовый код примера

git clone https://github.com/ivanov-s/flask-axios.git

VueJS конфликтует с Jinja2 и Django template

vuejs logo jinja2 logo django logo

В приложениях VueJS используется {{ varname }} для обработки шаблонов. В Jinja2 и Django шаблонах при рендеринге шаблона также используются фигурные скобки. Из-за этого возникает конфликт. Есть 3 способа решения проблемы.

VueJS — заменить фигурные скобки на что то другое

<!-- js файл -->
new Vue({
  delimiters: ['{*', '*}'],
  data: {
    VueVAR: 'test'
  }
})
<!-- html файл -->
<div id="app">
  {* VueVAR *} <!-- vuejs -->
  {{ name }}  <!-- переменная в Jinja2 или Django шаблоне переданная из views.py -->
</dev>

Использовать RAW в html для Jinja2

<!-- js файл -->
new Vue({
  data: {
    VueVAR: 'test'
  }
})
<!-- html файл -->
<div id="app">
  {{ VueVAR }}  <!-- vuejs -->
  {% raw %}
     {{ name }}  <!-- переменная в Jinja2 из views.py -->
  {% raw %}
</dev>

Для Django templates поменять фигурные скобки

# в файле setting.py
VARIABLE_TAG_START = '{*'
VARIABLE_TAG_END = '*}'
<!-- js файл -->
new Vue({
  data: {
    VueVAR: 'test'
  }
})
<!-- html файл -->
<div id="app">
{{ VueVAR }} <!-- vuejs -->
{* name *} <!-- переменная в Django template из views.py -->
</dev>

Ещё для Django использовать тег {% verbatim %}

<!-- html файл -->
<div id="app">
{% verbatim %}
{{ VueVAR }} <!-- vuejs -->
{% endverbatim %}
{{ name }} <!-- переменная в Django template из views.py -->
</dev>

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

В Vue-awesome-swiper не работают стрелки в слайдере

Столкнулся при использовании vue awesome swiper с неработающими стрелками в слайдере и пагинатором.

Оказалось проблема в версии новая версия swiper 6.1.1 не совместима с текущей версией плагина vue-awesome-swiper 4.1.1.

Делал как в документации, но только параметр loop подхватывался, остальные игнорировались:

        data: {
            swiperOption: {
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev'
                },
                loop: true,
                autoplay: {
                    delay: 3000,
                    stopOnLastSlide: false,
                },
            }
        }

Откатился на версию «swiper»: «^5.4.5» всё заработало.