В современном мире веб-разработки API (Application Programming Interface) обеспечивает взаимодействия между различными приложениями и сервисами.
REST API, стали стандартом для создания веб-сервисов благодаря их простоте и гибкости.
Наша сегодняшняя цель — подробно разобраться в процессе создания REST API с использованием Django и Django REST Framework, разбираясь в каждом шаге и его значение. Мы также рассмотрим, почему создание API важно и как это может быть полезно в ваших будущих проектах.
Почему REST API важны?
REST — это архитектурный стиль, который определяет набор ограничений и принципов для создания веб-сервисов.
REST API позволяют:
-
Обеспечивать взаимодействие между различными приложениями. Например, мобильное приложение может взаимодействовать с сервером через API.
-
Создавать микросервисную архитектуру. Разделение приложения на независимые сервисы улучшает масштабируемость и гибкость.
-
Обеспечивает совместимость с различными клиентами. REST API используют стандартные HTTP-методы и форматы данных, такие как JSON, что облегчает интеграцию в другие сервисы.
План действий по разработке
Теперь давайте определим наш план действий и поскорее приступим к написанию кода!
-
Настроим проект Django и приложения API.
-
Создадим модели данных с использованием Django ORM.
-
Настроим Django REST Framework.
-
Создадим сериализаторы для преобразования данных.
-
Реализуем представление и маршруты для обработки запросов.
-
Сделаем фильтрацию, поиск, сортировку, пагинацию.
-
Протестируем наше API.
-
Развернем проект в облаке Amvera тремя командами в IDE через загрузку кода в привязанный Git-репзиторий (что намного проще настройки VPS).
Каждый этап нашей статьи будет сопровождаться объяснениями и примерами кода, полный код вы сможете найти в репозитории на GitHub.
Приступаем!)
Установим Django и Django REST Framework
Сначала необходимо установить Django и DRF. Рекомендуем создать виртуальное окружение для изоляции зависимостей.
Создадим и активируем виртуальное окружение:
# Создаём виртуальное окружение python -m venv venv
# Активация виртуального окружения # На Windows: .\venv\Scripts\activate # На macOS и Linux: source venv/bin/activate
Установим необходимые пакеты для работы
pip install django pip install djangorestframework pip install django-filter pip install djangorestframework-simplejwt
Создадим папку проекта и нашего приложения
django-admin startproject Site cd Site python manage.py startapp api
Определим модели
В файле api/models.py создадим следующие модели:
-
Profile: Расширяет встроенную модель
User, добавляя биографию пользователя. -
Category: Категория для постов.
-
Post: Публикация, связанная с автором (пользователем) и категорией.
-
Comment: Комментарий к посту от пользователя.
from django.db import models from django.contrib.auth.models import User class Profile(models.Model): """Расширение модели User для добавления дополнительных полей""" user = models.OneToOneField(User, on_delete=models.CASCADE) bio = models.TextField(blank=True) def __str__(self): return f'Профиль пользователя {self.user.username}' class Category(models.Model): """Модель категории для постов""" name = models.CharField(max_length=100) description = models.TextField(blank=True) def __str__(self): return self.name class Post(models.Model): """Модель поста""" author = models.ForeignKey(User, related_name='posts', on_delete=models.CASCADE) category = models.ForeignKey(Category, related_name='posts', on_delete=models.SET_NULL, null=True) title = models.CharField(max_length=200) content = models.TextField() published = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) likes = models.ManyToManyField(User, related_name='liked_posts', blank=True) class Meta: ordering = ['-published'] def __str__(self): return self.title class Comment(models.Model): """Модель комментария""" post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE) author = models.ForeignKey(User, related_name='comments', on_delete=models.CASCADE) text = models.TextField() created = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['created'] def __str__(self): return f'Комментарий от {self.author.username} на {self.post.title}'
Почему мы делаем именно так:
-
Модели определяют структуру данных в приложении.
-
Используя Django ORM, мы можем работать с базой данных через Python-код без необходимости писать SQL-запросы.
-
Определение отношений между моделями (OneToOne, ForeignKey, ManyToMany) позволяет связать данные и обеспечить целостность информации.
После определения моделей необходимо создать и применить миграции
python manage.py makemigrations python manage.py migrate
Перейдем к настройке Django REST Framework
Настройка settings.py
В файле Site/settings.py добавим необходимые приложения и настройки и с использованием DEFAULT_PAGINATION_CLASS и PAGE_SIZE организуем пингацию
INSTALLED_APPS = [ # Сторонние приложения 'rest_framework', 'rest_framework_simplejwt', 'django_filters', # Наше приложение 'api', # Встроенные приложения Django 'django.contrib.admin', 'django.contrib.auth', # ... остальные приложения ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', # JWT аутентификация 'rest_framework.authentication.SessionAuthentication', # Сессионная аутентификация для браузера ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # Разрешения по умолчанию ), 'DEFAULT_FILTER_BACKENDS': ( 'django_filters.rest_framework.DjangoFilterBackend', # Фильтрация 'rest_framework.filters.SearchFilter', # Поиск 'rest_framework.filters.OrderingFilter', # Сортировка ), 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # Пагинация 'PAGE_SIZE': 10, # Размер страницы }
-
Подключение DRF и дополнительных пакетов необходимо для использования функциональности API.
-
Настройки DRF определяют поведение API, включая аутентификацию, разрешения, фильтрацию и пагинацию.
-
JWT-аутентификация позволяет безопасно аутентифицировать пользователей с помощью токенов.
-
Пагинация улучшает производительность и удобство использования при работе с большим объемом данных.
Создадим сериализаторы для преобразования данных
Сериализаторы преобразуют данные моделей в форматы, пригодные для передачи по сети (например, JSON), и обратно.
Файл api/serializers.py
from rest_framework import serializers from .models import Profile, Category, Post, Comment from django.contrib.auth.models import User class UserSerializer(serializers.ModelSerializer): """Сериализатор для модели User""" class Meta: model = User fields = ('id', 'username') class ProfileSerializer(serializers.ModelSerializer): """Сериализатор для профиля пользователя""" user = UserSerializer(read_only=True) class Meta: model = Profile fields = ('id', 'user', 'bio') class CommentSerializer(serializers.ModelSerializer): """Сериализатор для комментария""" author = UserSerializer(read_only=True) class Meta: model = Comment fields = ('id', 'author', 'text', 'created') class PostSerializer(serializers.ModelSerializer): """Сериализатор для поста""" author = UserSerializer(read_only=True) comments = CommentSerializer(many=True, read_only=True) category = serializers.SlugRelatedField(slug_field='name', queryset=Category.objects.all()) likes_count = serializers.SerializerMethodField() class Meta: model = Post fields = ('id', 'author', 'title', 'content', 'category', 'published', 'updated', 'comments', 'likes_count') def get_likes_count(self, obj): return obj.likes.count() class CategorySerializer(serializers.ModelSerializer): """Сериализатор для категории""" posts = PostSerializer(many=True, read_only=True) class Meta: model = Category fields = ('id', 'name', 'description', 'posts')
-
Сериализаторы обеспечивают преобразование сложных типов данных в формат JSON для передачи по API.
-
Они также обрабатывают валидацию данных при создании или обновлении объектов.
-
Вложенные сериализаторы позволяют включать связанные данные в ответы API.
Реализуем представления и маршруты для обработки запросов
в файле api/views.py
Разработаем PostViewSet с использованием filter_backends, filterset_fields, search_fields и ordering_fields где настраиваем фильтрацию, поиск и сортировку
from rest_framework import viewsets, permissions, filters from django_filters.rest_framework import DjangoFilterBackend from rest_framework.decorators import action from rest_framework.response import Response from .models import Profile, Category, Post, Comment from .serializers import ProfileSerializer, CategorySerializer, PostSerializer, CommentSerializer from .permissions import IsOwnerOrReadOnly class ProfileViewSet(viewsets.ModelViewSet): """Представление для профилей пользователей""" queryset = Profile.objects.all() serializer_class = ProfileSerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly] def perform_create(self, serializer): serializer.save(user=self.request.user) class CategoryViewSet(viewsets.ModelViewSet): """Представление для категорий""" queryset = Category.objects.all() serializer_class = CategorySerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly] class PostViewSet(viewsets.ModelViewSet): """Представление для постов""" queryset = Post.objects.all() serializer_class = PostSerializer permission_classes = [IsOwnerOrReadOnly] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['category__name', 'author__username'] search_fields = ['title', 'content'] ordering_fields = ['published', 'author__username', 'likes_count'] def perform_create(self, serializer): serializer.save(author=self.request.user) @action(detail=True, methods=['post']) def like(self, request, pk=None): """Пользователь может лайкнуть пост""" post = self.get_object() post.likes.add(request.user) return Response({'status': 'пост лайкнут'}) @action(detail=True, methods=['post']) def unlike(self, request, pk=None): """Пользователь может убрать лайк с поста""" post = self.get_object() post.likes.remove(request.user) return Response({'status': 'лайк с поста убран'}) class CommentViewSet(viewsets.ModelViewSet): """Представление для комментариев""" queryset = Comment.objects.all() serializer_class = CommentSerializer permission_classes = [IsOwnerOrReadOnly] def perform_create(self, serializer): serializer.save(author=self.request.user)
-
Фильтрация позволяет получать данные по определенным критериям.
-
Поиск облегчает нахождение информации по ключевым словам.
-
Сортировка позволяет организовывать данные в определенном порядке.
в файле api/permissions.py создадим пользовательское разрешение
from rest_framework import permissions class IsOwnerOrReadOnly(permissions.BasePermission): """ Пользовательское разрешение, позволяющее владельцам объекта редактировать его. """ def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True return obj.author == request.user
-
Пользовательские разрешения позволяют настроить доступ к ресурсам в соответствии с бизнес-логикой.
-
В данном случае, только автор поста или комментария может его редактировать или удалять.
в файле api/urls.py настроим маршруты для API
from django.urls import include, path from rest_framework import routers from .views import ProfileViewSet, CategoryViewSet, PostViewSet, CommentViewSet router = routers.DefaultRouter() router.register(r'profiles', ProfileViewSet) router.register(r'categories', CategoryViewSet) router.register(r'posts', PostViewSet) router.register(r'comments', CommentViewSet) urlpatterns = [ path('', include(router.urls)), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), ]
И в файле Site/urls.py добавим пути для JWT-аутентификации
from django.contrib import admin from django.urls import path, include from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView, ) urlpatterns = [ path('admin/', admin.site.urls), path('', include('myapi.urls')), path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), ]
-
Маршрутизация определяет, какие URL-адреса обрабатывают определенные представления.
-
JWT-аутентификация требует эндпоинтов для получения и обновления токенов.
Протестируем наше API
Для тестирования получим JWT-токен. Для этого необходимо отправить POST-запрос на /api/token/ с именем пользователя и паролем.
Где name и password ваши данные.
`curl -X POST -d "username=name&password=your_password" http://127.0.0.1:8000/api/token/`
Выходные данные:
{ "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." }
Теперь мы будем использовать полученный access токен для доступа к защищенным ресурсам, таким как список постов.
Входные данные:
-
Заголовок
Authorizationсо значениемBearer your_access_token, гдеyour_access_token— это токен, полученный на предыдущем шаге.
Команда cURL:
curl -H "Authorization: Bearer your_access_token" http://127.0.0.1:8000/posts/
Выходные данные:
[ { "id": 1, "author": { "id": 1, "username": "alex" }, "title": "Первый пост", "content": "Это содержимое первого поста.", "category": "Django", "published": "2023-10-29T12:00:00Z", "updated": "2023-10-29T12:00:00Z", "comments": [], "likes_count": 0 } ]
Здесь сервер проверяет токен в заголовке запроса и, если он действителен, возвращает запрошенные данные.
Теперь протестируем основные функции нашего API: создание поста, лайк поста и добавление комментария.
Создадим пост
Входные данные:
-
Заголовки:
-
Content-Type: application/json -
Authorization: Bearer your_access_token
-
-
Тело запроса:
"title": "Новый пост", "content": "Содержимое нового поста.", "category": "Django"
-
Команда cURL:
curl -X POST -H "Content-Type: application/json" \ -H "Authorization: Bearer your_access_token" \ -d '{"title": "Новый пост", "content": "Содержимое нового поста.", "category": "Django"}' \ http://127.0.0.1:8000/posts/
Выходные данные:
"id": 2, "author": { "id": 1, "username": "alex" }, "title": "Новый пост", "content": "Содержимое нового поста.", "category": "Django", "published": "2024-10-29T12:30:00Z", "updated": "2024-10-29T12:30:00Z", "comments": [], "likes_count": 0
Лайк поста
Входные данные:
-
Заголовок
Authorization: Bearer your_access_token -
URL содержит идентификатор поста, который мы хотим лайкнуть (например,
2).
Команда cURL:
curl -X POST -H "Authorization: Bearer your_access_token" \ http://127.0.0.1:8000/posts/2/like/
Выходные данные:
{ "status": "пост лайкнут" }
Добавим комментарий
Входные данные:
-
Заголовки:
-
Content-Type: application/json -
Authorization: Bearer your_access_token
-
Тело запроса:
{ "post": 2, "text": "Отличный пост!" }
Команда cURL:
curl -X POST -H "Content-Type: application/json" \ -H "Authorization: Bearer your_access_token" \ -d '{"post": 2, "text": "Отличный пост!"}' \ http://127.0.0.1:8000/comments/
Выходные данные:
{ "id": 1, "author": { "id": 1, "username": "alex" }, "text": "Отличный пост!", "created": "2024-10-29T12:35:00Z" }
Деплой на сервера Amvera
После успешного тестирования нашего API на локальном сервере, давай развернём его на удалённом сервере, чтобы он был доступен 24/7 и не зависел от вашего компьютера.
Регистрация в сервисе Amvera Cloud
-
Создай аккаунт:
-
Перейди на сайт Amvera.
-
Нажми на кнопку «Регистрация».
-
Заполни все необходимые поля, включая номер телефона, и нажми «Отправить код».
-
Введи код из SMS, подтверди, что ты не робот, и нажми «Регистрация».
-
Подтверди адрес электронной почты, перейдя по ссылке в письме.
-
Создание проекта и размещение приложения
-
Создай новый проект:
-
После входа на платформу, на главной странице нажми кнопку «Создать» или «Создать первый!».
Создание проекта -
2.Настрой проект:
-
Присвой проекту название (лучше на английском).
-
Выбери тарифный план. Для развертывания нашего APi достаточно самого простого тарифа.

-
Подготовка кода для развертывания:
-
Amvera использует git для доставки кода в облако. Вам потребуется создать файл конфигурации
amvera.yml, который подскажет облаку, как запускать ваш проект. -
Для упрощения создания этого файла воспользуйтесь графическим инструментом генерации.

-
Выбор окружения и зависимостей:
-
Укажите версию Python и путь до файла
requirements.txt, который содержит все необходимые пакеты. -
Укажите путь до основного файла вашего проекта, например
main.py.
-
-
Генерация и загрузка файла:
-
Нажмите «Generate YAML» для создания файла
amvera.ymlи загрузите его в корень вашего проекта.
-
Файл конфигурации amvera.yml служит для того, чтобы платформа Amvera знала, как правильно собрать и запустить ваш проект. Этот файл содержит ключевую информацию об окружении, зависимостях, а также инструкциях для запуска приложения.
Структура файла amvera.yml:
meta: environment: python toolchain: name: pip version: 3.8 build: requirementsPath: requirements.txt run: scriptName: manage.py persistenceMount: /data containerPort: 8000
Для того чтобы наш проект корректно работал в среде Amvera, важно указать все необходимые пакеты в файле requirements.txt. Этот файл определяет все зависимости Python, которые нужны для выполнения кода.
Вот так выглядит наш файл requirements.txt :
Django==3.2.9 djangorestframework==3.12.4 django-filter==2.4.0 djangorestframework-simplejwt==4.7.2
Инициализация и отправка проекта в репозиторий:
-
Инициализируйте git репозиторий в корне вашего проекта, если это еще не сделано:
git init -
Привяжите локальный репозиторий к удаленному на Amvera:
git remote add amvera -
Добавьте и зафиксируйте изменения:
git add . git commit -m "Initial commit" -
Отправьте проект в облако:
git push amvera master
Сборка и развертывание проекта:
-
После отправки проекта в систему, на странице проекта статус изменится на «Выполняется сборка». После завершения сборки проект перейдет в стадию «Выполняется развертывание», а затем в статус «Успешно развернуто».
-
Если проект не развернулся, проверьте логи сборки и логи приложения для отладки.
-
Если проект завис на этапе «Сборка», убедитесь в корректности файла
amvera.yml
Поздравляю! Мы создали приложение REST API с использованием Django и Django REST Framework.
Если вам требуется легко развернуть проект на сервере и доставлять в него обновления тремя командами в IDE, зарегистрируйтесь в облаке со встроенным CI/CD Amvera Cloud, и получите 111 руб. на тестирование функционала.
ссылка на оригинал статьи https://habr.com/ru/articles/856798/

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