🏠 Домашнее задание 12 — Урок 26
⚡ Суть ДЗ 12
Создать три API-эндпоинта для менеджера задач с помощью DRF:
- POST /tasks/ — создание задачи (title, description, status, deadline)
- GET /tasks/ и GET /tasks/<id>/ — список и конкретная задача
- GET /tasks/stats/ — агрегирующий эндпоинт (total, по статусам, просроченные)
Сдать: код эндпоинтов + скриншоты Postman.
📋 Текст задания из LMS
Цель: Освоить работу с Django REST Framework для создания, получения и агрегирования данных, используя модели задач.
Задание 1: Эндпоинт для создания задачи
Создайте эндпоинт для создания новой задачи. Задача должна быть создана с полями title, description, status, и deadline.
Шаги для выполнения:
- Определите сериализатор для модели Task.
- Создайте представление для создания задачи.
- Создайте маршрут для обращения к представлению.
Задание 2: Эндпоинты для получения списка задач и конкретной задачи по её ID
Создайте два новых эндпоинта для:
- Получения списка задач
- Получения конкретной задачи по её уникальному ID
Шаги для выполнения:
- Создайте представления для получения списка задач и конкретной задачи.
- Создайте маршруты для обращения к представлениям.
Задание 3: Агрегирующий эндпоинт для статистики задач
Создайте эндпоинт для получения статистики задач, таких как общее количество задач, количество задач по каждому статусу и количество просроченных задач.
Шаги для выполнения:
- Определите представление для агрегирования данных о задачах.
- Создайте маршрут для обращения к представлению.
- Код эндпоинтов: Вставьте весь код представлений и маршрутов.
- Скриншоты ручного тестирования: Приложите скриншоты консоли или Postman, подтверждающие успешное выполнение запросов для каждого эндпоинта.
🛠️ Подготовка окружения
Шаг 1: Создание виртуального окружения
# Создаём папку проекта
mkdir task_manager_api
cd task_manager_api
# Создаём venv
python -m venv venv
# Активируем (Windows PowerShell)
.\venv\Scripts\Activate.ps1
# Если ошибка ExecutionPolicy — выполните:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Шаг 2: Установка зависимостей
pip install django djangorestframework
# Фиксируем зависимости
pip freeze > requirements.txt
Шаг 3: Создание Django-проекта и приложения
django-admin startproject config .
python manage.py startapp tasks
Шаг 4: Настройка settings.py
# config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # DRF
'tasks', # наше приложение
]
Шаг 5: Git-инициализация
git init
git add .
git commit -m "initial: Django project with DRF"
✅ Пошаговое решение
Шаг 1: Модель Task
Связь с теорией: структура модели — основа для ModelSerializer (см. theory.html#serialization).
# tasks/models.py
from django.db import models
from django.utils import timezone
class Task(models.Model):
STATUS_CHOICES = [
('todo', 'To Do'),
('in_progress', 'In Progress'),
('done', 'Done'),
]
title = models.CharField(max_length=200)
description = models.TextField(blank=True, default='')
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='todo'
)
deadline = models.DateField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
@property
def is_overdue(self):
"""Задача просрочена, если deadline в прошлом и статус не 'done'."""
if self.deadline and self.status != 'done':
return self.deadline < timezone.now().date()
return False
Шаг 2: Миграции
python manage.py makemigrations tasks
python manage.py migrate
Шаг 3: Сериализатор
Связь с теорией: ModelSerializer автоматически строит поля из модели (см. theory.html#serialization).
# tasks/serializers.py
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
is_overdue = serializers.BooleanField(read_only=True)
class Meta:
model = Task
fields = [
'id',
'title',
'description',
'status',
'deadline',
'created_at',
'is_overdue',
]
read_only_fields = ['id', 'created_at', 'is_overdue']
Шаг 4: Представления (Views)
Связь с примерами: паттерн @api_view + Response (см. examples.html — Пример 12).
# tasks/views.py
from django.utils import timezone
from django.db.models import Count, Q
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Task
from .serializers import TaskSerializer
# === Задание 1: Создание задачи ===
@api_view(['POST'])
def task_create(request):
"""POST /api/tasks/ — создание новой задачи."""
serializer = TaskSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# === Задание 2: Список и конкретная задача ===
@api_view(['GET'])
def task_list(request):
"""GET /api/tasks/ — список всех задач."""
tasks = Task.objects.all().order_by('-created_at')
serializer = TaskSerializer(tasks, many=True)
return Response(serializer.data)
@api_view(['GET'])
def task_detail(request, pk):
"""GET /api/tasks/<pk>/ — получение задачи по ID."""
try:
task = Task.objects.get(pk=pk)
except Task.DoesNotExist:
return Response(
{'error': f'Task with id={pk} not found'},
status=status.HTTP_404_NOT_FOUND
)
serializer = TaskSerializer(task)
return Response(serializer.data)
# === Задание 3: Агрегирующий эндпоинт ===
@api_view(['GET'])
def task_stats(request):
"""GET /api/tasks/stats/ — статистика по задачам.
Возвращает:
- total: общее количество задач
- by_status: количество задач по каждому статусу
- overdue: количество просроченных задач (deadline < today, status != 'done')
"""
today = timezone.now().date()
# Общее количество
total = Task.objects.count()
# По статусам — используем annotate (тема урока 26)
by_status_qs = Task.objects.values('status').annotate(
count=Count('id')
)
by_status = {entry['status']: entry['count'] for entry in by_status_qs}
# Просроченные задачи — фильтрация через Q-объекты
overdue_count = Task.objects.filter(
deadline__lt=today,
status__in=['todo', 'in_progress']
).count()
return Response({
'total': total,
'by_status': by_status,
'overdue': overdue_count,
})
Шаг 5: URL-маршруты
# tasks/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('tasks/', views.task_list, name='task-list'),
path('tasks/create/', views.task_create, name='task-create'),
path('tasks/stats/', views.task_stats, name='task-stats'),
path('tasks/<int:pk>/', views.task_detail, name='task-detail'),
]
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('tasks.urls')),
]
Шаг 6: Запуск сервера
python manage.py runserver
🔍 Проверка в VS Code и Postman
Запуск через VS Code (F5)
Создайте файл .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Django runserver",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": ["runserver"],
"django": true,
"justMyCode": true
}
]
}
Нажмите F5 — сервер запустится в режиме отладки. Для точек останова: кликните слева от номера строки в views.py, затем выполните запрос — VS Code остановится на точке.
Тестирование через Postman
Тест 1: Создание задачи (POST)
- Метод:
POST - URL:
http://127.0.0.1:8000/api/tasks/create/ - Headers:
Content-Type: application/json - Body (raw JSON):
{ "title": "Изучить DRF сериализаторы", "description": "Прочитать документацию и написать первый serializer", "status": "todo", "deadline": "2026-06-30" } - Ожидаемый ответ:
201 Createdс JSON-объектом задачи
Тест 2: Список задач (GET)
- Метод:
GET - URL:
http://127.0.0.1:8000/api/tasks/ - Ожидаемый ответ:
200 OKс массивом задач
Тест 3: Конкретная задача (GET by ID)
- Метод:
GET - URL:
http://127.0.0.1:8000/api/tasks/1/ - Ожидаемый ответ:
200 OKс объектом задачи - Несуществующий ID:
GET /api/tasks/999/→404 Not Found
Тест 4: Агрегирующий эндпоинт (GET stats)
- Метод:
GET - URL:
http://127.0.0.1:8000/api/tasks/stats/ - Ожидаемый ответ:
{ "total": 3, "by_status": { "todo": 2, "in_progress": 1, "done": 0 }, "overdue": 1 }
Тестирование через терминал (curl / Python requests)
# Создать задачу
curl -X POST http://127.0.0.1:8000/api/tasks/create/ \
-H "Content-Type: application/json" \
-d '{"title": "Test task", "status": "todo"}'
# Список задач
curl http://127.0.0.1:8000/api/tasks/
# Статистика
curl http://127.0.0.1:8000/api/tasks/stats/
Django Admin (опционально)
# tasks/admin.py
from django.contrib import admin
from .models import Task
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
list_display = ['title', 'status', 'deadline', 'created_at']
list_filter = ['status']
search_fields = ['title', 'description']
# Создать суперпользователя и войти в /admin/
python manage.py createsuperuser
📚 Связь с теорией урока
| Элемент ДЗ | Тема урока | Ссылка |
|---|---|---|
| ModelSerializer для Task | Сериализация в DRF, ModelSerializer | theory.html#serialization |
| @api_view + Response | Установка DRF, архитектурные слои | theory.html#drf-install |
| task_stats: Count + values().annotate() | Метод annotate() — количество по группам | theory.html#aggregation |
| Фильтр просроченных задач | Фильтрация по временным меткам | theory.html#timestamps |
| Сортировка по created_at | Метод order_by() | theory.html#orderby |