Django создает разрешения для каждой создаваемой вами модели, например:
can_view_{model_name}
can_add_{model_name}
can_edit_{model_name}
Изначально они применимы только к администратору Django. Хорошо, если я хочу применить их на уровне модели, почему я не могу это сделать:
class MyModel(models.Model)
def can_view(self, user):
if user.has_perm('my_app.can_view_my_model'):
return True
return False
И затем каждый раз, когда ORM пытается найти эту модель, он должен сначала проверить разрешение.
Вместо этого мне приходится заходить в каждое представление и вручную проверять:
class MyModelDetail(APIView):
@transaction.atomic
def get(self, request):
try:
if not request.user.has_perm("my_app.can_view_my_model"):
raise APIException("You do not have permission to view this model")
И повторите это для всех представлений, которые ищут мою модель.
Есть ли более простой способ?
Это применимо только к просмотру списка. «Это разрешение должно применяться только к представлениям, имеющим свойство .queryset или метод get_queryset()»
Любой общий вид, в том числе детальный, также имеет это. Вам нужно не только использовать представления списков.
Я не вижу там APIView
. Вы предлагаете мне разбить APIView
на его общие классы? не понимаю, чем это лучше..
Да. Альтернативно, вместо использования общего представления, вы можете определить queryset
самостоятельно.
«Это разрешение должно применяться только к представлениям, имеющим свойство .queryset или метод get_queryset()» относится к разрешениям >object<. В предыдущем разделе объясняется, как обращаться с проверками разрешений, которые можно выполнить с помощью стандартных классов разрешений. В следующем разделе объясняется, как создавать собственные классы разрешений. Ваш пример не похож на разрешение объекта.
И чтобы ответить на ваш вопрос «почему нельзя?» эти разрешения относятся к конкретным функциям страниц администрирования. Расширение их для более общей семантики - это... не то, чего хотели бы все разработчики Django. (В идеальном мире, возможно, Django мог бы иметь более единообразную структуру авторизации/разрешения для веб-страниц, остальных страниц, интерфейса администратора и т. д. Но вам также необходимо учитывать всю историю Django при критике дизайнерских решений. Назад совместимость является серьезной проблемой.)
Вы можете это сделать, вам просто нужно будет реализовать это в APIView, так что добавьте это.
@NickODell Я не понимаю, что ты предлагаешь. Определить набор запросов в APIView?
@StephenC Я не понимаю, что показано в APIView. Возможно, вы сможете дать ответ. Я не критикую дизайнерские решения, но когда клиент спрашивает, можем ли мы гарантировать, что пользователь не сможет создать объект на уровне модели, я должен сказать... нет, это не поддерживается Django... нет. проезд очень хорошо.
@willeM_VanOnsem Вы не можете просто добавить DjangoModelPermissions в APIView, поскольку он будет жаловаться, что «невозможно применить DjangoModelPermissions к представлению, которое не устанавливает .queryset
или не имеет .get_queryset()
метода».
@AlxVallejo: Я говорю о has_object_permission
: django-rest-framework.org/api-guide/permissions/… вы можете даже использовать это как «механизм двойной объектно-ориентированной диспетчеризации», если хотите.
Использование простого .get
, конечно, не сработает, поскольку тогда вы по сути обходите разрешения, но для GenericAPIView
или ModelViewSet
это работает.
@StephenC Кроме того, я не понимаю, насколько вы правы в применении к разрешениям объекта. Это четко указано в DjangoModelPermissions, на что намекает верхний комментарий.
@willeM_VanOnsem Хотя я не думал, что это права доступа к объектам. Если у вас есть решение, опубликуйте его, и я попробую.
@AlxVallejo: базовое разрешение имеет два метода: has_permission
для первой проверки запроса без объекта и для объектно-ориентированных запросов (например, получение, обновление и т. д. одного объекта) затем используется .has_object_permission
.
Что касается разрешения can_add
, это выглядит странно, поскольку оно не связано с объектом: вы не добавляете объект модели, возможно, сам класс модели.
@willeM_VanOnsem Это все здорово и все такое, но если не будет предложено какое-то конкретное решение, я, к сожалению, не понимаю, как что-то будет лучше простой проверки разрешений для каждого метода просмотра.
В платформе Django REST есть классы BasePermission [drf-doc], которые можно использовать для этого. Такое разрешение может выглядеть так:
from rest_framework import permissions
class AdminModelPermission(permissions.BasePermission):
METHOD_MAPPING = {
'GET': 'view',
'POST': 'add',
'PUT': 'edit',
'PATCH': 'edit',
'DELETE': 'delete',
}
def has_permission(self, request, view):
meta = view.get_queryset().model._meta
action = self.METHOD_MAPPING.get(request.method)
if action is not None:
return request.user.has_perm(
f'{meta.app_label}.{action}_{meta.model_name}'
)
return True
def has_object_permission(self, request, view, obj):
action = self.METHOD_MAPPING.get(request.method)
if action is not None:
method = getattr(obj, f'can_{action}', None)
if method is not None:
return method(request.user)
return True
а затем вставьте это в GenericAPIView
, GenericViewSet
или ModelViewSet
, например:
from rest_framework import viewsets
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
permission_classes = (AdminModelPermission,)
Если мы теперь, например, отправим запрос DELETE, он сначала проверит, есть ли у пользователя разрешение app_label.delete_mymodel
. Если да, он извлечет объект, а если сама модель имеет метод .can_delete(…)
, она вызовет его для вошедшего в систему пользователя, чтобы проверить, может ли он удалить этот конкретный объект. Если так, то, наконец, оно продолжится.
Однако важно, по крайней мере, получить объект через .get_object(…)
, поскольку именно здесь платформа REST Django проверяет .has_object_permission(…)
всех установленных разрешений и, кроме того, позволяет ей самой выполнить диспетчеризацию для вызова метода .has_permission(…)
.
ОК, это кажется отличным подходом, которого я больше нигде не видел. Я отчитаюсь о том, чем это обернется.
Пробовали ли вы использовать класс разрешений DjangoModelPermissions? См. django-rest-framework.org/api-guide/permissions/… и stackoverflow.com/questions/46584653/…