⚖️ Старый vs Новый — Урок 42

← К оглавлению урока

⚡ Главные изменения в Django 5.x / DRF 3.15+

  • Комбинирование permissions через | и & вместо кастомных OR-классов
  • SAFE_METHODS как константа вместо магических строк
  • message атрибут в BasePermission для кастомных сообщений об ошибке
  • datetime.now(tz=...) вместо наивного datetime.now()

1. Комбинирование разрешений

Из лекции (старое)Современное (DRF 3.9+ / Django 5.x)
# Кастомный OR-класс (из лекции)
class IsAuthOrAdmin(BasePermission):
    def has_permission(self, request, view):
        return (
            request.user.is_authenticated or
            request.user.is_staff
        )
# Оператор | (DRF 3.9+)
from rest_framework.permissions import (
    IsAuthenticated, IsAdminUser
)

class MyView(APIView):
    permission_classes = [
        IsAuthenticated | IsAdminUser
    ]

2. Проверка SAFE_METHODS

Из лекции (старое)Современное
# Магические строки
if request.method in ['GET', 'HEAD', 'OPTIONS']:
    return True
# Константа SAFE_METHODS
from rest_framework.permissions import SAFE_METHODS

if request.method in SAFE_METHODS:
    return True

3. Кастомное сообщение об ошибке разрешения

Из лекции (старое)Современное (DRF 3.0+)
# Нет кастомного сообщения
class IsOwnerOrReadOnly(BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in ['GET', 'HEAD', 'OPTIONS']:
            return True
        return obj.owner == request.user
# При отказе всегда: "You do not have permission..."
# С кастомным сообщением
class IsOwnerOrReadOnly(BasePermission):
    message = "Редактировать объект может только его владелец."

    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        return obj.owner == request.user

4. Проверка времени — наивный vs timezone-aware datetime

Из лекции (старое)Современное
# Из лекции — наивный datetime (не учитывает tz)
from datetime import datetime

class IsWorkHour(BasePermission):
    def has_object_permission(self, request, view, obj):
        current_hour = datetime.now().hour
        return 9 <= current_hour < 18
# Современно — с учётом timezone
from django.utils import timezone

class IsWorkHour(BasePermission):
    message = "Доступ доступен только с 9:00 до 18:00."

    def has_permission(self, request, view):
        # timezone-aware, учитывает TIME_ZONE из settings
        current_hour = timezone.localtime().hour
        return 9 <= current_hour < 18

5. Проверка is_staff vs is_superuser

ПаттернЗначение
request.user.is_staff Пользователь может заходить в Django Admin. Используется в IsAdminUser. Рекомендуется для обычного административного доступа.
request.user.is_superuser Суперпользователь — все разрешения по умолчанию. Редко нужен явно в permissions.

6. DjangoModelPermissions — обработка view-разрешения

DRF < 3.9 (старое)DRF 3.9+ (современное)
GET-запросы не требовали разрешения view — любой аутентифицированный мог читать По умолчанию GET не требует view-разрешения, но можно переопределить через perms_map
# Кастомизация perms_map в DjangoModelPermissions
class StrictModelPermissions(DjangoModelPermissions):
    perms_map = {
        'GET': ['%(app_label)s.view_%(model_name)s'],   # требуем view для GET
        'OPTIONS': [],
        'HEAD': ['%(app_label)s.view_%(model_name)s'],
        'POST': ['%(app_label)s.add_%(model_name)s'],
        'PUT': ['%(app_label)s.change_%(model_name)s'],
        'PATCH': ['%(app_label)s.change_%(model_name)s'],
        'DELETE': ['%(app_label)s.delete_%(model_name)s'],
    }