AJAX-запросы в Django на примере простейшего приложения сбора и показа сообщений

от автора

Предисловие

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

Часть 1. Подготовка проекта

Для демонстрации я создам небольшой Django проект, который имеет всего лишь одно приложение, обрабатывающее сообщение от пользователя. Вы можете скачать проект с GitHub отсюда или создать что-то подобное самостоятельно. Познакомимся с структурой приложения поближе.

Структура приложения

app/  │  │ ├── app/  │  └── #Конфигурация проекта  │ ├── modules/ #Здесь хранятся приложения  │  └── messages_app/  │   ├── models.py #Хранит модель Message (Имя отправителя, Текст, Время отправки)  │   ├── forms.py #Хранит форму модели Message  (Имя отправителя, Текст сообщения) │   ├── urls.py #Два адреса, обрабатывают views │   ├── views.py #Может отправить на шаблон отображения сообщений (messages.html) или на шаблон отправки     сообщения (send_message.html)  │   └── # Другие настройки по умолчанию  │ ├── templates/ #Здесь хранится весь фронт  │  ├── messages.html # Обрабатывает все сообщения  │  ├── send_message.html # Отправка сообщения для сохранение его в модель Message │  └── src/  │   └── js/  │    └── ajax.js/ #Здесь будет хранится код JS, пока файл пуст 

Все django-приложения будут храниться в modules, а html-шаблоны в templates в папке проекта, код JS будет хранится в templates/src/js.

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

Рассмотрим единственную модель приложения:

#/modules/models.py  from django.db import models  class Message(models.Model):     sender_name = models.CharField(max_length=100)  # Имя отправителя     message = models.TextField()  # Текст сообщения     sent_at = models.DateTimeField(auto_now_add=True)  # Время отправки

Я уже создал два тестовых сообщения для отображения. Эта модель используется во views.py, для отображения всех элементов и для создания нового элемента:

#/modules/views.py  from django.shortcuts import render from .models import Message from .forms import MessageForm   # Create your views here. def get_all_mesages(request):     messages = Message.objects.all()     return render(request, 'messages_app/messages.html', {'messages': messages} )  def send_message(request):     if request.method == 'POST':         form = MessageForm(request.POST)         if form.is_valid():             form.save()  # Сохраняем форму в базе данных             return render(request, 'messages_app/send_message.html', {'form': form, 'repeat': True})     else:         form = MessageForm()      return render(request, 'messages_app/send_message.html', {'form': form})

Во views используется форма из forms.py:

#/modules/forms.py  from django import forms from .models import Message  class MessageForm(forms.ModelForm):     class Meta:         model = Message         fields = ['sender_name', 'message']

HTML-шаблоны

Используемые шаблоны — message.html для отображения всех элементов:

<!-- /templates/message.html --> {% load static %}  <div id="messages-container">     {% for message in messages %}         <hr>         <p>Отправитель: {{ message.sender_name }}</p>         <p>Сообщение: {{ message.message }}</p>         <p>Отправлено: {{ message.sent_at }}</p>         <hr>     {% endfor %} </div>  <script src="{% static 'messages_app/src/js/ajax.js' %}"></script>

И send_message.html:

<!-- /templates/send_message.html --> {% if repeat %}     <div> Вы уже отправляли форму, хотите отправить повторно?</div> {% endif %}  <h1>Отправить сообщение</h1> <form method="post">     {% csrf_token %}     {{ form.as_p }}     <button type="submit">Отправить</button> </form>

По пути templates/src/js/ находится ajax.js, который сейчас пуст. В дальнейшем мы заполним его кодом.

Часть 2. Внедрение AJAX

Внутри AJAX-запроса используется стандартный HTTP-запрос, такой же, как и обычный GET или POST, однако нам, для эффективного использования AJAX, данные нужно завернуть в JSON-формат. Для этого мы будем использовать JsonResponse на бекенде — перейдем во views.py и создадим еще один метод, который будет заворачивать экземпляры модели в JSON.

#/modules/views.py  #...Ранее добавленные импорты и методы  from django.http import JsonResponse  def ajax_messages(request):     messages = Message.objects.all() #Получим все сообщения из модели      #Обработаем модель     messages_data = []     for message in messages:         messages_data.append({             'sender_name': message.sender_name,             'message': message.message,             'sent_at': message.sent_at.strftime('%Y-%m-%d %H:%M:%S'),  # Преобразуем дату в строку         })     return JsonResponse({'messages': messages_data})

Добавим отображение метода в urls.py

#/modules/urls.py  from django.urls import path from . import views  urlpatterns = [     path('', views.get_all_mesages, name='get_all_messages'),     path('send/', views.send_message, name='send_message'),      #ajax     path('get_data/', views.ajax_messages, name='get_data') ]

Попробуем перейти по ссылке https:localhost/get_data/. Нас ждет такой результат:

localhost/get_data/

localhost/get_data/

Теперь мы можем получать по этому адресу данные из модели в JSON-форматe. Далее создадим AJAX-запрос — для этого на странице отображения напишем следующий JS код и поместим его в ajax.js

// templates/src/js/ajax.js   let get_data_url = 'get_data/' // Переход на ссылку с ajax запросом  function draw_messages(messages) { // То что мы будем делать с нашими данными   console.log(messages) // Сейчас мы просто хотим вывести их в консоль }   function ajax_get(){   return fetch(get_data_url, {       method: 'GET',   }).then(response => response.json()) // Преобразуем полученный ответ в JSON     .then(data => draw_messages(data)) // Обрабатываем данные, полученные в ответе с помощью функции draw_message [3 строка]     .catch(error => console.error('Ошибка:', error)); // Если возникли ошибки, выводим их в консоль } 

Мы создали асинхронный GET-запрос который обращается к адресу /get_data/. Уже сейчас, если в консоли браузера на странице отображения сообщений мы введем ajax_get() — получим тот самый ответ в json — элементы модели Message.

Получаем object { messages: ...}

Получаем object { messages: …}

Нам нужно, чтобы метод обновлял все содержимое блока #messages-container, для этого изменим метод draw_messages внутри ajax.js

// templates/src/js/ajax.js  let get_data_url = 'get_data/' // Переход на ссылку с ajax запросом  const messagesContainer = document.querySelector('#messages-container'); // Сюда помещаются наши статьи  function draw_messages(messages) {   // Очистим контейнер перед добавлением новых сообщений   messagesContainer.innerHTML = '';    // Для каждого сообщения в массиве messages создаем HTML   messages.forEach(message => {       const messageElement = document.createElement('div');              // Создаем элементы для отображения данных сообщения       const senderName = document.createElement('p');       senderName.textContent = `Отправитель: ${message.sender_name}`;              const messageText = document.createElement('p');       messageText.textContent = `Сообщение: ${message.message}`;              const sentAt = document.createElement('p');       sentAt.textContent = `Отправлено: ${message.sent_at}`;              // Создаем разделитель       const hr = document.createElement('hr');              // Добавляем все элементы в messageElement       messageElement.appendChild(senderName);       messageElement.appendChild(messageText);       messageElement.appendChild(sentAt);       messageElement.appendChild(hr);              // Вставляем messageElement в контейнер       messagesContainer.appendChild(messageElement);   }); }  function ajax_get() {   fetch(get_data_url, {       method: 'GET', // Используем метод 'GET'   })   .then(response => response.json()) // Преобразуем ответ в JSON   .then(data => {       draw_messages(data.messages); // Передаем полученные сообщения в draw_messages   })   .catch(error => console.error('Ошибка:', error)); // Обрабатываем ошибки }

При AJAX-запросе мы полностью очищаем #messages-container и обновляем содержимым из GET-запроса.

Эта простая и местами примитивная реализация поможет понять всю суть процесса, однако сейчас все равно чего-то не хватает — добавим интервал в 1 секунду для ajax_get(), чтобы данные обновлялись автоматически, и нам не пришлось перезагружать страницу. Для этого добавим в конец ajax.js:

// templates/src/js/ajax.js   // ...Переменные и методы  setInterval(ajax_get, 1000);

Проверить результат можно просто: откройте страницу с отображением сообщений и страницу с отправкой. Заполните форму отправки и вернитесь на страницу с сообщениями, она автоматически обновится.

В этой статье мы рассмотрели простой GET-запрос. Однако такой подход не подойдет, если объем данных в модели будет слишком большим. В таком случае нужно реализовать POST-запрос с сохранением текущих данных на клиенте. Об этом могу написать позже, если статья вам понравиться, и вы захотите продолжения!


ссылка на оригинал статьи https://habr.com/ru/articles/862780/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *