When creating a form using Flask-WTF and implementing a method, say, to check whether a username is available, I'm curious to how/why such methods work.
What I mean is, not once do you reference any of methods defined in the form class throughout the rest of your code; they seem to work on their own without any interference - can somebody explains how and why it works this way?
For example:
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
email = StringField('Email', validators=[DataRequired(), Email()])
...
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user is not None:
raise ValidationError('Please use a different username.')
The above code seems to run validate_username on submission of the form without myself having to call it.
This is by convention - when your form is being validated, it will check whether you have defined any custom inline validators in the form of validate_your_field and run these.
You can find more details in the Custom Validators section of documentation.
Related
I started experimenting with django few afternoons ago and I have an app with a custom form in which I authenticate user the following way:
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
... work with username and password ...
However, I also read on some webpage (witch I no longer have in my search history :/) that you should not access cleaned_data like that and that it is better to hide using of cleaned_data to a custom method. And do something like this:
if form.is_valid():
form.process()
I don't think that it should matter much. Nonetheless, I'm curious which way is more preferable and django-like.
Thanks!
I normally implement a save method because ModelForm has a built in save method. But the pythonic way would be to use the best method name that you fell that describe the action and implement in the form.
In my Contact model I use an EmailField to assure I'm saving a valid email address.
In my form however, I only want the user to be able to change the part before the #, the domain part is not user defined. (e.g. every email needs to end with #gmail.com)
I have looked at a lot of questions about modifying Django form field values before and after validation and display phase, but a lot of the answers are kind of 'hacky' or seem to much of a workaround for my case.
Is there some support of kind of a display filter, which manages a fixed prefix/suffix of a Django form field, without having to edit POST values or needing to suppress certain validations?
Use the clean method in your form to validate the email. It'll be something like this
def clean(self):
"""Check if valid email for institution."""
super().clean()
cleaned_data = self.cleaned_data
if not '#gmail.com' in cleaned_data['email']
msg = forms.ValidationError("Your email must be #gmail")
self.add_error('email', msg)
I'm facing a validation problem.
I need to use form validation and model validation together, but django (1.10) doesn't seem to like this.
Here is a short version of my setup:
class MyModel(models.Model):
fk = models.ForeignKey('ap.Model')
foo = models.CharField(max_length=12)
def clean(self):
if self.fk.som_field != self.foo:
raise ValidationError("This did not validate")
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('fk',)
def view(request):
instance = MyModel(foo='bar')
form = MyModelForm(data=request.POST, instance=instance)
if form.is_valid():
# process
# redirect
# display template
So I have some model field validation in the model itself.
I need this here because it is re-used in other non-form related parts of my application.
And I have some user input validation in the form.
In this case, checking that the provided fk is valid and exists.
But when the form is validated and the user provided 'fk' is not valid, the form is rejected.
But the form also calls MyModel.full_clean add more model validation.
The problem is that calling MyModel.clean() without having any data in the field fk will raise a RelatedObjectDoesNotExist exception.
How can I do this the proper way ?
I feel that MyModel.full_clean() should not be called by the form until the form itself is valid to ensure that the model is validated with at least correct field types in it.
I could embed my MyModel.clean operations in a try/except that would catch a RealtedObjectDoesNotExist, but it has a bad code smell for me.
The fact that MyModel.fk exists should be ensured by the first form layer validation.
As you have found, the model form performs the instance validation (by calling self.instance.full_clean()`) whether or not the form is valid.
You could prevent this behaviour (I might try to override the _post_clean, but I think it's simpler to take account of the behaviour in the clean method.
Rather than catching the exception, it would be simpler to check that self.fk_id is not None before accessing self.fk.
class MyModel(models.Model):
fk = models.ForeignKey('ap.Model')
foo = models.CharField(max_length=12)
def clean(self):
if self.fk_id is not None and self.fk.som_field != self.foo:
raise ValidationError("This did not validate")
I wish to have the user agree to a TOS and to also support unique emails. django-registration has two different subclassed registration forms that does this: RegistrationFormTermsOfService and RegistrationFormUniqueEmail.
Do I have to make my own sublcass of RegistrationForm and then provide both those features? If so, how would this be accomplished? Would the registration form live inside my app's forms.py or somewhere else?
A quick look at the source for the two classes shows:
class RegistrationFormTermsOfService(RegistrationForm):
"""
Subclass of ``RegistrationForm`` which adds a required checkbox
for agreeing to a site's Terms of Service.
"""
tos = forms.BooleanField(widget=forms.CheckboxInput,
label=_(u'I have read and agree to the Terms of Service'),
error_messages={'required': _("You must agree to the terms to register")})
class RegistrationFormUniqueEmail(RegistrationForm):
"""
Subclass of ``RegistrationForm`` which enforces uniqueness of
email addresses.
"""
def clean_email(self):
"""
Validate that the supplied email address is unique for the
site.
"""
if User.objects.filter(email__iexact=self.cleaned_data['email']):
raise forms.ValidationError(_("This email address is already in use. Please supply a different email address."))
return self.cleaned_data['email']
As you can see these two classes don't overwrite methods defined by the other so you should be able to just define your own class as being:
from registration.forms import RegistrationFormUniqueEmail, RegistrationFormTermsOfService
class RegistrationFormTOSAndEmail(RegistrationFormUniqueEmail, RegistrationFormTermsOfService):
pass
And it should function, however I have not tested this. As to where to place this class; forms.py is a good location.
Update:
A little reading at https://django-registration.readthedocs.org/en/latest/views.html which tells us that we can pass the view some parameters via the url definition; for instance a form class.
Simply use a URL like:
url(r'^register/$',
RegistrationView.as_view(form_class=RegistrationFormTOSAndEmail),
name='registration_register')
I have a basic registration form that includes a BooleanField for people to accept Terms and Privacy Policy. What I want to do is change the language of the ValidationError that is raised if users do not check it.
class RegisterForm(forms.Form):
username = forms.CharField(label="Username")
email = forms.EmailField(label="Email")
location = forms.CharField(label="Location",required=False)
headline = forms.CharField(label="Headline",required=False)
password = forms.CharField(widget=forms.PasswordInput,label="Password")
confirm_password = forms.CharField(widget=forms.PasswordInput,label="Confirm Password")
terms = TermsField(label=mark_safe("I have read and understand the <a href='/terms'>Terms of Service</a> and <a href='/privacy'>Privacy Policy</a>."),required=True)
TermsField is subclassed from BooleanField:
class TermsField(forms.BooleanField):
"Check that user agreed, return custom message."
def validate(self,value):
if not value:
raise forms.ValidationError('You must agree to the Terms of Service and Privacy Policy to use this site.')
else:
super(TermsField, self).validate(value)
It validates correctly in that if a user does not check them TermsField the form does not validate, but it returns the generic "This field is required" error. This seems like a pretty simple task, and I'm sure I'm doing something basic wrong. Any ideas?
It's because Django sees the field is required and no value was provided, so it doesn't even bother calling your validate method (which comes after the built-in validation).
The way to accomplish what you're trying to accomplish is:
class RegisterForm(forms.Form):
# ...other fields
terms = forms.BooleanField(
required=True,
label=mark_safe('I have read and understand the <a href=\'/terms\'>Terms of Service</a> and <a href=\'/privacy\'>Privacy Policy</a>.')
error_messages={'required': 'You must agree to the Terms of Service and Privacy Policy to use Prospr.me.'}
)
That will override the default "required" message defined in Field.default_error_messages.