🏠 Самостоятельная практика
Закрепляющее задание по темам практикума 10
Не LMS-задание. Это закрепляющее задание составлено на основе тем практикума 10. Выполняется самостоятельно для углублённой практики.
⚡ Задание кратко
Расширить DRF-проект блогом: авто-привязка автора, IsAuthorOrReadOnly, кастомное разрешение can_publish, Swagger-документация.
- Модель Post с полем author (FK на User),
Meta.permissions = [("can_publish", "Can publish posts")] - ViewSet с
perform_create→author=request.userиget_queryset→ фильтр по автору - Класс
IsAuthorOrReadOnly(BasePermission)— только автор редактирует - Класс
CanPublish(BasePermission)— проверкаblog.can_publish - Swagger через drf-yasg:
/swagger/и/redoc/ - Дамп:
dumpdata --indent=4 > blog_backup.json
Задание: Блог с кастомными разрешениями и Swagger
Создайте DRF-проект блога (или расширьте существующий проект store) с реализацией всех паттернов практикума 10.
Часть 1: Подготовка окружения
# Создать виртуальное окружение
python -m venv blog_env
# Активировать (Windows)
blog_env\Scripts\activate
# Установить зависимости
pip install django djangorestframework drf-yasg
# Создать проект
django-admin startproject blog_project .
python manage.py startapp blog
# Применить миграции Django
python manage.py migrate
# Создать суперпользователя
python manage.py createsuperuser
Часть 2: Модель с пользовательским разрешением
Создайте модель Post с авто-привязкой автора и кастомным разрешением:
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
created_at = models.DateTimeField(auto_now_add=True)
published = models.BooleanField(default=False)
class Meta:
permissions = [
("can_publish", "Can publish posts"),
]
def __str__(self):
return self.title
python manage.py makemigrations
python manage.py migrate
Часть 3: Кастомные разрешения
Создайте файл blog/permissions.py:
# blog/permissions.py
from rest_framework.permissions import BasePermission
class IsAuthorOrReadOnly(BasePermission):
"""
Редактирование — только автор поста,
чтение — все аутентифицированные.
"""
def has_object_permission(self, request, view, obj):
if request.method in ['GET', 'HEAD', 'OPTIONS']:
return True
return obj.author == request.user
class CanPublish(BasePermission):
"""
Публикация поста — только пользователи с разрешением 'blog.can_publish'.
"""
def has_permission(self, request, view):
return request.user.has_perm('blog.can_publish')
Часть 4: Представления
# blog/views.py
from rest_framework import viewsets
from rest_framework.generics import ListAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Post
from .serializers import PostSerializer
from .permissions import IsAuthorOrReadOnly, CanPublish
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
def perform_create(self, serializer):
# Автор берётся из запроса автоматически
serializer.save(author=self.request.user)
@action(detail=True, methods=['post'],
permission_classes=[IsAuthenticated, CanPublish])
def publish(self, request, pk=None):
post = self.get_object()
post.published = True
post.save()
return Response({'status': 'published'})
class MyPostListView(ListAPIView):
"""Только посты текущего пользователя."""
serializer_class = PostSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Post.objects.filter(author=self.request.user)
Часть 5: Маршруты и Swagger
# blog_project/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from blog.views import PostViewSet, MyPostListView
router = DefaultRouter()
router.register(r'posts', PostViewSet)
schema_view = get_schema_view(
openapi.Info(
title="Blog API",
default_version='v1',
description="API for blog with custom permissions",
),
public=True,
permission_classes=(permissions.AllowAny,),
)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
path('api/my-posts/', MyPostListView.as_view(), name='my-posts'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0)),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0)),
]
Часть 6: Резервная копия базы данных
# Создать несколько постов через API или Admin, затем:
python manage.py dumpdata --indent=4 > blog_backup.json
# Проверить файл
# Удалить DB (db.sqlite3)
# Восстановить:
python manage.py migrate
python manage.py loaddata blog_backup.json
Проверка в VS Code и Postman
Запуск сервера через VS Code
- Откройте папку проекта в VS Code
- Создайте
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Django",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": ["runserver"],
"django": true,
"justMyCode": true
}
]
}
- Нажмите F5 для запуска с отладчиком
- Установите точку останова в
perform_create— убедитесь, чтоself.request.userсодержит правильного пользователя
Проверка через Postman
- Откройте
http://127.0.0.1:8000/swagger/— должна открыться Swagger UI - В Postman: POST
/api/posts/с JWT-токеном → убедитесь, что author заполнился автоматически - GET
/api/my-posts/— только посты текущего пользователя - PUT
/api/posts/{id}/от другого пользователя → 403 Forbidden - POST
/api/posts/{id}/publish/безcan_publish→ 403; с разрешением → 200