Подтвердить что ты не робот

Разрешения Django-rest-framework для создания в viewet

Я пытаюсь создать REST API и застреваю при регистрации пользователя: в основном мне нужно иметь токен доступа, прежде чем я зарегистрируюсь.

Это представление:

class UserViewSet(viewsets.ModelViewSet):

    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def metadata(self, request):
        """
        Don't include the view description in OPTIONS responses.
        """
        data = super(UserViewSet, self).metadata(request)
        return data

    def create(self, request):
        serializer = self.get_serializer(data=request.DATA, files=request.FILES)

        if serializer.is_valid():
            self.pre_save(serializer.object)
            self.object = serializer.save(force_insert=True)
            self.post_save(self.object, created=True)
            self.object.set_password(self.object.password)
            self.object.save()
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED,
                            headers=headers)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Это обходной путь:

@api_view(['POST'])
@permission_classes((AllowAny,))
@csrf_exempt
def create_auth(request, format=None):
    data = JSONParser().parse(request)
    serialized = UserSerializer(data=data)

    if serialized.is_valid():
        user = User.objects.create_user(
            serialized.init_data['email'],
            serialized.init_data['username'],
            serialized.init_data['password'],
        )
        user.groups = serialized.init_data['groups']

        user.save()

        serialized_user = UserSerializer(user)
        return Response(serialized_user.data, status=status.HTTP_201_CREATED, headers={"Access-Control-Allow-Origin": "http://127.0.0.1:8000/"})
    else:
        return Response(serialized._errors, status=status.HTTP_400_BAD_REQUEST, headers={"Access-Control-Allow-Origin": "http://127.0.0.1:8000/"})

Мой вопрос: как я могу указать в UserViewSet, что для создания я не требую учетных данных? Или указать собственный метод проверки подлинности? Я не хочу изменять классы проверки подлинности/разрешения для всего вида.

Спасибо, Adi

ИЗМЕНИТЬ для уточнения: незарегистрированным пользователям должно быть разрешено регистрировать данные POST, и им не разрешается ничего другого. Аутентифицированные пользователи могут получить список пользователей и обновить свой собственный профиль... это поведение по умолчанию. Вот почему AllowAny имеет не вариант. На мой взгляд, подходящим местом для этого является функция create, но я не понимаю, что я должен отменить.

4b9b3361

Ответ 1

Настроить метод get_queryset:

def get_queryset(self):
    if self.request.user.is_superuser:
        return User.objects.all()
    else:
        return User.objects.filter(id=self.request.user.id)

Таким образом, аутентифицированный пользователь может только извлекать, изменять или удалять свой собственный объект.

Укажите permission_classes = (AllowAny,), чтобы аутентифицированный пользователь мог создать новый.

EDIT: дальнейшее объяснение из комментариев

Настройка метода get_queryset таким образом означает следующее:

  • Да, пользователи, не прошедшие проверку подлинности, могут отправлять запрос GET для извлечения списка пользователей, но он будет пустым, поскольку возврат User.objects.filter(id = self.request.user.id) гарантирует, что только информация об аутентифицированном пользователе.

  • То же самое относится к другим методам, если аутентифицированный пользователь пытается УДАЛИТЬ другой объект пользователя, деталь: Не найден будет возвращена (поскольку пользователь, к которому он пытается получить доступ, не находится в наборе запросов).

  • Аутентифицированные пользователи могут делать все, что захотят, для своих пользовательских объектов.

Ответ 2

Вы можете использовать способность Django REST Framework для определения пользовательских разрешений. Вы можете указать как has_permission, так и has_object_permission в пользовательском классе. Это даст вам ожидаемое поведение броска 403s для всех пользователей, за исключением публикации в конечной точке создания. Это может выглядеть примерно так:

class IsAnonCreate(permissions.BasePermission):
    def has_permission(self, request, view):
        if request.method == "POST" and not request.user.is_authenticated():
            return True
        elif not request.user.is_authenticated() and request.method != "POST":
            return False
        elif request.method in permissions.SAFE_METHODS:
            return True

        return False

    def has_object_permission(self, request, view, obj):
        if not request.user.is_authenticated():
            return False
        if request.method in permissions.SAFE_METHODS:
            return True

        return obj.username == request.user.username

Затем вы можете добавить некоторую специальную обработку для аутентифицированных пользователей.

Затем все, что вам нужно сделать, это добавить класс разрешений к вашему ModelViewSet:

class UserViewSet(viewsets.ModelViewSet):

    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (IsAnonCreate, )

Ответ 3

Это основано на ответе @argaen, и это сработало для меня:

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    permission_classes = (AllowAny,)
    authentication_classes = (NoAuthentication,)
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('id', 'email', 'name')

    def get_queryset(self):
        user = TokenAuthentication().authenticate(self.request)
        if user is not None:
            user = user[0]
            if user.is_superuser:
                return get_user_model().objects.all()
            else:
                return get_user_model().objects.filter(id=user.id)

        return get_user_model().objects.none()