Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Permission Class в Django REST Framework: назначение
Permission класс в Django REST Framework — это механизм для реализации гранулярного контроля доступа на уровне API endpoints. Он определяет, кто может выполнять какие действия над какими ресурсами.
1. Основное назначение
Permission класс проверяет разрешение перед выполнением handler функции:
from rest_framework import permissions
from rest_framework.views import APIView
from rest_framework.response import Response
# ❌ Без Permission
class UnsafePostView(APIView):
def post(self, request):
# Любой может создавать посты
Post.objects.create(
title=request.data['title'],
content=request.data['content'],
author=request.user # Проблема: кто угодно может быть автором
)
return Response({"status": "created"})
# ✅ С Permission
class SafePostView(APIView):
permission_classes = [permissions.IsAuthenticated] # Только авторизованные
def post(self, request):
Post.objects.create(
title=request.data['title'],
content=request.data['content'],
author=request.user # Теперь гарантировано есть пользователь
)
return Response({"status": "created"})
2. Встроенные классы Permission
DRF предоставляет готовые классы:
from rest_framework import permissions
# AllowAny — разрешить всем (по умолчанию)
class PublicView(APIView):
permission_classes = [permissions.AllowAny]
# IsAuthenticated — только авторизованные пользователи
class PrivateView(APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
return Response({"user": request.user.username})
# IsAdminUser — только администраторы
class AdminView(APIView):
permission_classes = [permissions.IsAdminUser]
# IsAuthenticatedOrReadOnly — авторизованные могут писать, остальные только читать
class BlogView(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request):
return Response(Post.objects.all().values())
def post(self, request): # Только авторизованные
Post.objects.create(**request.data)
return Response({"status": "created"})
3. Создание кастомного Permission класса
Для специфичных правил доступа:
from rest_framework import permissions
class IsOwner(permissions.BasePermission):
"""
Разрешить доступ только владельцу объекта
"""
def has_object_permission(self, request, view, obj):
# Проверка для конкретного объекта
return obj.author == request.user
class IsPostOwnerOrReadOnly(permissions.BasePermission):
"""
Только владелец может редактировать/удалять
Остальные только читают
"""
def has_object_permission(self, request, view, obj):
# Разрешить чтение всем
if request.method in permissions.SAFE_METHODS: # GET, HEAD, OPTIONS
return True
# Редактирование только владельцу
return obj.author == request.user
class IsNotBlocked(permissions.BasePermission):
"""
Проверить что пользователь не заблокирован
"""
def has_permission(self, request, view):
if not request.user.is_authenticated:
return True # Анонимные могут читать
return not request.user.profile.is_blocked
4. Методы Permission класса
from rest_framework import permissions
class CompletePermission(permissions.BasePermission):
message = "Доступ запрещён"
def has_permission(self, request, view):
"""
Проверка VIEW-LEVEL разрешения.
Вызывается для ВСЕХ запросов к view.
"""
# Пример: проверить rate limit
if request.user.api_calls_today >= 1000:
return False
return True
def has_object_permission(self, request, view, obj):
"""
Проверка OBJECT-LEVEL разрешения.
Вызывается при доступе к конкретному объекту (detail view).
"""
# Пример: проверить владельца
if hasattr(obj, 'author'):
return obj.author == request.user
return True
# Использование
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [CompletePermission]
5. Комбинирование Permission классов
Логика И (все должны пройти):
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
# Все три должны пройти
permission_classes = [
IsAuthenticated, # Должен быть авторизован
IsNotBlocked, # Не должен быть заблокирован
IsPostOwnerOrReadOnly # Владелец или только чтение
]
# В этом примере:
# 1. has_permission() проверяется для IsAuthenticated
# 2. has_permission() проверяется для IsNotBlocked
# 3. has_object_permission() проверяется для IsPostOwnerOrReadOnly (в retrieve/update/delete)
Для логики ИЛИ (хотя бы один должен пройти):
class IsStaffOrReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
# OR логика
if request.method in permissions.SAFE_METHODS:
return True
return request.user.is_staff
6. Динамические Permission в зависимости от действия
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
def get_permissions(self):
"""
Разные permissions для разных действий
"""
if self.action in ['list', 'retrieve']:
# Чтение разрешено для всех
permission_classes = [permissions.AllowAny]
elif self.action in ['create']:
# Создание только для авторизованных
permission_classes = [permissions.IsAuthenticated]
elif self.action in ['update', 'partial_update', 'destroy']:
# Редактирование только для владельца
permission_classes = [IsOwner]
else:
permission_classes = [permissions.IsAuthenticated]
return [permission() for permission in permission_classes]
7. Проверка прав в сериализаторе или сервисе
Помимо View-level permissions, можно проверять в бизнес-логике:
class OrderService:
@staticmethod
def cancel_order(order_id: int, user: User) -> Order:
order = Order.objects.get(id=order_id)
# View-level permission исключит анонимных
# Но сервис ещё раз проверяет владельца
if order.customer != user:
raise PermissionError("Not order owner")
if order.status != Order.Status.PENDING:
raise ValidationError("Can only cancel pending orders")
order.status = Order.Status.CANCELLED
order.save()
return order
# В handler
class OrderViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
@action(detail=True, methods=['post'])
def cancel(self, request, pk=None):
try:
order = OrderService.cancel_order(pk, request.user)
return Response({"status": "cancelled"})
except PermissionError:
return Response(
{"detail": "Not authorized"},
status=status.HTTP_403_FORBIDDEN
)
8. Интеграция с Django permissions
DRF может использовать встроенные Django permissions:
from rest_framework.permissions import DjangoModelPermissions
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
# Использует Django model permissions
# post.add, post.change, post.delete, post.view
permission_classes = [DjangoModelPermissions]
# В администрировании даёш права группам
# Группа 'Editors' получает post.add, post.change
# Группа 'Moderators' получает post.delete
9. Обработка ошибок Permission
from rest_framework.exceptions import PermissionDenied
class CustomPermission(permissions.BasePermission):
message = "У вас нет прав на это действие"
def has_permission(self, request, view):
if not self.check_access(request):
# Кастомное сообщение об ошибке
raise PermissionDenied(detail=self.message)
return True
# API ответ при отказе:
# {
# "detail": "У вас нет прав на это действие"
# }
# HTTP 403 Forbidden
Ключевые практики:
- Всегда используй permissions для защиты endpoints
- Разделяй логику: view-level (has_permission) и object-level (has_object_permission)
- Создавай кастомные классы для специфичных правил доступа
- Комбинируй несколько permissions если нужна сложная логика
- Тестируй permissions отдельно от business logic
- Документируй какие права нужны для каждого endpoint
Permission классы — критический компонент безопасности API, обеспечивающий контроль доступа и защиту от неавторизованного использования.