Последнее обновление 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
Эта команда устанавливает необходимые библиотеки, а также создает виртуальную среду.
Активируйте виртуальную среду с помощью команды ниже:
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, по-прежнему полезны при создании масштабируемых приложений.