I'm having the following problem: the login request returns "Unable to login with provided credentials" after I do a PUT request (changing fields like first_name, last_name, address) even though the username and password are correct in the DB.
The following view I use to make my Login Request.
class RetrieveAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
resp = Response({
'token': token.key,
'user_id': user.pk,
'email': user.email,
'role': user.role,
})
return resp
And these are for my users and registration of a user:
from users.models import User
from users.serializers import UserSerializer, RegistrationSerializer
from rest_framework import viewsets, generics
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
# Create your views here.
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
class RegistrationView(generics.CreateAPIView):
model = User
serializer_class = RegistrationSerializer
My serializers look like this:
from rest_framework import serializers, status
from users.models import User
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'})
class Meta:
model = User
fields = '__all__'
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = super(UserSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
def update(self, instance, validated_data):
instance = super(UserSerializer, self).update(instance, validated_data)
instance.set_password(validated_data['password'])
instance.save()
return instance
class RegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'}, write_only=True)
def create(self, validated_data):
user = super(RegistrationSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = User
fields = ['email', 'username', 'password']
It looks like the problem is coming from the serializer:
Solved the problem guys! So, the problem was coming from the is_active field from User. On my update method from the serializer, that field was set to False after I did that request. In order to solve this, on my update method I set the is_active field to True.
def update(self, instance, validated_data):
instance = super(UserSerializer, self).update(instance, validated_data)
instance.set_password(validated_data['password'])
instance.is_active = True
instance.save()
return instance
Related
Im having a lot of problems in my first django, rest-framework application.
Im not getting the token after Im signing up, instead of
"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?
When Im trying to sign in, postman tells me:
{"username" :["user with this username already exist.]}
and Im trying to sign in not sign up.
why is this happening?
When Im trying to get all the todos that belongs to the user I get this error:
{"details":"Authentication credantials were not provided" }
, instead of an empty list, why is that?
how can I get the user if I have only the token?
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'))
]
Check output:
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.
view.py
class UserViewSet(viewsets.ModelViewSet):
""" API endpoint that allows users to be viewed or edited. """
serializer_class = UserSerializer
queryset = UserRegister.objects.all()
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.UpdateRegister,)
This is the view.py file. Here i have added authentication and permission to my view set.
permission.py
class UpdateRegister(permissions.BasePermission):
""" Allow user to edit their own profile. """
def has_object_permissions(self, request, view, obj):
""" Check user is trying to edit their own profile. """
if request.method in permissions.SAFE_METHODS:
return True
return obj.id == request.user.id
serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = UserRegister
fields = ('id', 'name', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
""" Create and return a new user"""
user = UserRegister(
email=validated_data['email'],
name=validated_data['name'],
)
user.set_password(validated_data['password'])
user.save()
return user
Error shown in network tab
So guy i am being bother by this error. Hope you guys will address my mistake and help me to solve the error.
POST request for creating user is working fine, but when I preform PUT method on a user and change the password I'm getting Invalid password format or unknown hashing algorithm, so I'm a bit confused why is this happening, so can someone please help me overcome this.
MyUserSerializer
from rest_framework import serializers
from business_accounts.models.my_user import MyUser
class MyUserSerializer(serializers.ModelSerializer):
"""
TODO: MyUser model Serializers
:return: TODO
"""
password = serializers.CharField(min_length=8, write_only=True)
class Meta:
model = MyUser
fields = '__all__'
def create(self, validated_data):
user = super(MyUserSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
User Detailed APIView
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from ..serializers.my_user_serializers import MyUserSerializer
from business_accounts.models.my_user import MyUser
class UserDetailView(APIView):
"""
User detail api view
"""
def get_object(self, pk):
try:
return MyUser.objects.get(pk=pk)
except MyUser.DoesNotExist:
raise Http404
def get(self, request, pk):
user = self.get_object(pk)
serializer = MyUserSerializer(user)
return Response(serializer.data)
def put(self, request, pk):
user = self.get_object(pk)
serializer = MyUserSerializer(user, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
user = self.get_object(pk)
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
You need to use hashing algorithm.
check -> this
You need to override serializer's update method also to correctly set password for PUT request:
class MyUserSerializer(serializers.ModelSerializer):
"""
TODO: MyUser model Serializers
:return: TODO
"""
password = serializers.CharField(min_length=8, write_only=True)
class Meta:
model = MyUser
fields = '__all__'
def create(self, validated_data):
user = super(MyUserSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
def update(self, instance, validated_data):
password = validated_data.pop('password')
user = super(MyUserSerializer, self).update(instance, validated_data)
if password:
user.set_password(password)
user.save()
return user
Note set_password method is using for password hasing.
I know that this is a frequent topic, but however, with all the resources available on the web and stackoverflow, I couldn't get my form to work properly.
I get this error : Exception Type: RelatedObjectDoesNotExist, Exception Value:
User has no profile.
Here is my setup in Django.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if kwargs.get('created', False):
Profile.objects.create(user=kwargs['instance'])
post_save.connect(create_user_profile,sender=User)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, created,**kwargs):
if created:
instance.profile.save()
forms.py
from django.contrib.auth.models import User
from django import forms
from django.contrib.auth.forms import UserCreationForm
class SignUpForm(UserCreationForm):
birth_date = forms.DateField()
location = forms.CharField()
password1 = forms.CharField(label=("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(label=("Confirm password"), widget=forms.PasswordInput)
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'location','email', 'birth_date')
widgets = {
'birth_date': forms.DateInput(attrs={'class':'datepicker'}),
}
labels = {
'username': ('Capser name'),
}
help_texts = {
'username' : None,
'birth_date': None,
}
views.py
class UserFormView(View):
form_class = SignUpForm
template_name = 'home/registration_form.html'
#display a blank form
def get(self, request):
form = self.form_class(None)
return render (request, self.template_name, {'form': form})
#process form data
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
#user.refresh_from_db() # load the profile instance created by the signal
password = form.cleaned_data['password1']
user.set_password(password)
username = form.cleaned_data['username']
user.profile.birth_date = form.cleaned_data.get('birth_date')
user.profile.location = form.cleaned_data.get('location')
user.save()
#return user objects if credentials are correct
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect('home:home')
return render (request, self.template_name, {'form': form})
And if I uncomment the commented lins user.refresh_from_db(), i get the following error : Exception Type: DoesNotExist, Exception Value:
User matching query does not exist.
I'm not sure at all, but I suspect that the signals in the models.py are not working properly.
Anyone could help ?
First of all, you can't assign: user.profile, because user is an instance of the model User, which does not have this attribute.
I would not recommend this structure that you are using to create profile after creating a User using signals. You'd better create the User with its attributes (username, password, email) and then create the Profile instance.
user.save()
Profile.objects.create(
user=user,
location=form.cleaned_data.get('location'),
birth_date=form.cleaned_data.get('birth_date'))
If you still want to use signals to this task, you should search for "How to pass arguments by signals". So, you would pass location and birth_date by a dictionary and use this data to create a Profile.
I want to return an auth token in json after successful user registration. How can I do this ?
For registration I use the following
seriazilers.py
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = [
'id',
'username',
'password',
'email',
]
write_only_fields = ('password',)
read_only_fields = ('id',)
def create(self, validated_data):
user = User.objects.create(
username=validated_data['username'],
)
user.set_password(validated_data['password'])
user.save()
return user
views.py
class CreateUser(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = UserSerializer
The are many ways to do this. Here is example in context of your existing code. (put this in your views.py)
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from rest_framework import status
class CreateUser(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = UserSerializer
def create(self, request, *args, **kwargs): # <- here i forgot self
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
token, created = Token.objects.get_or_create(user=serializer.instance)
return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=headers)
Here's a simple solution for the user when he/she wants to login/sign-in
first of all download django-rest-framework-jwt with pip
pip install djangorestframework-jwt
in your UserSerializer add this to make sure the username and password are correct (add as many fields as you wish)
username = serializers.CharField(read_only=True)
password = serializers.CharField(read_only=True)
Now in your view.py add this
# authenticate: will check if the user exist
from django.contrib.auth import authenticate
# api_settings: will help generating the token
from rest_framework_jwt.settings import api_settings
def login_page(request):
payload_handler = api_settings.JWT_PAYLOAD_HANDLER
encode_handler = api_settings.JWT_ENCODE_HANDLER
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = authenticate(username=request.data['username'], password=request.data['password'])
if user:
payload = payload_handler(user)
token = encode_handler(payload)
return Response({'token': token})
And mainly that's it! hope it helps!