Разрешение Django для каждого действия
Я новичок в разработке с Django + Django Rest-framework, и я работаю над проектом, который обеспечивает доступ REST Api. Мне было интересно, что лучше всего назначить другое разрешение для каждого действия данного ApiView или Viewset.
Предположим, что я определил некоторые классы разрешений, такие как "IsAdmin", "IsRole1", "IsRole2",... и я хочу предоставить разные разрешения для отдельных действий (например, пользователь с Role1 может создавать или извлекать, пользователь с Role2 может обновлять, и только администратор может удалить).
Как я могу структурировать представление на основе класса, чтобы назначить класс разрешений для действий "create", "list", "retrieve", "update", "delete"?
Я пытаюсь сделать это, чтобы иметь класс, который можно использовать повторно для разных таблиц с одинаковым шаблоном разрешения.
Возможно, я просто утонул в дюйме воды, спасибо за ваши ответы.
Ответы
Ответ 1
Вы можете создать собственный класс разрешений, расширяющий DRF BasePermission
.
Вы реализуете has_permission
, где у вас есть доступ к объектам request
и view
. Вы можете проверить request.user
для соответствующей роли и вернуть True
/False
в зависимости от ситуации.
Посмотрите на предоставленный класс IsAuthenticatedOrReadOnly (и другие) для хорошего примера того, насколько это просто.
Я надеюсь, что это поможет.
Ответ 2
В документации DRF,
Примечание. Метод has_object_permission на уровне экземпляра будет вызываться только в том случае, если проверки уровня_изличения уже прошли
Предположим следующее разрешение о user
object
- Список: только для персонала
- Создать: любой
- Получить: самостоятельно или персонал
- Обновление, частичное обновление: собственная личность или персонал.
- Уничтожьте: только персонал
<я > permissons.py
from rest_framework import permissions
class UserPermission(permissions.BasePermission):
def has_permission(self, request, view):
if view.action == 'list':
return request.user.is_authenticated() and request.user.is_admin
elif view.action == 'create':
return True
elif view.action in ['retrieve', 'update', 'partial_update', 'destroy']:
return True
else:
return False
def has_object_permission(self, request, view, obj):
if view.action == 'retrieve':
return request.user.is_authenticated() and (obj == request.user or request.user.is_admin)
elif view.action in ['update', 'partial_update']:
return request.user.is_authenticated() and (obj == request.user or request.user.is_admin)
elif view.action == 'destroy':
return request.user.is_authenticated() and request.user.is_admin
else:
return False
<я > views.py
from .models import User
from .permissions import UserPermission
from .serializers import UserSerializer
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (UserPermission,)
Ответ 3
Django имеет класс persmissions, называемый DjangoObjectPermissions, который использует Django Guardian в качестве сервера аутентификации.
Когда у вас есть хранитель Django, активный в ваших настройках, вы просто добавляете permission_classes = [DjandoObjectPermissions]
к своему представлению и автоматически выполняете авторизацию прав, поэтому вы можете "CRUD" на основе разрешения, установленного для определенной группы или пользователя django.contrib.auth
.
См. gist с примером.
Вы можете установить Django Guardian в качестве вашей аутентификации http://django-guardian.readthedocs.org/en/latest/installation.html
Ответ 4
Представления класса на основе RestFramework имеют методы для каждого HTTP-глагола (т.е. HTTP GET = > view.get() и т.д.). Вам просто нужно использовать разрешения django.contrib.auth, пользователей, группы и декораторы, как описано.
Ответ 5
Я лично ненавижу такие специфические разрешения frankenmonster, на мой взгляд, это не очень идиоматично, когда дело доходит до рамки Django;
Поэтому я придумал следующее решение - он очень похож на то, как работают декораторы @list_route и @detail_route.
Мы полагаемся на то, что методы/функции являются объектами первого класса
Прежде всего, я создаю такой dectorator:
decorators.py
def route_action_arguments(**kwargs):
"""
Add arguments to the action method
"""
def decorator(func):
func.route_action_kwargs = kwargs
return func
return decorator
Как вы можете видеть, он добавляет словарь к функции, которую он украшает параметрами, переданными как список arg
Теперь я создал такой mixin:
mixins.py
class RouteActionArgumentsMixin (object):
"""
Use action specific parameters to
provide:
- serializer
- permissions
"""
def _get_kwargs(self):
action = getattr(self, 'action')
if not action:
raise AttributeError
print('getting route kwargs for action:' + action)
action_method = getattr(self, action)
kwargs = getattr(action_method, 'route_action_kwargs')
print(dir(kwargs))
return kwargs
def get_serializer_class(self):
try:
kwargs = self._get_kwargs()
return kwargs['serializer']
except (KeyError, AttributeError):
return super(RouteActionArgumentsMixin, self).get_serializer_class()
def get_permissions(self):
try:
kwargs = self._get_kwargs()
return kwargs['permission_classes']
except (KeyError, AttributeError):
return super(RouteActionArgumentsMixin, self).get_permissions()
Mixin делает две вещи;
когда get_permissions вызывается, он проверяет, который выполняется "действие", и looksup коллекции permission_classes от "route_action_kwargs", связанный с viewset.action_method.route_action_kwargs
когда вызывается get_serializer_class, он делает то же самое и выбирает "сериализатор" из "route_action_kwargs"
Теперь мы можем использовать его:
@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):
"""
User and profile managment viewset
"""
queryset = User.objects.all()
serializer_class = UserSerializer
@list_route(methods=['post'])
@route_action_arguments(permission_classes=(AllowAny,), serializer=LoginSerializer)
def login(self, request):
serializer = self.get_serializer_class()(data=request.data)
Для пользовательского маршрута мы явно определяем, что мы можем просто задать @route_action_arguments явно для метода.
В терминах общих представлений и методов мы все равно можем их добавить, используя
@method_decorator
@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):