Для начала давайте подумаем, почему стандартных разрешений может быть недостаточно. Рассмотрим пару примеров:
- Роль автора: вы хотите, чтобы только пользователь, создавший статью, мог её редактировать или удалять.
- Специфическая логика доступа: только пользователи с активной подпиской могут оставлять комментарии.
- Динамические условия: доступ к ресурсу разрешён только в случае, если дата создания объекта не старше месяца.
В таких случаях проще и логичнее написать собственное разрешение, чем пытаться объединить стандартные.
Как пишутся кастомные разрешения?
В DRF разрешение — это класс, который наследуется от BasePermission и реализует метод has_permission() или/и метод has_object_permission(). Давайте разберём их по очереди:
has_permission(self, request, view)— проверяет, может ли пользователь вообще получить доступ к эндпоинту (в рамках всего представления).has_object_permission(self, request, view, obj)— проверяет права доступа к конкретному объекту (например, к определённой статье).
Пример 1: разрешение только для авторов объекта
Допустим, вы хотите разрешить редактировать записи только тем пользователям, которые их создали.
Код разрешения будет выглядеть так:
from rest_framework.permissions import BasePermission
class IsAuthor(BasePermission):
"""
Разрешает доступ только авторам объекта.
"""
def has_object_permission(self, request, view, obj):
# Проверяем, что пользователь является автором объекта
return obj.author == request.user
Теперь применим его в нашем ViewSet:
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated
from .permissions import IsAuthor
from .models import Post
from .serializers import PostSerializer
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
# Композиция разрешений: пользователь должен быть аутентифицирован и являться автором объекта.
permission_classes = [IsAuthenticated, IsAuthor]
Такой подход позволит выделить авторов среди всех аутентифицированных пользователей.
Пример 2: разрешение на основе группы
Теперь создадим разрешение, которое позволяет доступ только пользователям, принадлежащим к определённой группе, например, "Модераторы".
from rest_framework.permissions import BasePermission
class IsInModeratorGroup(BasePermission):
"""
Разрешает доступ только для пользователей из группы "Модераторы".
"""
def has_permission(self, request, view):
# Проверяем, что пользователь аутентифицирован и состоит в группе "Модераторы"
return request.user.is_authenticated and request.user.groups.filter(name="Модераторы").exists()
В этом случае мы проверяем доступ на уровне эндпоинта, а не конкретного объекта.
Применяем разрешение так же, как в предыдущем примере:
permission_classes = [IsAuthenticated, IsInModeratorGroup]
Логика и порядок применения разрешений
DRF позволяет задавать несколько разрешений одновременно, комбинируя их в список permission_classes. При этом все разрешения из списка проверяются; если хотя бы одно возвращает False — доступ будет запрещён.
Пример с комбинированием:
permission_classes = [IsAuthenticated, IsAuthor]
Чтобы запрос прошёл, пользователь должен быть аутентифицирован и являться автором объекта.
Пример 3: условие для GET и POST запросов
Теперь давайте создадим кастомное разрешение, которое:
- Разрешает
GET-запросы всем. - Требует аутентификации для
POST-запросов.
from rest_framework.permissions import BasePermission, SAFE_METHODS
class ReadOnlyOrIsAuthenticated(BasePermission):
"""
Разрешает доступ для всех на чтение (GET), но требует аутентификации для записи (POST).
"""
def has_permission(self, request, view):
if request.method in SAFE_METHODS: # SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
return True
return request.user.is_authenticated
Вот так мы можем одновременно поддерживать открытый доступ для чтения данных и защищённый доступ для их создания.
Интеграция кастомных разрешений в реальном проекте
Предположим, мы работаем над блогом, где:
- Модераторы могут удалять любые комментарии.
- Авторы комментариев могут редактировать только свои комментарии.
Создадим два кастомных разрешения: IsModerator и IsCommentAuthor.
from rest_framework.permissions import BasePermission
class IsModerator(BasePermission):
"""
Проверяет, является ли пользователь Модератором.
"""
def has_permission(self, request, view):
return request.user.groups.filter(name="Модераторы").exists()
class IsCommentAuthor(BasePermission):
"""
Проверяет, является ли пользователь автором комментария.
"""
def has_object_permission(self, request, view, obj):
return obj.author == request.user
Теперь настраиваем разрешения в ViewSet:
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated, SAFE_METHODS
from .permissions import IsModerator, IsCommentAuthor
from .models import Comment
from .serializers import CommentSerializer
class CommentViewSet(ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
def get_permissions(self):
# Открываем доступ только на чтение для всех
if self.request.method in SAFE_METHODS:
return [permission() for permission in []]
# Модераторы могут удалять любые комментарии
if self.request.method == 'DELETE':
return [IsModerator()]
# Любые другие действия (например, PUT) доступны только автору
return [IsAuthenticated(), IsCommentAuthor()]
Частые ошибки при создании кастомных разрешений
Забыли обработать
objвhas_object_permission()
DRF при доступе к объектам вызывает методhas_object_permission()только если он определён. Если вы ограничиваетесьhas_permission(), проверка прав на уровне объектов проигнорируется.Не возвращают
True/Falseявно
Методы разрешений всегда должны возвращать строгоTrueилиFalse, иначе возникнут неожиданные ошибки.Ошибки в логике сложных разрешений
Например, забыли учесть различные HTTP-методы. Всегда проверяйте свою логику через тесты!
Когда кастомизация — это зло?
Иногда соблазн написать сложные кастомные разрешения может сыграть злую шутку. Если ваши разрешения становятся избыточно комплексными, это затрудняет дальнейшую поддержку проекта. В таких случаях стоит пересмотреть архитектуру проекта или задуматься об использовании ролевой модели (IsInGroup), которая позволяет абстрагироваться от прямых проверок.
Практическое задание
Напишите кастомное разрешение, которое позволит:
- Пользователям из группы "Администраторы" удалять записи.
- Остальным пользователям только просматривать записи (GET).
- Отключите доступ на запись (POST, PUT) для всех остальных.
Примените это разрешение к модели
Postв вашем проекте. Напишите несколько тестов с помощью DRF тест-клиента, чтобы проверить разное поведение.
Теперь мы не просто знаем, как использовать встроенные возможности DRF, но и можем создавать собственные кастомные решения, адаптируя систему безопасности наших API к самым сложным сценариям. Кто теперь остановит вас от создания мирового господства с помощью кастомных разрешений? Никто! 🙂
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ