← Назад к вопросам
Опиши всю цепочку для создания endpoint по получению сущностей
1.8 Middle🔥 181 комментариев
#REST API и HTTP#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Полная цепочка создания endpoint для получения сущностей
Это фундаментальный вопрос, демонстрирующий понимание архитектуры REST API. Рассмотрим полный процесс на примере списка постов (Posts).
1. Определение модели (models.py)
Начинаем с создания модели данных, которая представляет структуру сущности в базе данных.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(
"auth.User",
on_delete=models.CASCADE,
related_name="posts"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_published = models.BooleanField(default=False)
class Meta:
ordering = ["-created_at"]
indexes = [models.Index(fields=["-created_at"])]
def __str__(self):
return self.title
2. Создание сериализатора (serializers.py)
Сериализатор преобразует модель в JSON и обратно, а также валидирует данные.
from rest_framework import serializers
from .models import Post
class AuthorSerializer(serializers.Serializer):
"""Сокращенная информация об авторе"""
id = serializers.IntegerField()
username = serializers.CharField()
class PostSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True)
author_id = serializers.IntegerField(write_only=True)
class Meta:
model = Post
fields = [
"id",
"title",
"content",
"author",
"author_id",
"created_at",
"updated_at",
"is_published"
]
read_only_fields = ["id", "created_at", "updated_at"]
def validate_title(self, value):
if len(value) < 3:
raise serializers.ValidationError(
"Заголовок должен содержать минимум 3 символа"
)
return value
3. Создание ViewSet или View (views.py)
Представление обрабатывает HTTP запросы и возвращает HTTP ответы. Рассмотрим оба подхода.
Вариант 1: ViewSet (рекомендуется)
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
"""
CRUD операции для постов.
GET /api/v1/posts/ - список всех постов
POST /api/v1/posts/ - создание нового поста
GET /api/v1/posts/{id}/ - получение конкретного поста
PUT /api/v1/posts/{id}/ - полное обновление поста
PATCH /api/v1/posts/{id}/ - частичное обновление
DELETE /api/v1/posts/{id}/ - удаление поста
"""
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ["author", "is_published"]
search_fields = ["title", "content"]
ordering_fields = ["created_at", "title"]
ordering = ["-created_at"]
def get_queryset(self):
"""Оптимизируем запросы с select_related и prefetch_related"""
queryset = super().get_queryset()
queryset = queryset.select_related("author")
# Если не опубликовано, могут видеть только авторы
if not self.request.user.is_staff:
queryset = queryset.filter(
models.Q(is_published=True) |
models.Q(author=self.request.user)
)
return queryset
def perform_create(self, serializer):
"""Автоматически устанавливаем автора"""
serializer.save(author=self.request.user)
@action(detail=True, methods=["post"])
def publish(self, request, pk=None):
"""Кастомный экшн для публикации поста"""
post = self.get_object()
post.is_published = True
post.save()
return Response(
{"status": "пост опубликован"},
status=status.HTTP_200_OK
)
Вариант 2: Простой APIView для GET
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class PostListView(APIView):
def get(self, request):
"""Получить список всех опубликованных постов"""
posts = Post.objects.filter(is_published=True)
serializer = PostSerializer(posts, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request):
"""Создать новый пост"""
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
serializer.save(author=request.user)
return Response(
serializer.data,
status=status.HTTP_201_CREATED
)
return Response(
serializer.errors,
status=status.HTTP_400_BAD_REQUEST
)
4. Регистрация в маршрутизаторе (urls.py)
При использовании ViewSet:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
# Маршрутизатор автоматически генерирует все URL patterns
router = DefaultRouter()
router.register(r"posts", PostViewSet, basename="post")
urlpatterns = [
path("api/v1/", include(router.urls)),
]
# Генерируемые endpoints:
# GET /api/v1/posts/ - список
# POST /api/v1/posts/ - создание
# GET /api/v1/posts/{id}/ - деталь
# PUT /api/v1/posts/{id}/ - обновление
# PATCH /api/v1/posts/{id}/ - частичное обновление
# DELETE /api/v1/posts/{id}/ - удаление
# POST /api/v1/posts/{id}/publish/ - кастомный экшн
При использовании APIView:
from django.urls import path
from .views import PostListView, PostDetailView
urlpatterns = [
path("api/v1/posts/", PostListView.as_view(), name="post-list"),
path("api/v1/posts/<int:pk>/", PostDetailView.as_view(), name="post-detail"),
]
5. Добавление в главный urls.py проекта
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("posts.urls")), # Включаем URLs приложения
]
6. Настройка REST_FRAMEWORK (settings.py)
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
"DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend",
"rest_framework.filters.SearchFilter",
"rest_framework.filters.OrderingFilter",
],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticatedOrReadOnly",
],
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle"
],
"DEFAULT_THROTTLE_RATES": {
"anon": "100/hour",
"user": "1000/hour"
},
}
7. Тестирование endpoint (tests.py)
from rest_framework.test import APITestCase
from rest_framework import status
from django.contrib.auth.models import User
from .models import Post
class PostAPITestCase(APITestCase):
def setUp(self):
self.user = User.objects.create_user(
username="testuser",
password="testpass"
)
self.post = Post.objects.create(
title="Test Post",
content="Test content",
author=self.user,
is_published=True
)
def test_get_posts_list(self):
"""Тест получения списка постов"""
response = self.client.get("/api/v1/posts/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]["title"], "Test Post")
def test_create_post(self):
"""Тест создания поста"""
self.client.force_authenticate(user=self.user)
data = {
"title": "New Post",
"content": "New content",
"author_id": self.user.id
}
response = self.client.post("/api/v1/posts/", data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Post.objects.count(), 2)
Полная цепочка в двух словах
- Model → структура данных в БД
- Serializer → преобразование Model ↔ JSON
- ViewSet/View → бизнес-логика и обработка запросов
- Router/URLs → маршрутизация HTTP запросов
- Settings → конфигурация DRF
- Tests → проверка корректности работы
Эта архитектура обеспечивает чистоту кода, разделение ответственности и легкость тестирования.