Inserting Errors into an InlineFormSet Custom Validator - django

I am using the following custom form validator to ensure that there is not more than one correct entry submitted to my application through an InlineFormSet.
class BaseAnswerFormSet(forms.models.BaseInlineFormSet):
def clean(self):
if any(self.errors):
return
if len([d['correct'] for d in self.forms if d['correct'].value()]) !=1:
raise forms.ValidationError("There must be one and only one correct answer")
return
This is working, as the form object that is presented will return False when evaluated as .is_clean() but there is no error returned. Here is what it shows up as when I use pdb in the view that handles the POST:
(Pdb) answerformset.is_valid()
False
(Pdb) answerformset.errors
[{}, {}, {}]
Shouldn't the raise forms.ValidationError("There must be one... create an error entry? I know that each of the empty dicts in the answerformset.errors list is for each of the answer forms, but I thought that there would be a non_field_error or something like that?
How can I get this clean function to return an error that I can display in a template? How can I add a non_field_error to this?

Please read Custom Formset Validation. Formset custom errors can be accessed using non_form_errors:
answerformset.non_form_errors()

Related

Django modelform validation

I am using django modelforms and formsets. But the problem I am facing is that when the form is not validated, instead of showing the erros on the html page it gives and error in the view.
View `
def create(request):
if request.method=="POST":
foo= fooform(request.POST,prefix="fooform")
if foo.valid():
#do stuff
else:
foo= fooform(prefix="fooform")
return render(request,'property/create.html',{'foo':foo})
`
Problem:
When the form does not validate there is a obvious error in the view where in it unable to find the fooform. I want the html page to show the error-ed fields. What am I doing wrong?
Edit:
I think I have found the main issue. The issue is that the modelform is not applying its form validations at the browser level. Because of this the form is getting submitted even though it is not valid. As a result it is failing the validation and not finding the else part.
How about using this pattern instead:
def create(request):
foo = fooform(request.POST or None, prefix='fooform')
if foo.valid():
#do stuff
return render(request,'property/create.html', {'foo': foo})
Your las line is wrong:
It say:
return render(request,'property/create.html',{'foo':fooform})
And should be:
return render(request,'property/create.html',{'foo':foo})
If this is not the issue, please, post errors messages and form code.

Django form validation fails (on update)

I'm fighting with the Django form validation for a while now. I'm using pretty basic code. Returning the form data using an ajax request is the only special thing but I don't think its the cause auf my problems here.
I'm trying to update an user entry. I create an instance of the user and I feed that instance into the CreateForm() but I still get the duplicate username error.
here is a part of the view:
def user_update(request):
if request.is_ajax():
print request.POST
user = User.objects.get(pk=int(request.POST['pk']))
print user
form = UserCreateForm(request.POST, instance=user)
print form
if form.is_valid():
form_cleaned = form.cleaned_data
else:
print '### form is invalid ###'
print form.error_messages
The output for request.POST is:
<QueryDict: {u'username': [u'testuser'], u'password1': [u'test'], u'password2': [u'test'], u'pk': [u'27'], u'csrfmiddlewaretoken': [u'wyBoaBAlxLTO952BzWSxR7HMK6W7nsAM'], u'email': [u'soso#soso.so']}>
The output for print user is:
testuser
In print form I always get the duplicate username error, hence the form is always invalid.
Please give me a hint. Many thanks in advance.
For form validation, you have used UserCreateForm.
Apart from the regular regex validation of fields, the UserCreateForm also checks if a given username is available or not. And any existing user will fail this check.
You should use an appropriate form like UserChangeForm or if your application only updates a particular field like password, then choose from the built in forms.
With reference to UserCreationForm in django, it has clean_username() validation method that checks if user with given username already exists, if so raises the validation error that you are getting.
You should better use UserChangeForm if you want to update user fields.
The UserCreationForm's clean_username() has the following code:
User._default_manager.get(username=username)
If this is successful, you get your error. Since your user (admin) already exists, you'll always get the error. As suggested by #sudipta, you're better off with the UserChangeForm.
IMHO, you could define your own custom form either with your own behaviour rather than using a stock one and deviate from its original purpose.

is_valid() vs clean() django forms

In the process of finding a way to validate my django forms, I came across two methods is_valid() and clean() in the django docs. Can anyone enlighten me the how they are different/same? What are the pros and cons of either?
Thanks.
is_valid() calls clean() on the form automatically. You use is_valid() in your views, and clean() in your form classes.
Your clean() function will return self.cleaned_data which if you will notice in the following view is not handled by you as the programmer.
form = myforms.SettingsForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
#do stuff
You didn't have to do clean_data = form.is_valid() because is_valid() will call clean and overwrite data in the form object to be cleaned. So everything in your if form.is_valid() block will be clean and valid. The name field in your block will be the sanitized version which is not necessarily what was in request.POST.
Update
You can also display error messages with this. In clean() if the form data isn't valid you can set an error message on a field like this:
self._errors['email'] = [u'Email is already in use']
Now is_valid() will return False, so in the else block you can redisplay the page with your overwritten form object and it will display the error message if your template uses the error string.
Just wanted to add that the best way now to add an error to a form you're manually validating in is_valid() is to use Form.add_error(field, error) to conform with Django's ErrorDict object.
Doing
self._errors['field'] = ['error message']
will come out funky when rendering {{form.errors}}, like:
fielderror messsage
instead of the expected
field
-error message
so instead do:
self.add_error('email', 'Email is already in use')
See https://docs.djangoproject.com/en/1.10/ref/forms/api/#django.forms.Form.add_error

redisplay django form after successfull call to is_valid() with custom error message

I have a django form that first validates its data through calling form.is_valid(). If its not, the form is redisplayed, with an error message regarding the invalid data.
Now if is_valid() is true, I try to save the data in an ldap backend. If the form.cleaned_data is not in correspondance with the ldap data type, I get an Exception from my ldap save method. Now what I would like to do in this case is to redisplay the form with an error message, just like the thing that happens after form.is_valid() returns false.
I tried reading some docs and also some django source, but could not find where I could hook into this.
An alternative would be to carefully build the form of (custom) form fields that would "guarantee" that the data is allready compliant to ldap syntax.
But I would like to make shure that I catch ldap syntax errors and display them in a convenient form. So if I could hook into that form redisplay mechanism would make me a happy little programmer :-)
Any ideas or hints?
Under your class for the form that extends forms.Form, add one of the following methods, assuming you have a is_valid_ldap_data() defined somewhere:
for a whole form:
def clean(self):
if !is_valid_ldap_data(self.cleaned_data.get("fieldname")):
raise forms.ValidationError("Invalid LDAP data type");
return self.cleaned_data
or for a single field:
def clean_fieldname(self):
if !is_valid_ldap_data(self.cleaned_data['fieldname'])):
raise forms.ValidationError("Invalid LDAP data type");
return self.cleaned_data['fieldname']
At your Form subclass implement custom field validation method
http://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute
Validation logic must go where it belongs. If form.is_valid() == True than form.cleaned_data must be valid. Just because code says so. You want to hide some of validation logic somewhere else -- and that is just bad practice.
It seems to me that you just have an additional step of validation.
You could, depending on which are your specific needs:
Put a validator on your fields, to check for simple things, or
Implement additional cleaning on your field or on multiple fields at once,
but in every case if the form is not valid for any reason (as in your case), is_valid should return False.

adding errors to Django form errors.__all__

How do I add errors to the top of a form after I cleaned the data? I have an object that needs to make a REST call to an external app (google maps) as a pre-save condition, and this can fail, which means I need my users to correct the data in the form. So I clean the data and then try to save and add to the form errors if the save doesn't work:
if request.method == "POST":
#clean form data
try:
profile.save()
return HttpResponseRedirect(reverse("some_page", args=[some.args]))
except ValueError:
our_form.errors.__all__ = [u"error message goes here"]
return render_to_response(template_name, {"ourform": our_form,},
context_instance=RequestContext(request))
This failed to return the error text in my unit-tests (which were looking for it in {{form.non_field_errors}}), and then when I run it through the debugger, the errors had not been added to the forms error dict when they reach the render_to_response line, nor anywhere else in the our_form tree. Why didn't this work? How am I supposed to add errors to the top of a form after it's been cleaned?
You really want to do this during form validation and raise a ValidationError from there... but if you're set on doing it this way you'll want to access _errors to add new messages. Try something like this:
from django.forms.util import ErrorList
our_form._errors["field_name"] = ErrorList([u"error message goes here"])
Non field errors can be added using the constant NON_FIELD_ERRORS dictionary key (which is __all__ by default):
from django import forms
errors = my_form._errors.setdefault(forms.forms.NON_FIELD_ERRORS, forms.util.ErrorList())
errors.append("My error here")
In Django 1.7 or higher, I would do:
form.add_error(field_name, "Some message")
The method add_error was added in 1.7. The form variable is the form I want to manipulate and field_name is the specific field name or None if I want an error that is not associated with a specific field.
In Django 1.6 I would do something like:
from django.forms.forms import NON_FIELD_ERRORS
errors = form._errors.setdefault(field_name, form.error_class())
errors.append("Some message")
In the code above form is the form I want to manipulate and field_name is the field name for which I want to add an error. field_name can be set to NON_FIELD_ERRORS to add an error not associated with a specific field. I use form.error_class() to generate the empty list of error messages. This is how Django 1.6 internally creates an empty list rather than instantiate ErrorList() directly.
You should raise the validationerror.
Why not put the verification within the form's clean method
class ProfileForm(forms.Form):
def clean(self):
try:
#Make a call to the API and verify it works well
except:
raise forms.ValidationError('Your address is not locatable by Google Maps')
that way, you just need the standard form.is_valid() in the view.
You're almost there with your original solution. Here is a base Form class I built which allows me to do the same thing, i.e. add non-field error messages to the form:
from django import forms
from django.forms.util import ErrorDict
from django.forms.forms import NON_FIELD_ERRORS
class MyBaseForm(forms.Form):
def add_form_error(self, message):
if not self._errors:
self._errors = ErrorDict()
if not NON_FIELD_ERRORS in self._errors:
self._errors[NON_FIELD_ERRORS] = self.error_class()
self._errors[NON_FIELD_ERRORS].append(message)
class MyForm(MyBaseForm):
....
All my forms extend this class and so I can simply call the add_form_error() method to add another error message.
I'm not sure how horrible of a hack this is (I've only really worked on two Django projects up until this point) but if you do something like follows you get a separate error message that is not associated with a specific field in the model:
form = NewPostForm()
if something_went_horribly_wrong():
form.errors[''] = "You broke it!"
If the validation pertains to the data layer, then you should indeed not use form validation. Since Django 1.2 though, there exists a similar concept for Django models and this is certainly what you shoud use. See the documentation for model validation.