I am trying to create a user creation api and post create api, the 'Get' request works fine but the post request is always empty. If there is anything I missed or need to add to the views, because the get request works fine for the post app. What could be the possible errors that are occuring.
Serializer.py
from rest_framework import serializers
from .models import User
class RegisterSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(
style={'input_type': 'password'}, write_only=True)
class Meta:
model = User
fields = ['username', 'email',
'date_of_birth', 'password', 'password2']
extra_kwargs = {'password': {'write_only': True}}
def save(self):
print("saved")
user = User(email=self.validated_data['email'],
username=self.validated_data['username'],
date_of_birth=self.validated_data['date_of_birth']
)
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError(
{'password': 'Passwords must match.'})
user.set_password(password)
user.save()
return user
Below is the views.py
from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .models import User
from .serializers import RegisterSerializer
from rest_framework.authtoken.models import Token
from rest_framework.parsers import JSONParser
#api_view(['POST', ])
def registration_view(request):
if request.method == 'POST':
serializer = RegisterSerializer(data=request.data)
data = {}
if serializer.is_valid():
print("Hello")
user = serializer.save()
data['response'] = 'Successfuly registered a new User'
data['date_of_birth'] = user.date_of_birth
data['username'] = user.username
data['email'] = user.email
token = Token.objects.get(user=user)
data['token'] = token
return Response(data)
else:
return Response(serializer.errors)
No matter the post request I always get an error saying this field is required,
Here is the error -- screenshot
Can anybody help me please,
Thank you
According to your screenshot you are sending data as query params with POST request. In Postman you should use body section with, for example, form-data option.
Related
I'm very new to Django and preparing for the DRF Session.
I have to write code in Serializers.py and views.py for Logout function but I don't know what to do and where to start.
Can you please help? Here's some code for register and login
Serialzers.py
from rest_framework import serializers
from .models import *
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'password']
def create(self, validated_data):
user = User.objects.create(
email=validated_data['email'], username=validated_data['username'],)
user.set_password(validated_data['password'])
user.save()
return user
class UserLoginSerializer(serializers.Serializer):
email = serializers.CharField(max_length=64)
password = serializers.CharField(max_length=128, write_only=True)
def validate(self, data):
email = data.get("email", None)
password = data.get("password", None)
if User.objects.filter(email=email).exists():
user = User.objects.get(email=email)
if not user.check_password(password):
raise serializers.ValidationError()
else:
return user
else:
raise serializers.ValidationError()
class UserLogoutSerializer(serializers.Serializer):
# I have to do this part
views.py
from django.shortcuts import get_object_or_404, render
from .serializers import *
from .models import *
from rest_framework import views
from rest_framework.response import Response
class SignUpView(views.APIView):
def post(self, request, format=None):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'message': 'Success', 'data': serializer.data})
return Response({'message': 'Fail', 'error': serializer.errors})
class LoginView(views.APIView):
def post(self, request):
serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid():
return Response({'message': "Success", 'data': serializer.data})
return Response({'message': "Fail", 'data': serializer.errors})
class LogoutView(views.APIView):
You do not necessarily have to use a serializer. Logout can be as simple as calling an endpoint once (logout just disables any active authorization token). Try this:
from django.contrib.auth import logout
class LogoutView(views.APIView):
def post(self, request):
logout(request)
return Response({'message': "Logout successful"})
It depends on how you are authenticating your user. From you code, I do not see how you actually authenticate your user (you only seem to check if the user exists, you do not give them an authorization token anywhere). Basically I am not sure how one would log out in your current architecture as there is no user logged in ever to log them out.
You might want to consider token authentication.
So, I'm trying to build simple register and login functionalities into my API. I can register just fine, but when I insert the user details on the login API view page, it gives me the "Expected a string value" error. I think this is probably because one of the credentials being passed somehow doesn't get stringified? I'm really not sure and I can't find much about it. Here's my code!
views.py
from django.shortcuts import render
from rest_framework.generics import GenericAPIView
from .serializers import UserSerializer, LoginSerializer
from rest_framework import status
from rest_framework.response import Response
from django.conf import settings
from django.contrib import auth
import jwt
class RegisterView(GenericAPIView):
serializer_class = UserSerializer
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class LoginView(GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
data = request.data
username = data.get('username', '')
password = data.get('password', '')
user = auth.authenticate(username=username, password=password)
if user:
auth_token = jwt.encode({'username': user.username}, settings.JWT_SECRET_KEY, algorithm="HS256")
serializer = UserSerializer(user)
data = {"user": serializer.data, "token": auth_token}
print(auth_token)
return Response(data, status=status.HTTP_200_OK)
### SEND RES
return Response({'detail':'Invalid Credentials'}, status=status.HTTP_401_UNAUTHORIZED)
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=65, min_length=8, write_only=True)
email = serializers.EmailField(max_length=220)
username = serializers.CharField(max_length=50)
class Meta:
model = User
fields = ['username', 'email', 'password']
def validate(self, attrs):
if User.objects.filter(email = attrs['email']).exists():
raise serializer.ValidationError({'email', ('email is already in use')})
return super().validate(attrs)
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class LoginSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=65, min_length=8, write_only=True)
username = serializers.CharField(max_length=220)
class Meta:
model = User
fields = ['username', 'password']
backends.py
import jwt
from django.contrib.auth.models import User
from rest_framework import authentication, exceptions
from django.conf import settings
class JWTAuth(authentication.BaseAuthentication):
def authenticate(self, request):
auth_data = authentication.get_authorization_header(request)
print(auth_data)
if not auth_data:
return None
prefix, token = auth_data.decode('utf-8').split('')
try:
payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms='HS256')
user = User.objects.get(username = payload['username'])
return (user, token)
except jwt.DecodeError as identifier:
raise exceptions.AuthenticationFailed('your token is invalid.')
except jwt.DecodeError as identifier:
raise exceptions.AuthenticationFailed('your token is expired.')
return super().authenticate(request
settings.py
#JWT
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY')
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api',
'rest_framework'
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'api.backends.JWTAuth',
)
}
This issue happens because os.env os.environ.get is return None
I too faced the same issue, make sure your .env file looks like this
export JWT_SECRET_KEY='your secret key here'
and .env file should be next to or in same level to settings.py
please refer to images below
[![enter image description here][1]][1]
and if you want some more fun to play with multiple environment variable check this resources
https://www.youtube.com/watch?v=ecshCQU6X2U
https://pypi.org/project/python-dotenv/
On your LoginView, the auth_token cannot be encoded because you have not defined the payload parameter as string.
You also need to define your secret key.
Change your code to look like this:
if user:
key='JWT_SECRET_KEY'
auth_token = jwt.encode ({'user':"payload"}, key, algorithm="HS256")
serializer = UserSerializer(user)
data={'user':serializer.data, 'token':auth_token}
return Response(data, status=status.HTTP_200_OK)
This should work.
I'm trying to create a well-rounded authentication/registration/login system in Django using the rest framework. I've slowly started wrapping my head on how to get it done well.
I want to be satisfied with my token situation, however after reading all the docs and looking at other Git repos, I've seem to dug myself in a hole of confusion. To my understanding, there are some libraries that do token authentication/refreshing for you, others give you the tools to create one.
I'm not sure if I have a solid base to build a token refresh function given my code below. How can I implement a token refresh system for users that log-in?
Here is my code, it works fine and I see registration data in my User model.
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=65, min_length=8, write_only=True)
email = serializers.EmailField(max_length=255, min_length=4)
username = serializers.RegexField("^(?!.*\.\.)(?!.*\.$)[^\W][\w.]{3,29}$")
first_name = serializers.RegexField("^[A-Za-z]+((\s)?((\'|\-|\.)?([A-Za-z])+))*$", max_length=32, min_length=2)
last_name = serializers.RegexField("^[A-Za-z]+((\s)?((\'|\-|\.)?([A-Za-z])+))*$", max_length=32, min_length=2)
class Meta:
model = User
fields = ['first_name', 'last_name', 'username', 'email', 'password']
def validate(self, attrs):
email = attrs.get('email', '')
if User.objects.filter(email=email).exists():
raise serializers.ValidationError(
{'email': ('Email is already in use')})
return super().validate(attrs)
def create(self, validated_data):
return User.objects.create_user(**validated_data)
backend.py [my authenitication system]
import jwt
from rest_framework import authentication, exceptions
from django.conf import settings
from django.contrib.auth.models import User
class JWTAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
auth_data = authentication.get_authorization_header(request)
if not auth_data:
return None
prefix, token = auth_data.decode('utf-8').split(' ')
try:
payload = jwt.decode(token, settings.JWT_SECRET_KEY)
user = User.objects.get(email=payload['email'])
return (email, token)
except jwt.DecodeError as identifier:
raise exceptions.AuthenticationFailed('Your token is invalid')
except jwt.ExpiredSignatureError as identifier:
raise exceptions.AuthenticationFailed('Your token has expired, please try again')
return super().authenticate(request)
views.py :
from django.shortcuts import render
from rest_framework import status
from rest_framework.generics import GenericAPIView
from .serializers import UserSerializer
from rest_framework.response import Response
from django.conf import settings
from django.contrib import auth
import jwt
# Create your views here.
class RegisterView(GenericAPIView):
serializer_class = UserSerializer
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class LoginView(GenericAPIView):
def post(self, request):
data = request.data
email = data.get('email','')
password = data.get('password','')
user = auth.authenticate(email=email, password=password)
if user:
auth_token = jwt.encode({'email':user.email}, settings.JWT_SECRET_KEY)
serializer = UserSerializer(user, many=True)
data = {
"email": serializer.data,
"token": auth_token,
}
return Response(data, status=status.HTTP_200_OK)
return Response({"detail": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED)
Should I re-do my authentication system to include a token refresh system?
When I try to make a POST request to my API endpoint to create a user in my Users table, I get mysterious Serializer errors. The error says that the email, password, codename (3 strings needed to create a user) are required. But I am sending all three strings. It is because the serializer is NOT valid (go to serializers.py, the if serializer.is_valid() check)... but I can't figure out why it's not valid.
Entire said error message:
{'codename': [ErrorDetail(string=u'This field is required.', code=u'required')], 'password': [ErrorDetail(string=u'This field is required.', code=u'required')], 'email': [ErrorDetail(string=u'This field is required.', code=u'required')]}
All these files are in my users folder within my Django directory.
serializers.py:
from rest_framework import serializers
from .models import User
class UserPostSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'password', 'codename')
views.py:
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from .serializers import *
from .models import User
from argon2 import PasswordHasher
import json
#api_view(['POST'])
def create_user(request):
print("inside create_user")
""" POST = Create user. """
data = {}
# print("request.data:\n", request.data)
serializer = UserPostSerializer(data=request.data)
print("got serializer")
if serializer.is_valid():
print("serializer is valid!")
email = serializer.data['email']
codename = serializer.data['codename']
password = serializer.data['password']
user = User.objects.filter(email=email)
if not user:
# Apply Argon2
ph = PasswordHasher()
hash = ph.hash(password)
# Create user
new_user = User(email=email, password=hash, codename=codename)
new_user.save()
request.session['email'] = email
request.session['id'] = new_user.pk
print(request.session['email'], "has logged in!")
print(request.session['id'], "user's id")
return Response(serializer.data, status=status.HTTP_200_OK)
# User with this email found... Please login...
else:
return Response(status=status.HTTP_409_CONFLICT)
else:
print(serializer.errors)
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
models.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class User(models.Model):
email = models.CharField(max_length=255, blank=False)
codename = models.CharField(max_length=255, blank=False)
password = models.CharField(max_length=255, blank=False)
profilePic = models.ImageField(upload_to='images/', blank=True)
I ended up finding a Stack Overflow post where they suggested to add extra_kwargs which specify that they are not required. This made my if serializers.is_valid() pass!
class UserPostSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'password', 'codename')
extra_kwargs = {'email': {'required': False}, 'password': {'required': False}, 'codename': {'required': False}}
I'm trying to create a profile for my user using django rest auth model, but when I'm sending the data to the user's creation, the user's image is not being filled, that is, it's getting null, I tried to send the node as much as image, as much as profile.image, but without success below my code follows:
models\profile.py
from django.conf import settings
from django.db import models
class Profile(models.Model):
image = models.ImageField(blank=True)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.user.email
serializers\user.py
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from django.contrib.auth.models import User
from src.v1.user.models.profile import Profile
from .profile import ProfileSerializer
class UserSerializer(serializers.ModelSerializer):
profile = serializers.SerializerMethodField()
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
username = serializers.CharField(
max_length=32,
validators=[UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(min_length=6, write_only=True)
#staticmethod
def get_profile(user):
"""
Get or create profile
"""
profile, created = Profile.objects.get_or_create(user=user)
return ProfileSerializer(profile, read_only=True).data
def create(self, validated_data):
user = User(email=validated_data['email'],
username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = User
fields = ('id', 'username', 'email', 'password', 'profile')
serializers\profile.py
from rest_framework import serializers
from src.v1.user.models.profile import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
class ProfileSerializerUpdate(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('image',)
views\user.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from src.v1.user.serializers.user import UserSerializer
from rest_framework.authtoken.models import Token
class UserView(APIView):
"""
Creates the user.
"""
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
token = Token.objects.create(user=user)
json = serializer.data
json['token'] = token.key
return Response(json, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
SerializerMethodField is read only by design. So it is not just the image, nothing is getting posted on the profile.
Also, creating the profile if it doesn't exist during a GET request is a bad design because generally, GET requests should be safe and not alter the state of the application.
You should instead, create the profile during user creation. Since there is just one field to be posted in the profile, it may be simpler and more effective to use a flat representation for the post data.
This is what you should do.
serializers/user.py
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer(read_only=True)
image = serializers.ImageField(write_only=True)
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
username = serializers.CharField(
max_length=32,
validators=[UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(min_length=6, write_only=True)
def create(self, validated_data):
user = User(email=validated_data['email'], username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
Profile.objects.create(user=user, image=validated_data['image')
return user
Now you can post your image field with the rest of the user fields