Django Emailfield fixed suffix - django

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)

Related

How does Flask-WTF use methods defined in a form?

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.

django form - raising specific field validation error from clean()

I have a validation check on a form which depends on more than one field, but it would be good to have the validation error show the user specifically which fields are causing the issue, rather than just an error message at the top of the form. (the form has many fields so it would be clearer to show specifically where the error is).
As a work around I tried to create the same validation in each of the relevant fields clean_field() method so the user would see the error next to those fields. However I only seem to be able to access that particular field from self.cleaned_data and not any other?
Alternatively is it possible to raise a field error from the forms clean() method?
Attempt 1:
def clean_supply_months(self):
if not self.cleaned_data.get('same_address') and not self.cleaned_data.get('supply_months'):
raise forms.ValidationError('Please specify time at address if less than 3 years.')
def clean_supply_years(self):
if not self.cleaned_data.get('same_address') and not self.cleaned_data.get('supply_years'):
raise forms.ValidationError('Please specify time at address if less than 3 years.')
def clean_same_address(self):
.....
If you want to access cleaned data for more than one field, you should use the clean method instead of clean_<field> method. The add_error() method allows you to assign an error to a particular field.
For example, to add the Please specify time at address error message to the same_address field, you would do:
def clean(self):
cleaned_data = super(ContactForm, self).clean()
if not self.cleaned_data.get('same_address') and not self.cleaned_data.get('supply_months'):
self.add_error('same_address', "Please specify time at address if less than 3 years.")
return cleaned_data
See the docs on validating fields that rely on each other for more info.

Is there a better way to create this Django form?

This particular form has 2 boolean fields, if the first field is No/False, then the next two fields become required. This form does work, but it seems very ugly to me.
Without clean_approved, approved is not actually required, meaning the required=True argument is just documentation, not working code
The self._errors dict code MIGHT (not in this case) overwrite any other errors that were present in that field.
With all the other ways Django makes it easy to make beautiful code, I must be doing something wrong for this Boolean field (that should be required) and the optionally required fields based on the answer to the first field.
Please show me the right way to do this.
class JobApprovalForm(forms.Form):
approved = forms.NullBooleanField(required=True)
can_reapply = forms.NullBooleanField(required=False)
reject_reason = forms.CharField(required=False, widget=forms.Textarea())
def clean_approved(self):
""" Without this, approved isn't really required! """
if self.cleaned_data['approved'] == None:
raise forms.ValidationError(_("This field is required."))
return self.cleaned_data['approved']
def clean(self):
""" Make the other two fields required if approved == False """
if not self._errors and self.cleaned_data['approved'] == False:
required_msg = [_("If this job is not approved, this field is required")]
if self.cleaned_data['can_reapply'] == None:
self._errors['can_reapply'] = self.error_class(required_msg)
if not self.cleaned_data['reject_reason']:
self._errors['reject_reason'] = self.error_class(required_msg)
return self.cleaned_data
While mit might seem a bit excessive you could consider using an inline form.
The outer Form contains your "approved" setting.
the inline form handles your rejection objects.
This will allow you to separate two different concerns. First case: approval yes/no. Second case: rejection handling
Unfortunately I can't provide you with example code right now,
but please have a look at this SO Question to get an initial idea of this concept
Inline Form Validation in Django

does django models offer something similar to forms' clean_<fieldname>()?

I am trying to move all business-logic-related validations to models, instead of leaving them in forms. But here I have a tricky situation, for which I like to consult with the SO community.
In my SignupForm (a model form), I have the following field-specific validation to make sure the input email does not exist already.
def clean_email(self):
email = self.cleaned_data['email']
if ExtendedUser.objects.filter(email=email).exists():
raise ValidationError('This email address already exists.')
return email
If I were to move this validation to the models, according to the official doc, I would put it in clean() of the corresponding model, ExtendedUser. But the doc also mentions the following:
Any ValidationError exceptions raised by Model.clean() will be stored
in a special key error dictionary key, NON_FIELD_ERRORS, that is used
for errors that are tied to the entire model instead of to a specific
field
That means, with clean(), I cannot associate the errors raised from it with specific fields. I was wondering if models offer something similar to forms' clean_<fieldname>(). If not, where would you put this validation logic and why?
You could convert your clean method into a validator and include it when you declare the field.
Another option is to subclass the model field and override its clean method.
However there is no direct equivalent of defining clean_<field name> methods as you can do for forms. You can't even assign errors to individual fields, as you can do for forms
As stated in the comment I believe you should handle this validation at the modelform level. If you still feel like it would be better to do it closer to the model, and since they can't be changed, I would advise a change directly at the db level:
ALTER TABLE auth_user ADD UNIQUE (email)
Which is the poor way to add the unique=True constraint to the User model without monkey patching auth.
As requested, I think that a good way to go about customizing different forms should be done by inheriting from a base modelform. A good example of this is found in django-registration. The only difference is that instead of the parent form inheriting from forms.Form you would make it a modelForm:
class MyBaseModelForm(ModelForm):
class Meta:
model = MyModel
You could then inherit from it and make different forms from this base model:
class OtherFormWithCustomClean(MyBaseModelForm):
def clean_email(self):
email = self.cleaned_data['email']
if ExtendedUser.objects.filter(email=email).exists():
raise ValidationError('This email address already exists.')
return email

Django filter fields during exception email notifications

In our Production, we use Django's automated method to get email notifications on exceptions.
This works great with one problem, the whole POST data gets emailed to all developers.
Some of the POST fields can be sensitive user data (password or worse - credit card).
Is there a way to tell Django what fields to filter ?
I was thinking of creating a middleware that clear request.POST of certain fields on exceptions, alas it appears I can't change request.POST.
Any ideas ?
About modifying the POST dictionary (within your middleware), you can do the following:
request.POST = request.POST.copy(); // make request.POST mutable
request.POST['new_item'] = '...' // add element
del request.POST['password'] // remove element