🏠 Домашнее задание

🎯 Самопроверка + разбор ДЗ: CRUD Task API К оглавлению урока

⚡ ДЗ в двух словах

Это урок-повторение. LMS-задания для Summary session 6 из источника — это задания блока DRF. Ниже их полный разбор.

Задание 1: Эндпоинт POST /tasks/ — создание задачи.

Задание 2: Эндпоинты GET /tasks/ и GET /tasks/<id>/.

Задание 3: GET /tasks/stats/ — агрегация: всего задач, по статусам, просроченные.

Это не отдельное LMS-задание. Урок 32 — итоговое повторение DRF-блока (Уроки 26–31). В источнике (Summary session 6) разобраны задания LMS из блока DRF. Ниже представлена их полная формулировка с пошаговым решением.

LMS-задания DRF-блока

Задание 1: Эндпоинт для создания задачи

Цель: Создайте эндпоинт для создания новой задачи. Задача должна создаваться с полями title, description, status и deadline.

Шаги:

  1. Определите сериализатор для модели Task.
  2. Создайте представление для создания задачи.
  3. Создайте маршрут для обращения к представлению.

Задание 2: Эндпоинты для получения задач

Цель: Создайте два эндпоинта:

  • Получение списка всех задач (GET /tasks/)
  • Получение конкретной задачи по её ID (GET /tasks/<pk>/)

Шаги:

  1. Создайте представления для получения списка и одной задачи.
  2. Создайте маршруты для обращения к представлениям.

Задание 3: Агрегирующий эндпоинт для статистики задач

Цель: Создайте эндпоинт GET /tasks/stats/, возвращающий статистику:

  • Общее количество задач
  • Количество задач по каждому статусу
  • Количество просроченных задач

Подготовка окружения

# Создать и активировать виртуальное окружение
python -m venv venv
venv\Scripts\activate          # Windows
# source venv/bin/activate     # Linux/macOS

# Установить зависимости
pip install django djangorestframework

# Создать проект
django-admin startproject task_project
cd task_project
python manage.py startapp tasks

# Добавить приложения в settings.py
# INSTALLED_APPS += ['rest_framework', 'tasks']

# Первые миграции
python manage.py migrate
python manage.py runserver

Пошаговое решение

Шаг 1: Модель Task

# tasks/models.py
from django.db import models

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)
    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 f"[{self.status}] {self.title}"

    class Meta:
        ordering = ['-created_at']
python manage.py makemigrations
python manage.py migrate

Шаг 2: Сериализатор (Задание 1)

# tasks/serializers.py
from rest_framework import serializers
from .models import Task

class TaskSerializer(serializers.ModelSerializer):
    created_at = serializers.DateTimeField(read_only=True)

    class Meta:
        model = Task
        fields = '__all__'

    def validate_deadline(self, value):
        from django.utils import timezone
        if value and value < timezone.now().date():
            raise serializers.ValidationError(
                "Дедлайн не может быть в прошлом."
            )
        return value

Шаг 3: Представления (Задания 1, 2, 3)

# tasks/views.py
from django.db.models import Count, Q
from django.utils import timezone
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 — создание; Задание 2 — список
@api_view(['GET', 'POST'])
def task_list_create(request):
    if request.method == 'GET':
        tasks = Task.objects.all()
        serializer = TaskSerializer(tasks, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

    elif request.method == 'POST':
        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 — одна задача по ID
@api_view(['GET'])
def task_detail(request, pk):
    try:
        task = Task.objects.get(pk=pk)
    except Task.DoesNotExist:
        return Response({'error': 'Task not found'}, status=status.HTTP_404_NOT_FOUND)
    serializer = TaskSerializer(task)
    return Response(serializer.data)

# Задание 3 — агрегация
@api_view(['GET'])
def task_stats(request):
    today = timezone.now().date()
    stats = Task.objects.aggregate(
        total=Count('id'),
        todo=Count('id', filter=Q(status='todo')),
        in_progress=Count('id', filter=Q(status='in_progress')),
        done=Count('id', filter=Q(status='done')),
        overdue=Count(
            'id',
            filter=Q(deadline__lt=today) & ~Q(status='done')
        ),
    )
    return Response(stats)

Шаг 4: Маршруты

# tasks/urls.py
from django.urls import path
from .views import task_list_create, task_detail, task_stats

urlpatterns = [
    path('tasks/', task_list_create, name='task-list-create'),
    path('tasks/<int:pk>/', task_detail, name='task-detail'),
    path('tasks/stats/', task_stats, name='task-stats'),
]

# task_project/urls.py
from django.contrib import admin
from django.urls import path, include

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

Проверка в VS Code / Postman

Тестирование в терминале

# Запустить сервер
python manage.py runserver

# Создать задачу (POST)
# В Postman: POST http://127.0.0.1:8000/api/tasks/
# Body -> raw -> JSON:
# {
#   "title": "Написать тесты",
#   "description": "Unit-тесты для Task API",
#   "status": "todo",
#   "deadline": "2025-12-31"
# }

# Получить все задачи (GET)
# http://127.0.0.1:8000/api/tasks/

# Получить одну задачу (GET)
# http://127.0.0.1:8000/api/tasks/1/

# Статистика (GET)
# http://127.0.0.1:8000/api/tasks/stats/

Ожидаемый ответ /api/tasks/stats/

{
    "total": 5,
    "todo": 2,
    "in_progress": 1,
    "done": 2,
    "overdue": 0
}

Проверка в VS Code (F5 / launch.json)

// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Django",
            "type": "debugpy",
            "request": "launch",
            "program": "${workspaceFolder}/manage.py",
            "args": ["runserver", "0.0.0.0:8000"],
            "django": true,
            "justMyCode": true
        }
    ]
}

Нажмите F5 — откроется отладчик Django. Ставьте точки останова в views.py и отслеживайте значения переменных.

Связь с теорией