wtform EqualTo does't validate - flask

I'm trying to validate a form using EqualTo validator but even if I test with different inputs the form does't throw any error.
this is the code of route:
#app.route("/register", methods=["GET", "POST"])
def register():
form = Form()
if form.validate_on_submit():
pass
return render_template("register.html", form=form)
this is the code of form class:
class Form(FlaskForm):
user_name = StringField(label="Username", validators=[
DataRequired(), Length(min=4, max=25)])
email = StringField(label="Email id", validators=[DataRequired()])
password = PasswordField(label="Password", validators=[
DataRequired()])
retype_password = PasswordField(
label="Retype Password", validators=[EqualTo('password', "not equal"), DataRequired()])
submit = SubmitField(label="Creat account", validators=[DataRequired()])

Try the following :
retype_password = PasswordField('Retype Password', validators=[DataRequired(), EqualTo('password')])
Retype Password is the label name for your input field.
Please make sure that you are writing proper error messages in you register.html page.
Check this out!

Related

Django ValueError The given username must be set from form self on form overridden save method

I´m in a django simple 3.2.9 project. When i try to override my form save method, y get next error:
ValueError The given username must be set from form self
It´s quite annoying, cause if I don´t override and call the save functionality from the view, it works just fine, but if I override method, it seems it can´t get self attributes from form, though it can do that on other validation methods within the class.
This, my RegisterForm class.
class RegisterForm(forms.Form):
username=forms.CharField(
required=True,
min_length=4,max_length=50,
widget=TextInput(attrs={
'class':'form-control'
})
)
email=forms.EmailField(
required=True,
min_length=4,max_length=50,
widget=TextInput(attrs={
'class':'form-control'
})
)
pwd=forms.CharField(
required=True,
min_length=4,max_length=50,
widget=PasswordInput(attrs={
'class':'form-control'
})
)
pwdr=forms.CharField(
required=True,
min_length=4,max_length=50,
widget=PasswordInput(attrs={
'class':'form-control'
})
)
def clean_username(self):
username=self.cleaned_data.get('username')
if User.objects.filter(username=username).exists():
raise forms.ValidationError('Ese nombre de usuario ya se encuentra en uso')
return username
def clean_email(self):
email=self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise forms.ValidationError('Ese email ya se encuentra en uso')
return email
def clean(self):
cleaned_data=super().clean()
if cleaned_data.get('pwd')!= cleaned_data.get('pwdr'):
self.add_error('pwdr','Las contraseñas no coinciden')
return redirect('register')
return redirect('home')
def save(self):
username=self.cleaned_data.get('username')
email=self.cleaned_data.get('email')
pwd=self.cleaned_data.get('pwd')
return User.objects.create_user(
username,
email,
pwd
)
This self contents from print(self) inside save method
label for="id_username">Username:<input
type="text" name="username" value="vidalon" class="form-control"
maxlength="50" minlength="4" required id="id_username">
Email: Pwd: Pwdr:
---UPDATED---
I got this from django docs
where we can read the following:
Note that Form.add_error() automatically removes the relevant field
from cleaned_data.
So basically i had a validation on two password fields and a form class with a clean method to validate these two related fields like this:
def clean(self):
cleaned_data=super().clean()
if cleaned_data.get('pwd')!= cleaned_data.get('pwdr'):
self.add_error('pwdr','Passwords doesn´t match')
return redirect('register')
return redirect('home')
So i guess question is solved, now it makes sense keep coding!
Thanks for the interest
I got this from django docs
where we can read the following:
Note that Form.add_error() automatically removes the relevant field from cleaned_data.

How would I make a Django form with confirming password backwards?

I’m using Django-registration’s one-step workflow. I’m adding a third form field, pw3, so that the user must confirm his/her password by typing it backwards. How would I go about that?
I'm not exactly in django-forms and I don't see any your code example, despite this I hope it's gonna help you.
class RegistrationForm(forms.Form):
... # your rest of fields
password = forms.CharField(label='Enter password', widget=forms.PasswordInput)
password_confirm = forms.CharField(label='Confirm your password from the back', widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
password_confirm = cleaned_data.get('password_confirm')
if password and password_confirm and password_confirm[::-1] != password:
self.add_error('password_confirm', "Password confirm doesn't match")
Also I recommend you to familiarize with documentation.
https://docs.djangoproject.com/en/2.2/ref/forms/validation/#raising-validation-error

AttributeError: 'AcceptInvite' object has no attribute 'email'

Im doing some unit testing in my Django project, and am getting error
"AttributeError: 'SignUp' object has no attribute 'email'"
when I run this test.
def test_signup(self):
response = self.c.post('/accounts/signup/', {'email': 'test#test.com', 'password': 'test123', 'password_conf': 'test123',
'org_name': 'test org', 'org_username': 'test org username', 'invite': '4013'})
code = response.status_code
self.assertTrue(code == 200)
The view this is testing simply takes a signup form, and creates a new account with it.
def signup(request):
# """Register a new account with a new org."""
if request.method == "POST":
form = SignUp(request.POST)
if not form.email or not form.password:
raise Exception("Email and Password are required")
if form.password != form.password_conf:
raise Exception("Password does not match confirmation")
if not form.org_name or not form.org_username:
raise Exception('Organization name and username are required')
if not form.invite:
raise Exception('Invitation code is required')
if form.is_valid():
cleaned_data = form.cleaned_data
email = cleaned_data['email']
password = cleaned_data['password']
org_name = cleaned_data['org_name']
org_username = cleaned_data['org_username']
invite_token = cleaned_data['invite']
invitation = OrgInvite.objects.get(token=invite_token)
if invitation.used:
raise Exception("invitation code is invalid")
account = Account(email=email, password=password)
account.save()
org = Org(org_name=org_name, org_username=org_username)
org.save()
invitation.used = False
invitation.save()
login(request)
# Send Email
md = mandrill.Mandrill(settings.MANDRILL_API_KEY)
t = invite_token.replace(' ', '+')
url = "https://www.humanlink.co/verify/{}".format(t)
message = {
'global_merge_vars': [
{'name': 'VERIFICATION_URL', 'content': url},
],
'to': [
{'email': account.email},
],
}
message['from_name'] = message.get('from_name', 'Humanlink')
message['from_email'] = message.get('from_email', 'support#humanlink.co')
try:
md.messages.send_template(
template_name='humanlink-welcome', message=message,
template_content=[], async=True)
except mandrill.Error as e:
logging.exception(e)
raise Exception('Unknown service exception')
The Signup form has an email field, and the data in request.POST should have the email I am sending it with my Client's post method being used in my unit test, so I am really not sure why it still wouldn't have an 'email' attribute.
Form:
class SignUp(forms.Form):
email = forms.EmailField()
password = forms.CharField()
password_conf = forms.CharField()
org_name = forms.CharField()
org_username = forms.CharField()
invite = forms.CharField()
You code suffers from multiple errors. To address your question, in your view method signup you were creating a form, but you shouldn't do form.email or form.password because that's not how django handles form data.
Other related issues, first, you need to call form.is_valid() before you could get any data from form object. Even so, you should use form.cleaned_data['email'] to access the form data.
Secondly. You shouldn't do empty check like that. If you put:
email = forms.EmailField(required=True)
django will automatically verify the emptiness for you already.
Thirdly, raising Exception in views.py method doesn't get your form to return the message to the template you want. If you have custom form validation, you should do it in form class's clean method.
Please check django doc about how to use form properly.

Django Custom User Email Account Verification

I am looking to add email account verification in Django. I have attempted using the django-registration app to do so, but it doesn't appear that it has been updated to be fully compatible with custom user models which causes too many problems. Is there another reliable and well-documented app out there which will allow me to send a verification email on user registration in django?
How I handle the email registration personally:
First of all, my Profile extending Django Users (models.py):
class Profile(models.Model):
user = models.OneToOneField(User, related_name='profile') #1 to 1 link with Django User
activation_key = models.CharField(max_length=40)
key_expires = models.DateTimeField()
In forms.py, the Registration class :
class RegistrationForm(forms.Form):
username = forms.CharField(label="",widget=forms.TextInput(attrs={'placeholder': 'Nom d\'utilisateur','class':'form-control input-perso'}),max_length=30,min_length=3,validators=[isValidUsername, validators.validate_slug])
email = forms.EmailField(label="",widget=forms.EmailInput(attrs={'placeholder': 'Email','class':'form-control input-perso'}),max_length=100,error_messages={'invalid': ("Email invalide.")},validators=[isValidEmail])
password1 = forms.CharField(label="",max_length=50,min_length=6,
widget=forms.PasswordInput(attrs={'placeholder': 'Mot de passe','class':'form-control input-perso'}))
password2 = forms.CharField(label="",max_length=50,min_length=6,
widget=forms.PasswordInput(attrs={'placeholder': 'Confirmer mot de passe','class':'form-control input-perso'}))
#recaptcha = ReCaptchaField()
#Override clean method to check password match
def clean(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password1 != password2:
self._errors['password2'] = ErrorList([u"Le mot de passe ne correspond pas."])
return self.cleaned_data
#Override of save method for saving both User and Profile objects
def save(self, datas):
u = User.objects.create_user(datas['username'],
datas['email'],
datas['password1'])
u.is_active = False
u.save()
profile=Profile()
profile.user=u
profile.activation_key=datas['activation_key']
profile.key_expires=datetime.datetime.strftime(datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
profile.save()
return u
#Sending activation email ------>>>!! Warning : Domain name is hardcoded below !!<<<------
#The email is written in a text file (it contains templatetags which are populated by the method below)
def sendEmail(self, datas):
link="http://yourdomain.com/activate/"+datas['activation_key']
c=Context({'activation_link':link,'username':datas['username']})
f = open(MEDIA_ROOT+datas['email_path'], 'r')
t = Template(f.read())
f.close()
message=t.render(c)
#print unicode(message).encode('utf8')
send_mail(datas['email_subject'], message, 'yourdomain <no-reply#yourdomain.com>', [datas['email']], fail_silently=False)
Now, in views.py, we need to handle all that, let's go :
The register view:
def register(request):
if request.user.is_authenticated():
return redirect(home)
registration_form = RegistrationForm()
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
datas={}
datas['username']=form.cleaned_data['username']
datas['email']=form.cleaned_data['email']
datas['password1']=form.cleaned_data['password1']
#We generate a random activation key
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
usernamesalt = datas['username']
if isinstance(usernamesalt, unicode):
usernamesalt = usernamesalt.encode('utf8')
datas['activation_key']= hashlib.sha1(salt+usernamesalt).hexdigest()
datas['email_path']="/ActivationEmail.txt"
datas['email_subject']="Activation de votre compte yourdomain"
form.sendEmail(datas)
form.save(datas) #Save the user and his profile
request.session['registered']=True #For display purposes
return redirect(home)
else:
registration_form = form #Display form with error messages (incorrect fields, etc)
return render(request, 'siteApp/register.html', locals())
The activation views :
#View called from activation email. Activate user if link didn't expire (48h default), or offer to
#send a second link if the first expired.
def activation(request, key):
activation_expired = False
already_active = False
profile = get_object_or_404(Profile, activation_key=key)
if profile.user.is_active == False:
if timezone.now() > profile.key_expires:
activation_expired = True #Display: offer the user to send a new activation link
id_user = profile.user.id
else: #Activation successful
profile.user.is_active = True
profile.user.save()
#If user is already active, simply display error message
else:
already_active = True #Display : error message
return render(request, 'siteApp/activation.html', locals())
def new_activation_link(request, user_id):
form = RegistrationForm()
datas={}
user = User.objects.get(id=user_id)
if user is not None and not user.is_active:
datas['username']=user.username
datas['email']=user.email
datas['email_path']="/ResendEmail.txt"
datas['email_subject']="Nouveau lien d'activation yourdomain"
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
usernamesalt = datas['username']
if isinstance(usernamesalt, unicode):
usernamesalt = usernamesalt.encode('utf8')
datas['activation_key']= hashlib.sha1(salt+usernamesalt).hexdigest()
profile = Profile.objects.get(user=user)
profile.activation_key = datas['activation_key']
profile.key_expires = datetime.datetime.strftime(datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
profile.save()
form.sendEmail(datas)
request.session['new_link']=True #Display: new link sent
return redirect(home)
Finally, in urls.py:
url(r'^register/$', 'register'),
url(r'^activate/(?P<key>.+)$', 'activation'),
url(r'^new-activation-link/(?P<user_id>\d+)/$', 'new_activation_link'),
With all that you should have something to start with, use the appropriate templatetags in the .txt emails and HTML and it should work.
NB: This code isn't perfect, there is duplication (for instance, the generation of the random key could be defined in a function), but it does the job. Also: the activation key is not generated using proper cryptographic functions. An alternative is to use a function like the following to generate the keys:
from django.utils.crypto import get_random_string
def generate_activation_key(username):
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!##$%^&*(-_=+)'
secret_key = get_random_string(20, chars)
return hashlib.sha256((secret_key + username).encode('utf-8')).hexdigest()
NB2: Django send_mail doesn't provide any tools to authenticate your emails. If you want to authenticate your emails (DKIM, SPF), I advise you to look into this: https://djangosnippets.org/snippets/1995/
NB3: There is a security issue with the view new_activation_link: it should check if the user requesting the re-send is the right one and also if he isn't already authenticated. I let you correct that.
You may also be interested in the simple but powerful django-verified-email-field.
Simply use VerifiedEmailField in Your forms:
from django import forms
from verified_email_field.forms import VerifiedEmailField
class RegistrationForm(forms.ModelForm):
email = VerifiedEmailField(label='email', required=True)
Or in Your models:
from django.db import models
from verified_email_field.models import VerifiedEmailField
class User(models.Model):
email = VerifiedEmailField('e-mail')
It renders two input fields: e-mail and verification code. The verification code is sent to the e-mail address using AJAX or during field's clean if there is no valid code for given e-mail, so it works even without javascript.

Django: errors not being shown when form is submitted

clean is not showing errors on my login form regardless of what my input is and regardless of what method of displaying errors I try in clean.
In my CustomUserCreationForm error displaying works perfectly. The only difference between the two is login extends forms.Form while Custom extends UserCreationForm
Also I'm using django-crispy-forms to render my forms
class LoginForm(forms.Form):
username = forms.CharField(label=('UserName'),
widget = forms.TextInput(attrs={'placeholder': _('Username')})
)
password = forms.CharField(label=('Password'),
widget=forms.PasswordInput(attrs={'placeholder' : _('Password') }),
)
def helper(self):
helper = FormHelper()
helper.form_id = "Login"
helper.form_method = "POST"
helper.layout = Layout(Div(
Field('username', css_class='input-box-rounded'),
Field('password', css_class='input-box-rounded'),
Submit('Login', 'Login', css_class='col-md-6 col-md-offset-3 rounded'),
css_class='col-md-4 col-md-offset-4 centered-div'))
return helper
def clean(self):
cleaned_data = super(LoginForm, self).clean()
if 'username' not in cleaned_data:
msg = _("Please enter a username")
self._errors['username'] = self.error_class([msg])
if 'password' not in cleaned_data:
msg = _("Please enter a password")
raise forms.ValidationError(msg)
u =authenticate(username = cleaned_data['username'], password = cleaned_data['password'])
if u == None:
msg = _("Username or Password is incorrect")
self.add_error('username', msg)
return cleaned_data
Can you post your view and template code? Without seeing either of those, I assume either your template needs to display the errors or your view is not handling the form, though I haven't used Django Crispy Forms.
{{ form.non_field_errors }}
{{ form.username.errors }}
FYI, the preferred way to handle error checking is to create a clean function for each field and have it raise a ValidationError when there is a problem. This will then be a field error (second line above).
def clean_password(self):
data = self.cleaned_data.get('password')
if not data:
raise ValidationError(_("Please enter a password"))
Also, since you're just checking that a field is there, so you could set required=True for each required field and skip the manual validation.
class LoginForm(forms.Form):
username = forms.CharField(label=('UserName'), required=True,
widget = forms.TextInput(attrs={'placeholder': _('Username')})
)
password = forms.CharField(label=('Password'), required=True,
widget=forms.PasswordInput(attrs={'placeholder' : _('Password') }),
)
See the documentation for more info: https://docs.djangoproject.com/en/dev/topics/forms/#customizing-the-form-template