Используйте API Django простым способом с помощью шаблонов Django

0
компьютеры и технологии 32.webp.webp

Последнее обновление 12.10.2023 — Василий Иванов

При использовании серверной технологии или платформы, такой как Django, Laravel или Node.js, для написания REST API, вам необходимо иметь дополнительные навыки работы с интерфейсом, используя такие платформы, как React, Angular и Vue, для использования конечных точек API. Но это не всегда так: вы можете использовать API в самом Django, используя шаблоны Django.

Настройка проекта Django и конечных точек API

Первым шагом будет создание каталога проекта. Откройте терминал и создайте каталог для вашего проекта.

 mkdir payment_wallet_project
cd payment_wallet_project

В этом руководстве вы создадите API для платежного кошелька.

Полный исходный код доступен в репозитории GitHub.

Начните с создания виртуальной среды. В этом случае вы будете использовать библиотеку Pipenv.

 pipenv install django djangorestframework 

Эта команда устанавливает необходимые библиотеки, а также создает виртуальную среду.

По теме:  Как исправить ошибку «Обнаружены проблемы с оборудованием» в средстве диагностики памяти Windows

Активируйте виртуальную среду с помощью команды ниже:

 pipenv shell

Создайте новый проект Django с именем PayApp.

 django-admin startproject PayApp . 

Использование точки (.) в конце команды django-admin гарантирует, что проект не создаст дубликат каталога проекта.

Создайте новое приложение Django в каталоге проекта.

 python manage.py startapp wallet 

Теперь приступайте к созданию приложения API, выполнив следующие действия.

Создание REST API платежного кошелька

Откройте файл Wallet/models.py и определите модели кошелька и транзакций.

 from django.db import models

class Wallet(models.Model):
   user = models.CharField(max_length=100)
   balance = models.DecimalField(max_digits=10, decimal_places=2)
   date_created = models.DateTimeField(auto_now_add=True)
   date_modified = models.DateTimeField(auto_now=True)

   def __str__(self):
       return self.user

class Transaction(models.Model):
   wallet = models.ForeignKey(Wallet, on_delete=models.CASCADE)
   amount = models.DecimalField(max_digits=10, decimal_places=2)
   timestamp = models.DateTimeField(auto_now_add=True)

В каталоге кошелька создайте новый файл Serializers.py и напишите сериализаторы модели кошелька и транзакции.

 from rest_framework import serializers
from .models import Wallet, Transaction

class WalletSerializer(serializers.ModelSerializer):
   class Meta:
       model = Wallet
       fields = '__all__'
class TransactionSerializer(serializers.ModelSerializer):
   class Meta:
       model = Transaction
       fields = '__all__'

Сериализаторы учитывают все поля моделей кошелька и транзакций.

В Wallet/views.py напишите представления для обработки логики реализации функциональности кошелька. Это включает в себя возможности пополнения и снятия средств.

 from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.decorators import action
from decimal import Decimal
from .models import Wallet, Transaction
from .serializers import WalletSerializer, TransactionSerializer

class WalletViewSet(viewsets.ModelViewSet):
   queryset = Wallet.objects.all()
   serializer_class = WalletSerializer

@action(detail=True, methods=['post'])
   def deposit(self, request, pk=None):
       wallet = self.get_object()
       amount = Decimal(request.data['amount'])
       wallet.balance += amount
       wallet.save()
       serializer = WalletSerializer(wallet)
       return Response(serializer.data)
      

@action(detail=True, methods=['post'])
   def withdraw(self, request, pk=None):
       wallet = self.get_object()
       amount = Decimal(request.data['amount'])
       if wallet.balance < amount:
           return Response({'error': 'Insufficient funds'},
                           status=status.HTTP_400_BAD_REQUEST)
       wallet.balance -= amount
       wallet.save()
       serializer = WalletSerializer(wallet)
       return Response(serializer.data)'

class TransactionViewSet(viewsets.ModelViewSet):
   queryset = Transaction.objects.all()
   Serializer_class = TransactionSerializer

Затем определите маршрутизацию URL-адресов для API, создав файл Wallet/urls.py:

 from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import WalletViewSet, TransactionViewSet, wallet_view

router = DefaultRouter()
router.register(r'wallets', WalletViewSet, basename='wallets')
router.register(r'transactions', TransactionViewSet, basename='transactions')

urlpatterns = [
   path('api/', include(router.urls)),
   path('wallets/<int:pk>/deposit/', WalletViewSet.as_view({'post': 'deposit'}),
        name='wallet-deposit'),
   path('wallets/<int:pk>/withdraw/', WalletViewSet.as_view({'post': 'withdraw'}),
        name='wallet-withdraw'),

]

В urls.py вашего проекта включите URL-адреса приложения:

 from django.contrib import admin
from django.urls import path, include

urlpatterns = [
   path('admin/', admin.site.urls),
   path('', include('wallet.urls')),
]

В файле PayApp/settings.py добавьте приложения Wallet и rest_framework в список INSTALLED_APPS.

 INSTALLED_APPS = [

"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",

"rest_framework", # new
"wallet", # new

]

При этом приложения Wallet и rest_framework будут зарегистрированы в приложении проекта Django.

Использование API с шаблонами Django

Теперь вы будете использовать шаблоны Django для создания простого интерфейса для использования API. Создайте файл Wallet.html в каталоге Wallet/templates/ и добавьте приведенный ниже HTML-код.

 <!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <title>Wallet</title>
   <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/
   css/bootstrap.min.css">
</head>
<body>
   <div class="container">
       <h1>Wallets</h1>
       <table class="table">
           <thead>
               <tr>
                   <th>User</th>
                   <th>Balance</th>
                   <th>Actions</th>
               </tr>
           </thead>
           <tbody>
               <tr>
                   <td>{{ wallet.user }}</td>
                   <td id="balance">{{ wallet.balance }}</td>
                   <td>
                       <div id="loading-indicator" class="d-none">
                           <div class="spinner-border text-primary" role="status">
                               <span class="sr-only">Loading...</span>
                           </div>
                           <p>Please wait while the deposit is being processed.</p>
                       </div>
                       <form id="deposit-form" method="post">
                           {% csrf_token %}
                           <input type="number" name="amount" step="0.01" min="0" required>
                           <button type="submit" class="btn btn-success">Deposit</button>
                       </form>
                       <form method="post" id="withdraw-form">
                           {% csrf_token %}
                           <input type="number" name="amount" step="0.01" min="0" required>
                           <button type="submit" class="btn btn-danger">Withdraw</button>
                       </form>
                   </td>
               </tr>
           </tbody>
       </table>
   </div>

</body>
</html>

HTML-файл отображает API ввода и вывода средств в красивом пользовательском интерфейсе, разработанном с использованием Bootstrap.

Взаимодействие пользователя с формами

В HTML-файле создайте тег сценария и добавьте следующий код в прослушиватель событий отправки формы депозита.

 <script>
document.querySelector('#deposit-form').addEventListener('submit', function (event) {
           event.preventDefault();
           document.querySelector('#loading-indicator').classList.remove('d-none');
           const amount = parseFloat(document.querySelector("#deposit-form " +
               "input[name="amount"]").value);
           fetch("{% url 'wallet-deposit' wallet.id %}", {
               method: "POST",
               headers: {
                   "Content-Type": "application/json",
                   "X-CSRFToken": getCookie("csrftoken")
               },
               body: JSON.stringify({ amount: amount })
           })
               .then(response => response.json())
               .then(data => {
                   console.log(data);
                   if (data.balance !== undefined) {
                       // Convert to number and format
                       const newBalance = parseFloat(data.balance).toFixed(2);
                       document.querySelector("#balance").textContent = newBalance;
                       document.querySelector('#loading-indicator').classList.
                       add('d-none');
                   }
               })
               .catch(error => {
                   console.error("Error:", error);
                   document.querySelector('#loading-indicator')
                       .classList.add('d-none');
               });
       });
</script>

Затем добавьте прослушиватель событий для отправки формы вывода, используя приведенный ниже код:

 <script>
document.querySelector('#withdraw-form').addEventListener('submit', function (event) {
   event.preventDefault();
   document.querySelector('#loading-indicator').classList.remove('d-none');
   const amount = parseFloat(document.querySelector("#withdraw-form " +
       "input[name="amount"]").value);
   fetch("{% url 'wallet-withdraw' wallet.id %}", {
       method: "POST",
       headers: {
           "Content-Type": "application/json",
           "X-CSRFToken": getCookie("csrftoken")
       },
       body: JSON.stringify({ amount: amount })
   })
       .then(response => response.json())
       .then(data => {
           console.log(data);
           if (data.balance !== undefined) { // Change to 'balance' for withdrawal
               const newBalance = parseFloat(data.balance).toFixed(2);
               document.querySelector("#balance").textContent = newBalance;
               document.querySelector('#loading-indicator').classList.add('d-none');
           }
       })
       .catch(error => {
           console.error("Error:", error);
           document.querySelector('#loading-indicator').classList.add('d-none');
       });
});
</script>

Прослушиватель событий отвечает за обработку отправки форм депозита и вывода средств (#deposit-form и #withdraw-form).

URL-адрес запроса на получение предназначен для сопоставления URL-адресов для действий по вводу и выводу средств.

Ответы JSON на депозиты и снятие средств затем анализируются для получения обновленного баланса (data.balance). Затем они форматируются и отображаются на странице.

Затем в файл Wallet/views.py добавьте следующее обновление для отображения страницы Wallet.html:

 from django.shortcuts import render

def wallet_view(request):
   # Retrieve the wallet to display
   wallet = Wallet.objects.first()
   return render(request, 'wallet.html', {'wallet': wallet})

В этом примере вы будете использовать метод запроса first() для выбора кошелька одного пользователя в демонстрационных целях.

Обновите файл urls.py, добавив путь к Wallet_view следующим образом:

 from .views import wallet_view

urlpatterns = [
   ...
     path('home/', wallet_view, name='wallet-page'),
]

Откройте страницу кошелька по URL-адресу: http://127.0.0.1:8000/home/.

Когда все настроено и работает как положено, выполните команды makemigrations иmigration. Наконец, запустите приложение:

 python manage.py makemigrations
python manage.py migrate

python manage.py runserver

Чтобы получить доступ к конечным точкам API, перейдите по адресу http://127.0.0.1:8000/api/.

Ожидаемый результат:

Перейдите на локальный хост для взаимодействия с кошельком.

Ожидаемый результат:

Кошелек показывает баланс и дает вам возможность внести или снять средства.

Понимание шаблонов Django и их роли в использовании API

Несмотря на то, что шаблоны Django отлично подходят для представления статического контента, они имеют определенные ограничения при использовании API:

  • Ограниченная гибкость: шаблоны Django менее гибкие, чем шаблоны, созданные с помощью Jinja2 или Twig, поскольку они используются для отображения определенных структур. Например, вам придется вручную проанализировать JSON и вставить данные в шаблон, если вам нужно использовать API, возвращающий данные JSON. Это может быть непросто, особенно если API предоставляет сложные структуры данных.
  • Нет поддержки асинхронных запросов: в шаблонах Django изначально отсутствует возможность обработки асинхронных запросов. Шаблоны по-прежнему нуждаются в синхронной обработке, хотя современные веб-фреймворки, такие как Flask и Django, поддерживают синтаксис async/await. Это означает, что вам придется дождаться завершения всех запросов, прежде чем создавать шаблон, если вам нужно получить данные из многочисленных источников перед отображением страницы.
  • Ограниченная обработка ошибок: ошибки могут возникать регулярно при использовании API. В шаблонах Django нет встроенных механизмов корректной обработки ошибок. В случае сбоя вызова API вам придется перехватить исключение и управлять им внутри самого шаблона, что может привести к неуклюжему и сложному в обслуживании коду.

Создавайте масштабируемые приложения

Предоставляя возможность отделить уровень представления от бизнес-логики, шаблоны Django позволяют разработчикам сосредоточиться на создании многократно используемого и удобного в обслуживании кода. Однако из-за своих ограничений шаблоны Django могут быть не лучшим выбором при использовании API в больших масштабах. Клиентские платформы, такие как React, по-прежнему полезны при создании масштабируемых приложений.