Im having a lot of problems in my first django, rest-framework application.
"user" : { "_id":"1", "username":"you", "email":"a@a.com" }, "token" : 'fjdjfkljgkghgjkl' }
Im getting
{
"_id":"1",
"username":"you",
"email":"a@a.com"
}
what could be the problem here?
{"username" :["user with this username already exist.]}
and Im trying to sign in not sign up. why is this happening?
{"details":"Authentication credantials were not provided" }
, instead of an empty list, why is that?
serializer code:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('_id', 'username', 'email', 'password')
extra_kwarg = { 'password' : {
'write_only' : True
}}
class SignUpSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('_id', 'username', 'email', 'password')
extra_kwarg = { 'password' : {
'write_only' : True
}}
def create_user(self, validated_data):
user = User.objects.create_user(validated_data['username'], validated_data['email'], validated_data['password'])
return user
class SignInSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'password')
def validate(self, data):
username = data.get("username", None)
password = data.get("password", None)
user = authenticate(username=username, password=password)
if user is None:
raise serializers.ValidationError('A user with this username and password is not found.')
return user
viewset code:
class SignUpViewSet(viewsets.ModelViewSet):
serializer_class = SignUpSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data = request.data)
serializer.is_valid(raise_exception = True)
user = serializer.save()
return Response({
'user': SignUpSerializer(user, context = self.get_serializer_context()).data,
'token': AuthToken.objects.create(user)
})
class SignInViewSet(viewsets.ModelViewSet):
serializer_class = SignInSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data = request.data)
serializer.is_valid(raise_exception = True)
user = serializer.validated_data
return Response({
'user': UserSerializer(user, context = self.get_serializer_context()).data,
'token': AuthToken.objects.create(user)
})
class GetUserViewSet(viewsets.ModelViewSet):
permission_classes = [
permissions.IsAuthenticated
]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
Issues which you have listed are mainly cause of point 2
.
{"username" :["user with this username already exist.]}
If you read it more carefully, it says user already exist. So it must be trying to create a user
instead of letting them login. And this behavior is excepted because the way you have used serializer in your SignInViewSet
.
class SignInViewSet(viewsets.ModelViewSet):
serializer_class = SignInSerializer
def post(self, request, *args, **kwargs):
serializer = SignInSerializer()
user = serializer.validate(attrs=request.data)
return Response({
'user': UserSerializer(user, context = self.get_serializer_context()).data,
'token': AuthToken.objects.create(user)
})
I have removed get_serializer
because I am not fimilar with it, but it should work fine. Main take away is that use serializer.is_valid()
only when you are updating
or creating
stuffs in database. As you can see right now it is trying to create a new user instance and that's why the error user with this username already exists
. After this you should have your token
and then set Headers
in your Postman
, if everything goes well you can access your user with request.user
in your views.
UPDATE:
views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from knox.models import AuthToken
from .serializers import SignInSerializer, SignUpSerializer, UserSerializer
# Create your views here.
class SignUpView(APIView):
serializer_class = SignUpSerializer
def post(self, request):
serializer = SignUpSerializer(data = request.data)
if serializer.is_valid():
user = serializer.save()
_, token = AuthToken.objects.create(user)
return Response({'user': serializer.data, 'token': token})
else:
return Response(serializer.errors)
serializers.py
class SignUpSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
extra_kwarg = { 'password' : {'write_only' : True }}
def create_user(self, validated_data):
user = User.objects.create_user(username=validated_data['username'], email=validated_data['email'], password=validated_data['password'])
user.save()
return user
Since you are not using viewsets
I have shifted SignUpView
to views.py
and I have removed your extra validation from serializers
as it won't work, instead if there is any error in your serializer it will be handled in your views, check if serializer.is_valid()
and else
statement. And your urls.py
in user
app will be something like this.
from rest_framework import routers
from .views import SignUpView
from django.urls import path
# user_router = routers.DefaultRouter()
# user_router.register(r'sign_up', SignUpViewSet, basename='sign_up')
# user_router.register(r'sign_in', SignInViewSet, basename='sign_in')
# user_router.register(r'user', GetUserViewSet, basename='user')
urlpatterns = [
path('sign_up/', SignUpView.as_view()),
]
and backend/urls.py
will be
from django.contrib import admin
from django.urls import include,path
from todo.urls import todo_router
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('api/todo/', include(todo_router.urls)),
path('api/auth/', include('user.urls')),
path('', TemplateView.as_view(template_name = 'index.html'))
]
NOTE: self.get_serializer(data = request.data)
is only available for ViewSets
, that's why views.py
has been edited. You can similarly update your other views too.