Validate Sign Up User data in serializers - Django Rest Framework - python

I have an issue with Validate Sign Up User data in serializers - Django Rest Framework. Hope your guys help me!
My request:
I want to create sign up form with user enter Duplicate email, it'll raise serializer object which duplicate.
My serializers:
class UserDuplicateSerializer(ModelSerializer):
class Meta:
model = User
fields = [
'username',
'full_name',
'first_name',
'last_name',
]
class UserSignUpSerializer(ModelSerializer):
username = CharField(required=True, allow_blank=False)
class Meta:
model = User
fields = [
'username',
'email',
'password'
]
extra_kwargs = {"password": {"write_only": True}}
# Validate duplicate username
def validate_username(self, value):
data = self.get_initial()
username = data.get("username")
username_qs = User.objects.filter(username=username)
if username_qs.exists():
duplicate_obj = User.objects.get(username=username)
serializer = UserDuplicateSerializer(duplicate_obj)
raise ValidationError(format(serializer.data))
else:
pass
return value
def create(self, validated_data):
username = validated_data['username']
...
user_obj.save()
return validated_data
It renders:
{'username': u'duongnuhabang', 'first_name': u'D\u01b0\u01a1ng N\u1eef', 'profile_pic_url': OrderedDict([('id', 71), ('image', '/media/users/2/13_71.jpg'), ('config_translateY', 0)]), 'last_name': u'H\u1ea1 B\u0103ng', 'full_name': u'D\u01b0\u01a1ng N\u1eef H\u1ea1 B\u0103ng'}
But I want to JSON Type:
{'username': 'duongnuhabang', 'first_name': 'Feed Git', ...}

raise ValidationError("This username has been registered!" +str( serializer.data))
use this in your validation error

Related

Pass 2 values to validator

In Django I try in forms create validator with compare password and confirm_password and I don't want do it in clean method. I want my do cystom validator and put him to widget confirm_password field.
I don't know ho pass two values password and confirm_password to my Validator.
def validate_test():
cleaned_data = super(UserSignUpForm, self).clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
print(f'password:{password}\nconfirm_password: {confirm_password}')
if password != confirm_password:
raise ValidationError(
_('%(password)s and %(confirm_password)s does not match - test'),
params={'password': password, 'confirm_password': confirm_password},
)
class UserSignUpForm(forms.ModelForm):
password = forms.CharField(
label="Password",
validators=[MinLengthValidator(8, message="Zbyt krótkie hasło, min. 8 znaków")],
widget=forms.PasswordInput(attrs={'style':'max-width: 20em; margin:auto', 'autocomplete':'off'}))
confirm_password = forms.CharField(
label="Confirm password",
validators=[MinLengthValidator(8, message="Zbyt krótkie hasło, min. 8 znaków"), validate_test()],
widget=forms.PasswordInput(attrs={'style':'max-width: 20em; margin:auto', 'autocomplete':'off'}))
class Meta:
model = User
fields = ("username", 'first_name', 'last_name', "password")
help_texts = {"username": None}
widgets = {
'username': forms.TextInput(attrs={'style':'max-width: 20em; margin:auto'}),
'first_name': forms.TextInput(attrs={'style':'max-width: 20em; margin:auto'}),
'last_name': forms.TextInput(attrs={'style':'max-width: 20em; margin:auto'}),
}
No I have different messages in web site
You can add clean()(Django Docs) method to your form:
class UserSignUpForm(forms.ModelForm):
...
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
print(f'password:{password}\nconfirm_password: {confirm_password}')
if password != confirm_password:
msg = _('%(password)s and %(confirm_password)s does not match - test') % {
'password': password, 'confirm_password': confirm_password
})
# raise ValidationError(msg)
# or use add_error()
self.add_error('password', msg)
self.add_error('confirm_password', msg)
It is also suggested by Django:
We are performing validation on more than one field at a time, so the
form’s clean() method is a good spot to do this. Notice that we are
talking about the clean() method on the form here, whereas earlier we
were writing a clean() method on a field. It’s important to keep the
field and form difference clear when working out where to validate
things. Fields are single data points, forms are a collection of
fields.
See also Cleaning a specific field attribute

Direct assignment to the forward side of a many-to-many set is prohibited. Use associated_class.set() instead

I'm getting an error where I tried to patch up but I don't understand where should I change the code.
This is my serializer.py file:
class TeacherSignupSerializer(serializers.ModelSerializer):
user = UserCreateSerializer()
# teacher = SchoolSerializer(read_only=True)
# teachers = ClassSerializer(read_only=True)
# teach = SubjectSerializer(read_only=True)
class Meta:
model = Teacher
fields = (
'name',
'email',
'mobile_number',
'city',
'school_name',
'subject',
'user',
'associated_class',
)
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create_user(**user_data)
teacher = Teacher.objects.create(**validated_data)
teacher.set_user(user)
return teacher
And this is my views.py:
class TeacherSigupAPIView(generics.GenericAPIView):
serializer_class = TeacherSignupSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
teacher = serializer.save()
if teacher:
return Response({
"status": True,
"teacher": TeacherSerializer(teacher, context=self.get_serializer_context()).data,
})
return Response({
"status": False,
"message": "Teacher is already exists"
}, status=status.HTTP_400_BAD_REQUEST)
The error I got was (place_error_here) but when I changed my code, that also gave the same issue.

user.save() not working properly when used in overriding serializer class's save() method

what i wanted was to send a post request with body containing email , username , password and password 2.
the serializer class has a save method which has been overriden so that it checks password1 is equal to password2 or not . if equal i wanted a user object to be created with email username and a password . and wanted it to be saved . im using User model of django.
error:
TypeError: Got a TypeError when calling User.objects.create(). This may be because you have a writable field on the serializer class tha
t is not a valid argument to User.objects.create(). You may need to make the field read-only, or override the RegistrationSerializer.create() method to handle this correctly.
Serializer Class:
class RegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = User
fields = ['email', 'username', 'password','password2']
extra_kwargs = {'password': {'write_only': True}}
# override one of its method to check if passwords match
def save(self):
user = User()
#cant create user py passing into constructor
user.email=self.validated_data['email']
user.username=self.validated_data['username']
password=self.validated_data['password']
password2=self.validated_data['password2']
if password!=password2:
raise serializers.ValidationError({'password':'password must match'})
user.set_password(password)
user.save()
return user
the view called:
#api_view(['POST', ])
def registration_view(request):
serializer=RegistrationSerializer(data=request.data)
data={}
if serializer.is_valid():
user=serializer.save()
data['response']="successfully created"
data['email']=user.email
data['username']=user.username
else:
data=serializer.errors
return Response(data)
As save method is not good place for validation, You should use validate function when you want control some fields are correct.
class RegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = User
fields = ['email', 'username', 'password','password2']
extra_kwargs = {'password': {'write_only': True}}
def validate(self, attrs):
if attrs.get('password') != attrs.get('password2'):
raise serializers.ValidationError({'password':'password must match'})
return attrs
def create(self, validated_data):
password2 = validated_data.pop('password2')
return super().create(validated_data)
If you want looks save function http://www.cdrf.co/3.9/rest_framework.serializers/ModelSerializer.html#save

Use email as authentication field and add email verification on django custom user model

I have this custom user model on my Django project. I want to make the email as authentication field instead of the username. Also, I want to perform an email verification.
models.py
class es_user(models.Model):
user = models.OneToOneField(User,related_name='es_user', on_delete=models.CASCADE),
is_activated = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('user_detail', kwargs={'id': self.pk })
view.py
def signup(request):
signup_form_instance = SignUpForm()
if request.method == "POST":
signup_form_instance = SignUpForm(request.POST)
if signup_form_instance.is_valid():
signup_form_instance2 = signup_form_instance.save(commit = False)
username = signup_form_instance2.username
password = signup_form_instance2.password
signup_form_instance2.password = make_password(signup_form_instance.cleaned_data['password'])
signup_form_instance2.save()
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request,user)
active_user = request.user
es_user_instance = es_user.objects.create(user= active_user)
# return index(request)
return redirect('index')
# return user_detail(request)#successful signup redirect or return
# return redirect('user_detail',id = [str(request.user.id) ])#kwargs={'id': request.user.id })
else:
print("SIGN UP FORM INVALID")
return render(request,'signup.html',{'signup_form':signup_form_instance})
forms.py
class SignUpForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email', 'password')
# for adding bootstrap classes & placeholders
widgets = {
'username': TextInput(attrs={
'class':'form-control',
'placeholder': 'Username *'}),
'email': EmailInput(attrs={
'class':'form-control',
'placeholder': 'Your Email *'}),
'password': PasswordInput(attrs={
'class':'form-control',
'placeholder': 'Your Password *'}),
}
help_texts = {
'username': None,
}
# to remove labels in form
labels = {
'username': (''),
'email':(''),
'password':(''),
}
My project is near completion so I cannot change my user model anymore or even change its name. So is there a way I can add email verification and using email instead of username for authentication without changing my user model.
I've seen a solution for a similar problem in this post . But I cannot use it since I use my custom user model es_user. is there a way in which I can edit it for my problem
To use Email as authentication, you have to use make new python file Backend.py and inside it write
class AuthenticationBackend(backends.ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
usermodel = get_user_model()
print(usermodel)
try:
user = usermodel.objects.get(Q(username__iexact=username) | Q(
email__iexact=username))
if user.check_password(password):
return user
except user.DoesNotExist:
pass
and add this AuthenticationBackend in settings.py as
AUTHENTICATION_BACKENDS = (
'users.backend.AuthenticationBackend',
'django.contrib.auth.backends.ModelBackend',
)
This will let you authenticate user with both username and password.
For second part of your question,Please follow:
Django Custom User Email Account Verification

django form validation -validation error custom message not work

This is a Django rest project
I'm creating a user registration form .. I wrote a validation for username like below but my custom validation error is not showing and it shows a default message like below... how can I fix this?
My other question is: Whats the difference between def validate_username and def clean_username
Thank You
This is the default Django message:
"{
"username": [
"A user with that username already exists."
]
}
"
This is my view:
class UserRegisterApiView(CreateAPIView):
permission_classes = [AllowAny]
serializer_class = UserRegisterSerializer
queryset = User.objects.all()
This is my serilizer:
class UserRegisterSerializer(ModelSerializer):
#email2 = EmailField(label='confirm email')
email = EmailField(label='email', )
class Meta:
model = User
fields = [
'username',
'first_name',
'email',
'password',
]
extra_kwargs = {
"password": {
"write_only": True
}
}
# check if the user name is taken
def validate_username(self, value):
username = value
qs = User.objects.filter(username=username)
if qs.exists():
raise ValidationError("این نام کاربری آزاد نمیباشد")
return value
The uniquire message is come from models as:
username = models.CharField(
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.'),
validators=[username_validator],
error_messages={
'unique': _("A user with that username already exists."),
},
)
In your case,you need to rewrite your username's UniqueValidator in serializer.
class UserRegisterSerializer(ModelSerializer):
def __init__(self, *args, **kwargs):
super(UserRegisterSerializer, self).__init__(*args, **kwargs)
for validator in self.fields['username'].validators:
from rest_framework.validators import UniqueValidator
if type(validator) == UniqueValidator:
validator.message = 'این نام کاربری آزاد نمیباشد'

Categories