I am trying to create a REST API and am stuck at user registration: basically I need to have the access token before I register.
This is the view:
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)
This is the workaround:
@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/"})
My question is: How can I specify in the UserViewSet that for the create I don't require credentials? Or specify a custom authentication method? I don't want to change the authentication/permission classes for the whole viewset.
Thanks, Adi
EDIT to clarify: unregistered users should be allowed to POST registration data and should not be allowed anything else. Authenticated users can get the user list and update their own profile...this is the default behaviour. This is why AllowAny is not an option. In my view, the proper place for this is the create function, but I don't get what I am supposed to override.
You could utilizing Django REST Framework's ability to define custom permissions. You can specify both a
has_permission
andhas_object_permission
within a custom class. This will give you the expected behavior of throwing 403s to anon users for everything except posting to the creation endpoint. It might look something like:You could then add some custom handling for authenticated users if you wanted.
Then all you need to do is add the permission class to your
ModelViewSet
:Customize the get_queryset method:
This way, an authenticated user can only retrieve, modify or delete its own object.
Specify the
permission_classes = (AllowAny,)
so an authenticated user can create a new one.EDIT: further explanation from comments
Customizing the get_queryset method this way means the following:
Yes, non-authenticated users can send the GET request to retrieve the user list but it will be empty because the return User.objects.filter(id=self.request.user.id) ensures that only information about the authenticated user is returned.
The same applies for other methods, if an authenticated user tries to DELETE another user object, a detail: Not found will be returned (because the user it is trying to access is not in the queryset).
Authenticated users can do whatever they want to their user objects.
This is based on @argaen answer and it worked for me: