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}}
Related
In the following code I created a Login API but when I hit the request in Postman it's always give me error response. How to rectify the problem?
This is my views.py file
from django.shortcuts import render
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import UserSerializer, RegisterSerializer, UserLoginSerializer
from django.contrib.auth.models import User
from rest_framework import generics, permissions
from rest_framework import status
from django.contrib.auth import authenticate,login
from django.contrib import auth
# from knox.models import AuthToken
# Create your views here.
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view
#Login Credentials
"//Create a Login API in Django?"
class UserLoginView(APIView):
permission_classes = (AllowAny,)
serializer_class = UserLoginSerializer
def get(self,request,format=None):
serializer=UserLoginSerializer(data=request.data)
# print(serializer.is_valid(raise_exception=True));
if serializer.is_valid(raise_exception=True):
email = serializer.data.get('email')
password = serializer.data.get('password')
print(email)
print(password)
user=authenticate(email=email,password=password)
print(user)
if user is not None:
login(request, user)
return Response({'msg':'Login Success'},status=status.HTTP_200_OK)
else:
return Response({'msg':{'Email or Password Does not match'}},status=status.HTTP_404_NOT_FOUND)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#This is my serializer.py file.
class UserLoginSerializer(serializers.ModelSerializer):
email=serializers.EmailField(max_length=200)
class Meta:
model = User
fields= ['email','password']
When I hit the request then its always give me error
Django default User model takes the (username & password) field for Authentication.
in your code, you tried to authenticate with (email & password) so it gives None on every login request.
try for Authentication (username & password) instead of (email & password) I mean change like this...
user=authenticate(username=username,password=password)
Instead of...
user=authenticate(email=email,password=password)
If you still want to Authenticate with email then you need to Customize the Default User model
for Customizing Django User Model refer to the link here
User Model Cutomizaion code is here...
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
In the above code this line USERNAME_FIELD = 'email' give permission to authenticate with Email field Insted of Username field
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.
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
I am relatively new to Django, and web development in general. Basically, I'm trying to build a website with two types of users: customers, and suppliers. Both the customers and suppliers have user accounts (containing a username, email, and password) created using Django's built-in 'from django.contrib.auth import login -forms.Form' and stored in the table 'auth_user' in my mySQL database.
But, the suppliers can also create a more detailed profile (name, bio, images etc) which is created using a 'forms.ModelForm' called 'SupplierSignupForm' and stored in a separate table to 'auth_user', called 'home_suppliersignup'.
What I want to happen is, when a supplier is logged in, they can click on their 'my profile' link in the header and be taken to their profile located in the 'home_suppliersignup' table in my database. Currently, I know how to call a logged in users ID from the table 'auth_user' using their session info, and then use that to link to their respective 'auth_user' profile:
views.py
user = request.user
template
My profile
urls.py
url(r'^supplier-profile/(?P<id>[0-9]+)', supplier_profile, name="supplier_profile")
But I don't know how to use this to pull up their information from another database table (i.e. home_suppliersignup).
Any help would be much appreciated!
My current code:
models.py
from __future__ import unicode_literals
from django.db import models
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
class SupplierSignUp(models.Model):
LOCATION_CHOICES=(
("Central London", "Central London"),
("North London", "North London"),
("East London", "East London"),
("South London", "South London"),
("West London", "West London"),
)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=50)
bio = models.CharField(max_length=50)
area = models.CharField(max_length=50, choices=LOCATION_CHOICES, null=True)
booking_link = models.CharField(max_length=100, null=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
def __str__(self):
return self.email
def get_absolute_url(self):
return reverse("supplier_profile", kwargs={"id": self.id})
forms.py
from django import forms
from .models import SupplierSignUp
from django.contrib.auth import (
authenticate,
get_user_model,
login,
logout,
)
from django.contrib.auth.models import User, Group
User = get_user_model()
class SupplierSignUpForm(forms.ModelForm):
class Meta:
model = SupplierSignUp
fields = ['name', 'bio', 'area', 'booking_link']
class UserLoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
def clean(self, *args, **kwargs):
username = self.cleaned_data.get("username")
password = self.cleaned_data.get("password")
if username and password:
user = authenticate(username=username, password=password)
if not user:
raise forms.ValidationError("This user does not exist")
if not user.check_password(password):
raise forms.ValidationError("Incorrect password")
if not user.is_active:
raise forms.ValidationError("This user is no longer active")
return super(UserLoginForm, self).clean(*args, **kwargs)
class UserRegisterForm(forms.ModelForm):
username = forms.CharField()
email = forms.EmailField(label="Email Address")
email2 = forms.EmailField(label="Confirm Email", widget=forms.TextInput(attrs={'autocomplete':'false'}))
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = [
'username',
'email',
'email2',
'password',
]
def clean_email2(self):
email = self.cleaned_data.get('email')
email2 = self.cleaned_data.get('email2')
if email != email2:
raise forms.ValidationError("Emails must match")
email_qs = User.objects.filter(email=email)
if email_qs.exists():
raise forms.ValidationError("This email has already been registered")
return email
Assuming that you are not using a class based generic views, try this:
user=request.user
SupplierSignUp.objects.get(user=user)
You are taking the logged in user from the request, do there is no need to specify the user id in your urls.py / template (unless you want users to be able to see each others profile).
I am trying to develop a website,where in the signup flow after entering the user credentials and successfully validating Google reCaptcha, the user has to be directed to a page, displaying a list of security questions,the user has to answer one of the question to successfully signup on the website.
My forms.py file is here.
import re
from django import forms
from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm
from .models import CustomUser
from django.conf import settings
from django.utils.translation import ugettext as _
import urllib
import urllib.request as urllib2
import json
class SignUpForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and
password.
"""
password1= forms.CharField(label='Password',widget=forms.PasswordInput)
password2=forms.CharField(label='Confirm Password', widget=forms.PasswordInput)
class Meta:
#model to be used
model = CustomUser
#fields that have to be populated
fields=['username']
def __init__(self,*args,**kwargs):
self.request=kwargs.pop('requestObject',None)
super(SignUpForm,self).__init__(*args,**kwargs)
self.fields['username'].required=True
self.fields['password1'].required=True
self.fields['password2'].required=True
def clean(self):
super(SignUpForm,self).clean()
'''
Test the Google Recaptcha
'''
#url at which request will be sent
url='https://www.google.com/recaptcha/api/siteverify'
#dictionary of values to be sent for validation
values={
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': self.request.POST.get(u'g-recaptcha-response', None),
'remoteip': self.request.META.get("REMOTE_ADDR", None),
}
#making request
data=urlllib.parse.urlencode(values)
binary_data=data.encode('utf-8')
req= urllib.request.Request(url, binary_data)
response= urllib.request.urlopen(req)
result = json.loads(response.read().decode('utf-8'))
# result["success"] will be True on a success
if not result["success"]:
raise forms.ValidationError("Only humans are allowed to submit this form.")
return self.cleaned_data
def clean_password2(self):
#checking whether the passwords match or not
password1=self.cleaned_data.get('password1');
password2=self.cleaned_data.get('password2');
if password1 and password2 and password1!=password2:
raise forms.ValidationError("Passwords don't match!");
return password2;
def save(self, commit=True):
#overriding the default save method for ModelForm
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
The models.py file is here
from django.db import models
from django.conf import settings
from django.contrib.auth.models import AbstractUser
# Create your models here.
#Choices of questions
SECRET_QUESTION_CHOICES = (
('WCPM', "In which city did your parents meet?"),
('WCN', "What was your childhood nickname?"),
('WPN', "What is your pet's name?"),
)
#Secret Questions
class SecretQuestion(models.Model):
id=models.AutoField(primary_key=True)
secret_question = models.CharField(max_length = 100, choices = SECRET_QUESTION_CHOICES, default = SECRET_QUESTION_CHOICES[0],null=False, blank=False)
class Meta:
db_table='Security Questions'
class CustomUser(AbstractUser):
profile_pic = models.ImageField(upload_to = 'profile_pics/', blank = True, null = True)
following = models.ManyToManyField('self', symmetrical = False, related_name = 'followers', blank = True)
ques_followed = models.ManyToManyField('question.Question', related_name = 'questions_followed', blank = True)
topic_followed = models.ManyToManyField('topic.Topic', related_name = 'topic_followed', blank = True)
security_questions = models.ManyToManyField(SecretQuestion, related_name = 'secret_question_user', through = "SecretQuestionAnswer")
class Meta:
db_table = 'User'
def __str__(self):
return self.username
class SecretQuestionAnswer(models.Model):
user = models.ForeignKey(CustomUser)
secret_question = models.ForeignKey(SecretQuestion)
answer = models.CharField(max_length = 100,blank=False, null=False)
class Meta:
db_table='SecretQuestionAnswer'
Now, after successfully submitting the SignUpForm, the user should only be registered(means saved on the database) if he answers one of the questions given above successfully.
How should work on this problem? Please help.
I have been banging my head on this problem for the past two days.
Can somebody help.
Normaly you need to have a boolean field which will indicate if the user was finished will all steps. By default this field will be False, and when ready set it to True. You can allow login only when field is set to True.
Another approach is using a wizard http://django-formtools.readthedocs.org/en/latest/. With this approach you need to protect user's password somehow because it's not a good idea to stay in the session.