💻 Примеры — Урок 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:
- Перейдите Admin → Users → [пользователь]
- В разделе Permissions поставьте галочки для модели Genre
- Сохраните — теперь 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