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
Related
I want to add password field and confirm_password field to my UserSerializer. I wrote a function called create to create a hashes password for my password field but before it can create the hashes password I want it to make sure that confirm_password and password are matched. The code works fine if I remove the confirm_password field. What is the problem?
[ Updated ]
My serializers.py
# serializer define the API representation
class UserSerializer(serializers.HyperlinkedModelSerializer):
# password field
password = serializers.CharField(
write_only = True,
required = True,
help_text = 'Enter password',
style = {'input_type': 'password'}
)
# confirm password field
confirm_password = serializers.CharField(
write_only = True,
required = True,
help_text = 'Enter confirm password',
style = {'input_type': 'password'}
)
class Meta:
model = User
fields = [
'url', 'first_name', 'last_name', 'email',
'password', 'confirm_password', 'is_staff'
]
def create(self, validated_data):
if validated_data.get('password') != validated_data.get('confirm_password'):
raise serializers.ValidationError("Those password don't match")
elif validated_data.get('password') == validated_data.get('confirm_password'):
validated_data['password'] = make_password(
validated_data.get('password')
)
return super(UserSerializer, self).create(validated_data)
error I got
TypeError: User() got an unexpected keyword argument 'confirm_password'
[20/Aug/2020 16:15:44] "POST /users/ HTTP/1.1" 500 168152
error I got in browser
TypeError at /users/
Got a `TypeError` when calling `User.objects.create()`. This may be because you have a writable field on the serializer class that is not a valid argument to `User.objects.create()`. You may need to make the field read-only, or override the UserSerializer.create() method to handle this correctly.
I can edit the question if you need more detail. Ty!
You are trying to save field confirm_password into your User model. I believe that this field is used only to confirm password, but User model doesn't have this field really.
Try to pop this field from validated_data before saving:
def create(self, validated_data):
if validated_data.get('password') != validated_data.get('confirm_password'):
raise serializers.ValidationError("Those password don't match")
elif validated_data.get('password') == validated_data.get('confirm_password'):
validated_data['password'] = make_password(
validated_data.get('password')
)
validated_data.pop('confirm_password') # add this
return super(UserSerializer, self).create(validated_data)
P.S. Validation is usually done in validate() method, not in create().
from_the_docs.
Try using password1 and password2 instead of password and confirm_passowrd as password field names, respectively.
You are performing validation in create method. This is not right approach. This should be done in is_valid or validate method of serializer. Your create method should look like this
def create(self, validated_data):
# confirm_password should not be sent to create as it is not part of User model
validated_data.pop('confirm_password', None)
return super(UserSerializer, self).create(validated_data)
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
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
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
I'm having trouble overriding clean method of a built-in Django form (django.contrib.auth.SetPasswordForm). This form has two fields: new_password1 and new_password2.
so in my views.py I call the customized form (MySetPasswordForm):
def reset_confirm(request, uidb64=None, token=None):
return password_reset_confirm_delegate(request,
template_name='app/reset_confirm.html',
set_password_form = MySetPasswordForm, uidb64=uidb64,
token=token, post_reset_redirect=reverse('main_page'))
In my forms.py: I want to define my own clean method to show my customized error messages. here's how I wrote MySetPasswordForm:
from django.contrib.auth.forms import SetPasswordForm
class MySetPasswordForm(SetPasswordForm):
error_messages = { 'password_mismatch': _("Missmatch!"), }
def clean(self):
password1 = self.cleaned_data.get('new_password1', '')
password2 = self.cleaned_data.get('new_password2', '')
print password1 #prints user's entered value
print password2 #prints nothing!!
print self.data['new_password2'] #prints user's entered value
if password1 == '':
self._errors["new_password1"] = ErrorList([u"enter pass1!"])
if password2 == '':
self._errors["new_password2"] = ErrorList([u"enter pass2"])
elif password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return self.cleaned_data
The problem is that when the user enter the repeat password wrong, instead of getting "Missmatch" error, it gives "enter pass2"! Also print password2 doesn't print user's entered value for password2.
What am I doing wrong in this code?! and what is the best way to do customized error messages?
p.s. using the original SetPasswordForm in the view works fine.
The SetPasswordForm checks that new_password1 and new_password2 match in the clean_new_password2 method.
When the passwords do not match, new_password2 is not included in self.cleaned_data, so you can't access it in the clean method.
If you want to override the error message for mismatched passwords, then setting it in the error_messages dict is the correct approach. I would then remove the clean method from your form.
If you need a different required error message for each field, you could set it in the __init__ method.
class MySetPasswordForm(SetPasswordForm):
error_messages = {
'password_mismatch': _("Missmatch!"),
'required': _("Please enter a password"), # If you do not require the fieldname in the error message
}
def __init__(self, *args, **kwargs):
super(MySetPasswordForm, self).__init__(*args, **kwargs)
self.fields['new_password1'].error_messages['required'] = _("enter pass1!")
When you call the clean method of form super method def clean_new_password2(self) all ready is called, so self.cleaned_data['new_password2'] is empty You need override the clean_new_password2 in your form, look for source auth forms
class MySetPasswordForm(SetPasswordForm):
def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2')
if password1 and password2:
if password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2