💻 Примеры — Урок 42

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

⚡ Ключевые примеры

# permissions.py — IsOwnerOrReadOnly
from rest_framework.permissions import BasePermission, SAFE_METHODS

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

# views.py — автоматический владелец
class BookViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

    def get_queryset(self):
        return Book.objects.filter(owner=self.request.user)

Пример 1: Полный проект с полем owner

Полная реализация: модель с owner, сериализатор, permissions, представления, маршруты.

models.py

# myapp/models.py
from django.db import models
from django.contrib.auth.models import User

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.CharField(max_length=100)
    published_date = models.DateField()
    price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    is_bestseller = models.BooleanField(default=False)
    owner = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='books'
    )

    def __str__(self):
        return f"{self.title} (owner: {self.owner.username})"

permissions.py

# myapp/permissions.py
from rest_framework.permissions import BasePermission, SAFE_METHODS

class IsOwnerOrReadOnly(BasePermission):
    """
    Разрешает редактирование объектов только их владельцам.
    Остальным — только чтение.
    """
    def has_object_permission(self, request, view, obj):
        # SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
        if request.method in SAFE_METHODS:
            return True
        return obj.owner == request.user

serializers.py

# myapp/serializers.py
from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
        read_only_fields = ['owner']  # клиент не передаёт owner вручную

views.py

# myapp/views.py
from rest_framework import viewsets
from rest_framework.generics import ListAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.permissions import IsAuthenticated
from .models import Book
from .serializers import BookSerializer
from .permissions import IsOwnerOrReadOnly

# ViewSet для списка и создания
class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

    def perform_create(self, serializer):
        # Автоматически назначаем текущего пользователя владельцем
        serializer.save(owner=self.request.user)

# Представление для книг текущего пользователя
class UserBookListView(ListAPIView):
    serializer_class = BookSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        # Только книги текущего пользователя
        return Book.objects.filter(owner=self.request.user)

# Детальное представление с проверкой владельца
class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

urls.py

# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet, UserBookListView, BookDetailView

router = DefaultRouter()
router.register(r'books', BookViewSet, basename='book')

urlpatterns = [
    path('', include(router.urls)),
    path('my-books/', UserBookListView.as_view(), name='user-books'),
    path('books-detail/<int:pk>/', BookDetailView.as_view(), name='book-detail'),
]

Тестирование через Postman

# 1. Создать книгу (POST /books/)
# Header: Authorization: Bearer <access_token>
# Body: {"title": "My Book", "author": "John Doe", "published_date": "2024-01-01"}
# owner будет установлен автоматически

# 2. Просмотр (GET /books/1/) — доступен всем аутентифицированным
# Header: Authorization: Bearer <любой_токен>
# Response: {"id": 1, "title": "My Book", "owner": 1, ...}

# 3. Изменение не владельцем (PUT /books/1/)
# Header: Authorization: Bearer <токен_другого_пользователя>
# Response 403: {"detail": "You do not have permission to perform this action."}

# 4. Получить только свои книги (GET /my-books/)
# Header: Authorization: Bearer <мой_токен>
# Response: [...только мои книги...]

Пример 2: IsAdminOrOwner — администраторы или владельцы

# myapp/permissions.py
from rest_framework.permissions import BasePermission, SAFE_METHODS

class IsAdminOrOwner(BasePermission):
    """
    Администраторы могут делать всё.
    Обычные пользователи могут изменять только свои объекты.
    Чтение доступно всем аутентифицированным.
    """
    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        # Администратор или владелец
        return request.user.is_staff or obj.owner == request.user

Пример 3: IsWorkHour — ограничение по времени

# myapp/permissions.py
from rest_framework.permissions import BasePermission
from datetime import datetime

class IsWorkHour(BasePermission):
    """
    Доступ к API только в рабочие часы (9:00–18:00).
    """
    message = "Доступ к этому ресурсу доступен только с 9:00 до 18:00."

    def has_permission(self, request, view):
        current_hour = datetime.now().hour
        return 9 <= current_hour < 18
Обратите внимание: в примере из лекции использован has_object_permission, но для ограничения по времени логичнее использовать has_permission — чтобы проверка применялась ещё до обращения к объекту.

Пример 4: DjangoModelPermissions в ViewSet

# myapp/views.py
from rest_framework import viewsets
from rest_framework.permissions import DjangoModelPermissions
from .models import Genre
from .serializers import GenreSerializer

class GenreViewSet(viewsets.ModelViewSet):
    queryset = Genre.objects.all()
    serializer_class = GenreSerializer
    permission_classes = [DjangoModelPermissions]
    # Теперь Django Admin-разрешения управляют доступом к API

Настройка пользователя в Django Admin:

  1. Перейдите Admin → Users → [пользователь]
  2. В разделе Permissions поставьте галочки для модели Genre
  3. Сохраните — теперь API проверяет эти разрешения для данного пользователя

Пример 5: Создание группы программно (через управляющую команду)

# myapp/management/commands/setup_groups.py
from django.core.management.base import BaseCommand
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from myapp.models import Book

class Command(BaseCommand):
    help = 'Создать группы пользователей с разрешениями'

    def handle(self, *args, **kwargs):
        content_type = ContentType.objects.get_for_model(Book)

        # Создать группу "Editors"
        editors, _ = Group.objects.get_or_create(name='Editors')
        view_perm = Permission.objects.get(content_type=content_type, codename='view_book')
        add_perm = Permission.objects.get(content_type=content_type, codename='add_book')
        change_perm = Permission.objects.get(content_type=content_type, codename='change_book')
        editors.permissions.set([view_perm, add_perm, change_perm])

        # Создать группу "Readers"
        readers, _ = Group.objects.get_or_create(name='Readers')
        readers.permissions.set([view_perm])

        self.stdout.write(self.style.SUCCESS('Группы созданы успешно'))
python manage.py setup_groups