Django + AJAX: как использовать AJAX в шаблонах Django

AJAX или асинхронный JavaScript и XML — это набор методов веб-разработки, использующих веб-технологии на стороне клиента для создания асинхронных веб-запросов.

Проще говоря, AJAX позволяет обновлять веб-страницы асинхронно, негласно обмениваясь данными с веб-сервером. Это означает, что обновление частей веб-страницы возможно без перезагрузки всей страницы.

Мы можем делать запросы AJAX из шаблонов Django, используя JQuery. AJAX методы библиотеки jQuery позволяют нам запрашивать текст, HTML, XML или JSON с удаленного сервера, используя как HTTP Get, так и HTTP Post. Полученные данные могут быть загружены непосредственно в выбранные HTML-элементы вашей веб-страницы.

В этом руководстве мы узнаем, как выполнять AJAX HTTP GET и POST запросы из шаблонов Django.

Требования к знаниям

Я предполагаю, что у вас есть базовые знания о Django. Поэтому я не буду вдаваться в настройку проекта. Это очень простой проект с приложением под названием AJAX, в котором я использую bootstrap и Django crispy form для стилизации.

Репозиторий Gitlab — https://gitlab.com/PythonRu/django-ajax

Выполнение AJAX GET запросов с помощью Django и JQuery

Метод HTTP GET используется для получения данных с сервера.

В этом разделе мы создадим страницу регистрации, где мы будем проверять доступность имени пользователя с помощью JQuery и AJAX в шаблонах Django. Это очень распространенное требование для большинства современных приложений.

# ajax/views.py
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login, authenticate
from django.shortcuts import render, redirect
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy


@login_required(login_url='signup')
def home(request):
    return render(request, 'home.html')


class SignUpView(CreateView):
    template_name = 'signup.html'
    form_class = UserCreationForm
    success_url = reverse_lazy('home')

    def form_valid(self, form):
        valid = super().form_valid(form)
        login(self.request, self.object)
        return valid


def validate_username(request):
    """Проверка доступности логина"""
    username = request.GET.get('username', None)
    response = {
        'is_taken': User.objects.filter(username__iexact=username).exists()
    }
    return JsonResponse(response)

Итак, у нас есть три представления, первым является home, которое отображает довольно простой шаблон домашней страницы.

Далее идет SignUpView, унаследованный от класса CreateView. Он нужен для создания пользователей с помощью встроенного Django-класса UserCreationForm, который предоставляет очень простую форму для регистрации. При успешном ее прохождении происходит вход в систему, и пользователь перенаправляется на домашнюю страницу.

Наконец, validate_username — это наше AJAX представление, которое возвращает объект JSON с логическим значением из запроса, проверяющего, существует ли введенное имя пользователя.

Класс JsonResponse возвращает HTTP-ответ с типом содержимого application/json, преобразуя переданный ему объект в формат JSON. Поэтому, если имя пользователя уже существует в базе данных, он вернет JSON-объект, показанный ниже.

{'is_taken': true}
# ajax/urls.py
from django.urls import path
from .views import home, SignUpView, validate_username

urlpatterns = [
    path('', home, name='home'),
    path('signup', SignUpView.as_view(), name='signup'),
    path('validate_username', validate_username, name='validate_username')
]

# dj_ajax/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('ajax/', include('ajax.urls'))
]
{# home.html #}
<h1>Привет, {{ user.username }}!</h1>
{# signup.html #}
{% load crispy_forms_tags %}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
</head>

<body>
<div class="container mt-5 w-50">
  <form id="signupForm" method="POST">
    {% csrf_token %}
    {{ form|crispy  }}
    <input type="submit" name="signupSubmit" class="btn btn-success btn-lg" />
  </form>
</div>

{% block javascript %}
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

  <script>
      $(document).ready(function () {
          // отслеживаем событие отправки формы
          $('#id_username').keyup(function () {
              // создаем AJAX-вызов
              $.ajax({
                  data: $(this).serialize(), // получаяем данные формы
                  url: "{% url 'validate_username' %}",
                  // если успешно, то
                  success: function (response) {
                      if (response.is_taken == true) {
                          $('#id_username').removeClass('is-valid').addClass('is-invalid');
                          $('#id_username').after('<div class="invalid-feedback d-block" id="usernameError">This username is not available!</div>')
                      }
                      else {
                          $('#id_username').removeClass('is-invalid').addClass('is-valid');
                          $('#usernameError').remove();

                      }
                  },
                  // если ошибка, то
                  error: function (response) {
                      // предупредим об ошибке
                      console.log(response.responseJSON.errors)
                  }
              });
              return false;
          });
      })
  </script>
{% endblock javascript %}
</body>
</html>

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

Внутри тега head мы загружаем bootstrap, используя CDN. Вы также можете сохранить библиотеку себе на диск и отдавать ее клиенту из статических папок.

  <form id="signupForm" method="POST">
    {% csrf_token %}
    {{ form|crispy  }}
    <input type="submit" name="signupSubmit" class="btn btn-success btn-lg" />
  </form>

Затем мы создаем форму Django, используя для стилизации тег crispy.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

Далее внутри блока javascript мы запрашиваем JQuery от google CDN, вы также можете загрузить ее локально.

Затем у нас есть еще один скрипт с методом ready(). Код, написанный внутри метода $(document).ready(), будет запущен, когда DOM страницы будет готов для выполнения JavaScript.

$('#id_username').keyup(function () {
    // создаем AJAX-вызов
    $.ajax({
        data: $(this).serialize(), // получаяем данные формы
        url: "{% url 'validate_username' %}",
        // если успешно, то
        success: function (response) {
            if (response.is_taken == true) {
                $('#id_username').removeClass('is-valid').addClass('is-invalid');
                $('#id_username').after('<div class="invalid-feedback d-block" id="usernameError">Это имя пользователя недоступно!</div>')
            }
            else {
                $('#id_username').removeClass('is-invalid').addClass('is-valid');
                $('#usernameError').remove();
            }
        },
        // если ошибка, то
        error: function (response) {
            // предупредим об ошибке
            console.log(response.responseJSON.errors)
        }
    });
    return false;
});

Метод ajax запускается функцией keyup. Он принимает на вход объект с параметрами запроса. После успешного завершения запросов запускается одна из колбэк-функций success или error. При успешном вызове мы используем условный оператор для добавления и удаления классов is-valid/is-invalid поля ввода. А return false в конце скрипта предотвращает отправку форм, таким образом останавливая перезагрузку страницы.

Сохраните файлы и запустите сервер, вы должны увидеть AJAX в действии.

Выполнение AJAX POST запросов с помощью Django и JQuery

Метод HTTP POST используется для отправки данных на сервер.

В этом разделе мы узнаем, как делать POST-запросы с помощью JQuery и AJAX в шаблонах Django.
Мы создадим контактную форму и сохраним данные, предоставленные пользователем, в базу данных с помощью JQuery и AJAX.

# ajax/models.py
from django.db import models

class Contact(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    message = models.TextField()

    def __str__(self):
        return self.name
# ajax/forms.py
from django import forms
from .models import Contact


class ContactForm(forms.ModelForm):
    class Meta:
        model = Contact
        fields = '__all__'
# ajax/urls.py
...
from .views import home, SignUpView, validate_username, contact_form

urlpatterns = [
    ...
    path('contact-form/', contact_form, name='contact_form')
]
# ajax/views.py
...
from .forms import ContactForm

...

def contact_form(request):
    form = ContactForm()
    if request.method == "POST" and request.is_ajax():
        form = ContactForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data['name']
            form.save()
            return JsonResponse({"name": name}, status=200)
        else:
            errors = form.errors.as_json()
            return JsonResponse({"errors": errors}, status=400)

    return render(request, "contact.html", {"form": form})

В представлении мы проверяем ajax-запрос с помощью метода request.is_ajax(). Если форма корректно заполнена, мы сохраняем ее в базе данных и возвращаем объект JSON с кодом состояния и именем пользователя. Для недопустимой формы мы отправим клиенту найденные ошибки с кодом 400, что означает неверный запрос (bad request).

{# contact.html #}
{% load crispy_forms_tags %}

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Contact us</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">

</head>

<body>
<div class="container mt-5 w-50">
  <form id="contactForm" method="POST">
    {% csrf_token %}
    {{ form|crispy }}
    <input type="submit" name="contact-submit" class="btn btn-success btn-lg" />
  </form>
</div>
{% block javascript %}
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script>
      $(document).ready(function () {
          // отслеживаем событие отправки формы
          $('#contactForm').submit(function () {
              // создаем AJAX-вызов
              $.ajax({
                  data: $(this).serialize(), // получаем данные формы
                  type: $(this).attr('method'), // GET или POST
                  url: "{% url 'contact_form' %}",
                  // если успешно, то
                  success: function (response) {
                      alert("Спасибо, что обратились к нам " + response.name);
                  },
                  // если ошибка, то
                  error: function (response) {
                      // предупредим об ошибке
                      alert(response.responseJSON.errors);
                      console.log(response.responseJSON.errors)
                  }
              });
              return false;
          });
      })
  </script>
{% endblock javascript %}
</body>
</html>

Давайте разобьем шаблон на более мелкие модули, чтобы лучше понять его.

Сначала мы импортируем bootstrap в head, используя CDN. Затем внутри body мы создаем форму с тегом crispy для стилизации.

После этого в первом javascript-блоке мы загружаем JQuery из CDN. Далее внутри функции $(document).ready() мы добавили наш AJAX метод.

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script>
      $(document).ready(function () {
          // отслеживаем событие отправки формы
          $('#contactForm').submit(function () {
              // создаем AJAX-вызов
              $.ajax({
                  data: $(this).serialize(), // получаем данные формы
                  type: $(this).attr('method'), // GET или POST
                  url: "{% url 'contact_form' %}",
                  // если успешно, то
                  success: function (response) {
                      alert("Спасибо, что обратились к нам " + response.name);
                  },
                  // если ошибка, то
                  error: function (response) {
                      // предупредим об ошибке
                      alert(response.responseJSON.errors);
                      console.log(response.responseJSON.errors)
                  }
              });
              return false;
          });
      })
  </script>

При отправке формы мы вызываем метод ajax(), который сериализует ее данные и отправляет их по заданному URL-адресу. В случае успеха мы показываем диалоговое окно с сообщением, сгенерированным на основе полученного имени пользователя.

Выполнение AJAX POST запросов с использованием представлений на основе классов

Нам нужно просто вернуть объект JSON из метода form_valid() класса FormView. Вы также можете использовать другие стандартные представления, основанные на классах, переопределив метод post().

# ajax/views.py
...
from django.views.generic.edit import CreateView, FormView
...

class ContactFormView(FormView):
    template_name = 'contact.html'
    form_class = ContactForm

    def form_valid(self, form):
        """
        Если форма валидна, вернем код 200
        вместе с именем пользователя
        """
        name = form.cleaned_data['name']
        form.save()
        return JsonResponse({"name": name}, status=200)

    def form_invalid(self, form):
        """
        Если форма невалидна, возвращаем код 400 с ошибками.
        """
        errors = form.errors.as_json()
        return JsonResponse({"errors": errors}, status=400)

Обучение с трудоустройством

Профессия Python-разработчик

Профессия Python-разработчик

7 820 4 692 ₽/мес.
Профессия Data Scientist

Профессия Data Scientist

11 520 6 912 ₽/мес.
Профессия Python Fullstack

Профессия Python Fullstack

7 820 4 692 ₽/мес.
Профессия Data Science: Аналитик

Профессия Data Science: Аналитик

6 600 3 960 ₽/мес.

Появились вопросы? Задайте на Яндекс.Кью

У сайта есть сообщество на Кью >> Python Q <<. Там я, эксперты и участники отвечаем на вопросы по python и программированию.