Spec-Zone.ru › Flask 2.2

JavaScript, fetch, и JSON

Вы можете сделать вашу HTML-страницу динамичной, изменяя данные без перезагрузки всей страницы. Вместо отправки HTML <form> и перенаправления для повторного рендеринга шаблона, вы можете добавить JavaScript, который вызывает fetch() и заменяет содержимое на странице.

fetch() — это современное встроенное решение JavaScript для отправки запросов со страницы. Вы, возможно, слышали о других методах и библиотеках «AJAX», таких как XMLHttpRequest() или jQuery. Они больше не нужны в современных браузерах, хотя вы можете использовать их или другую библиотеку в зависимости от требований вашего приложения. Эти документы будут сосредоточены только на встроенных функциях JavaScript.

Рендеринг Шаблонов

Важно понимать разницу между шаблонами и JavaScript. Шаблоны рендерятся на сервере, прежде чем ответ будет отправлен браузеру пользователя. JavaScript выполняется в браузере пользователя после рендеринга и отправки шаблона. Поэтому невозможно использовать JavaScript для влияния на то, как рендерится шаблон Jinja, но можно отобразить данные в JavaScript, который будет выполняться.

Чтобы предоставить данные JavaScript при рендеринге шаблона, используйте фильтр tojson() в блоке <script>. Это преобразует данные в допустимый объект JavaScript и гарантирует, что любые небезопасные символы HTML будут отображаться безопасно. Если вы не используете фильтр tojson, вы получите ошибку SyntaxError в консоли браузера.

data = generate_report()
return render_template("report.html", chart_data=data)
<script>
    const chart_data = {{ chart_data|tojson }}
    chartLib.makeChart(chart_data)
</script>

Менее распространённый способ — добавить данные в атрибут data- тега HTML. В этом случае вы должны использовать одинарные кавычки вокруг значения, а не двойные, иначе вы получите недопустимый или небезопасный HTML.

<div data-chart='{{ chart_data|tojson }}'></div>

Генерация URL-адресов

Другой способ получения данных с сервера в JavaScript — это отправка запроса за ними. Сначала вам нужно знать URL-адрес для запроса.

Самый простой способ сгенерировать URL-адреса — продолжить использование url_for() при рендеринге шаблона. Например:

const user_url = {{ url_for("user", id=current_user.id)|tojson }}
fetch(user_url).then(...)

Однако, вам может потребоваться сгенерировать URL-адрес на основе информации, известной только в JavaScript. Как обсуждалось выше, JavaScript выполняется в браузере пользователя, а не в рамках рендеринга шаблона, поэтому вы не можете использовать url_for в этот момент.

В этом случае вам нужно знать «корневой URL», под которым обслуживается ваше приложение. В простых настройках это /, но это также может быть что-то другое, например https://example.com/myapp/.

Простой способ сообщить вашему JavaScript-коду об этом корне — задать его как глобальную переменную при рендеринге шаблона. Тогда вы можете использовать его для генерации URL-адресов из JavaScript.

const SCRIPT_ROOT = {{ request.script_root|tojson }}
let user_id = ...  // do something to get a user id from the page
let user_url = `${SCRIPT_ROOT}/user/${user_id}`
fetch(user_url).then(...)

Отправка запроса с помощью fetch

fetch() принимает два аргумента: URL-адрес и объект с другими параметрами и возвращает Promise. Мы не будем рассматривать все доступные параметры и будем использовать then() по обещанию, а не другие обратные вызовы или синтаксис await. Подробнее об этих функциях см. в документации MDN.

По умолчанию используется метод GET. Если ответ содержит JSON, он может использоваться с then() цепочкой обратных вызовов.

const room_url = {{ url_for("room_detail", id=room.id)|tojson }}
fetch(room_url)
    .then(response => response.json())
    .then(data => {
        // data is a parsed JSON object
    })

Для отправки данных используйте метод, такой как POST, и передайте параметр body. Наиболее распространённые типы данных — данные формы или данные JSON.

Для отправки данных формы передайте заполненный объект FormData. Это использует тот же формат, что и HTML-форма, и будет обращаться к нему с помощью request.form в представлении Flask.

let data = new FormData()
data.append("name": "Flask Room")
data.append("description": "Talk about Flask here.")
fetch(room_url, {
    "method": "POST",
    "body": data,
}).then(...)

В общем случае отдавайте предпочтение отправке данных запроса в формате данных формы, как это используется при отправке HTML-формы. JSON может представлять более сложные данные, но если вам это не нужно, лучше придерживаться более простого формата. При отправке данных JSON также должен быть отправлен заголовок Content-Type: application/json, в противном случае Flask вернёт ошибку 400.

let data = {
    "name": "Flask Room",
    "description": "Talk about Flask here.",
}
fetch(room_url, {
    "method": "POST",
    "headers": {"Content-Type": "application/json"},
    "body": JSON.stringify(data),
}).then(...)

Следование Перенаправлениям

Ответ может быть перенаправлением, например, если вы вошли в систему с помощью JavaScript вместо традиционной HTML-формы, и ваше представление вернуло перенаправление вместо JSON. JavaScript-запросы следуют перенаправлениям, но не изменяют страницу. Если вы хотите изменить страницу, вы можете проверить ответ и вручную применить перенаправление.

fetch("/login", {"body": ...}).then(
    response => {
        if (response.redirected) {
            window.location = response.url
        } else {
            showLoginError()
        }
    }
)

Замена Содержимого

Ответ может быть новым HTML, либо новым разделом страницы для добавления или замены, либо совершенно новой страницей. В целом, если вы возвращаете всю страницу, лучше обработать это с помощью перенаправления, как показано в предыдущем разделе. Следующий пример показывает, как заменить <div> HTML, возвращённым запросом.

<div id="geology-fact">
    {{ include "geology_fact.html" }}
</div>
<script>
    const geology_url = {{ url_for("geology_fact")|tojson }}
    const geology_div = getElementById("geology-fact")
    fetch(geology_url)
        .then(response => response.text)
        .then(text => geology_div.innerHtml = text)
</script>

Возврат JSON из представлений

Чтобы вернуть объект JSON из вашего представления API, вы можете напрямую вернуть словарь из представления. Он будет сериализован в JSON автоматически.

@app.route("/user/<int:id>")
def user_detail(id):
    user = User.query.get_or_404(id)
    return {
        "username": User.username,
        "email": User.email,
        "picture": url_for("static", filename=f"users/{id}/profile.png"),
    }

Если вы хотите вернуть другой тип JSON, используйте функцию jsonify(), которая создаёт объект ответа с заданными данными, сериализованными в JSON.

from flask import jsonify

@app.route("/users")
def user_list():
    users = User.query.order_by(User.name).all()
    return jsonify([u.to_json() for u in users])

Обычно не рекомендуется возвращать данные файлов в ответе JSON. JSON не может напрямую представлять двоичные данные, поэтому они должны быть закодированы в base64, что может быть медленным, занимает больше пропускной способности для отправки и не так легко кешируется. Вместо этого используйте одно представление для обработки файлов и генерируйте URL-адрес для нужного файла, который будет включён в JSON. Затем клиент может сделать отдельный запрос для получения связанного ресурса после получения JSON.

Получение JSON в представлениях

Используйте свойство json объекта request, чтобы декодировать тело запроса как JSON. Если тело не является допустимым JSON или заголовок Content-Type не установлен на application/json, будет выброшена ошибка 400 Bad Request.

from flask import request

@app.post("/user/<int:id>")
def user_update(id):
    user = User.query.get_or_404(id)
    user.update_from_json(request.json)
    db.session.commit()
    return user.to_json()

© 2007–2022 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/2.2.x/patterns/javascript/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API