Django - form Clean() and field errors - django

I'm trying to set field errors in a form clean() and I'm currently doing:
self._errors['address'] = self._errors.get('address', ErrorList())
self._errors['address'].append(_(u'Please specify an address.'))
Is there a better and if possible shorter method for doing this?

New in Django 1.7 is Form.add_error( field, message ).
https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.add_error

Maybe this will help you . Its generally preferred you override clean and inside the function you could do the following
If you want to raise form specific errors you could do .
self._errors["field"] = ErrorList([u"Error"])
this is make sure you get the error class
if you have an non field error you could simple raise a validation error like so
raise forms.ValidationError(_("Error"))
Hope this helps.

Standard way is raise ValidationError(message).
Move field-specific validation to clean_<fieldname>() methods, clean_address in your case. ValidationError raised in such method will attach error message to specific field. One raised from clean() will be attributed to model in general.

Related

Model validation: can I access ValidationError code?

I have two directly related questions.
Django documentation recommends raising ValidationError with a code:
# Good
ValidationError(_('Invalid value'), code='invalid')
# Bad
ValidationError(_('Invalid value'))
How can I access this code in tests? All my attempts to use as_data, as_json, or simply .code on the caught exception fail. Unfortunately, the suggestions I see all relate to form validation. My test validates the models.
It is almost the same question as asked before (I don't use forms).
The related question: the same documentation page linked above gives a few examples of how to raise ValidationError, and while "Raising ValidationError" section recommends using the code, "Using validation in practice" never mentions it again, and the examples there don't use code. I wonder if that's an indication of this feature being stale.
I learned how to debug Django tests in PyCharm, and it helped me find a solution. For others' sake:
The error codes are accessible via exception.error_dict[field_name][err_no].code. For example, the following checks a very specific error is raised:
def test_negative_photo_number(self):
"""Cannot create photo with negative photo number"""
with self.assertRaises(ValidationError) as ve_context:
self.create_photo(album_number=1, photo_number=-2)
e = ve_context.exception
print(e.error_dict)
self.assertEqual(len(e.error_dict.keys()), 1, 'Encountered more than one problematic field')
self.assertEqual(len(e.error_dict['number']), 1, 'Encountered more than one error')
self.assertEqual(e.error_dict['number'][0].code, 'min_value')
For ValidationError raised outside field validators (e.g. by model.clean method), replace field name ('number' above) with __all__.

Django: Order of validation in Models

Recently I found out that its possible to define Django form validation directly in the models.py file. This can be done the following way:
fev1_liter = models.DecimalField(validators=[MaxValueValidator(8.2),
MinValueValidator(0.3)],
max_digits=3, decimal_places=2)
This is an awesome alternative to validation in forms.py, but I do have a very annoying problem:
How can I control in which order the validation is executed?
In this example Django will first validate if the inputs digits is in the format x.xx and thereafter min and max value. This results in some very confusing error messages.
Thanks in advance!
For each model field, field.clean() first performs field validation via field.validate(), then via field.run_validators(), validators are called in order they are returned from the field.validators iterator.
This makes sense, because in the general case you can expect your validators to fail if the field validation failed, so it makes for easier debugging. Remember that field validators are non-obligatory, so field.validate() takes precedence. If you want to change the behavior, you'll have to create your own Field classes and override the field.clean() behavior.
You can inspect the field sources for more details.

Django ValidationError - how to use this properly?

Currently there is code that is doing (from with the form):
# the exception that gets raised if the form is not valid
raise forms.ValidationError("there was an error");
# here is where form.is_valid is called
form.is_valid() == False:
response['msg']=str(form.errors)
response['err']='row not updated.'
json = simplejson.dumps( response ) #this json will get returned from the view.
The problem with this, is that it is sending err message to the client as:
__all__"There was an error."
I want to remove the "all" garbage from the error template that is returned. How can I go about doing this? it seems to get added deep in django form code.
It's because the error is not associated with any field in particular, but it's so called non-field error.
If you're only interested in non-field errors, just simply pass this to the response:
response['msg']=str(form.errors['__all__'])
errors is an instance of a subclass of dict with some special rendering code. Most of the keys are the fields of the form, but as the docs describe, raising ValidationError in clean produces an error message that isn't associated with any particular field:
Note that any errors raised by your Form.clean() override will not be associated with any field in particular. They go into a special “field” (called __all__), which you can access via the non_field_errors() method if you need to. If you want to attach errors to a specific field in the form, you will need to access the _errors attribute on the form, which is described later.
https://docs.djangoproject.com/en/dev/ref/forms/validation/
So you can either generate your string representation of the errors differently (probably starting with form.errors.values() or form.errors.itervalues(), and maybe using the as_text method of the default ErrorList class) or associate your error with a particular field of the form as described in the docs:
When you really do need to attach the error to a particular field, you should store (or amend) a key in the Form._errors attribute. This attribute is an instance of a django.forms.utils.ErrorDict class. Essentially, though, it’s just a dictionary. There is a key in the dictionary for each field in the form that has an error. Each value in the dictionary is a django.forms.utils.ErrorList instance, which is a list that knows how to display itself in different ways. So you can treat _errors as a dictionary mapping field names to lists.
If you want to add a new error to a particular field, you should check whether the key already exists in self._errors or not. If not, create a new entry for the given key, holding an empty ErrorList instance. In either case, you can then append your error message to the list for the field name in question and it will be displayed when the form is displayed.
https://docs.djangoproject.com/en/dev/ref/forms/validation/#form-subclasses-and-modifying-field-errors

Invalid keyword argument on new model entry

I have the following model:
class mark(models.Model):
title=models.CharField(max_length=35)
url=models.URLField(max_length=200)
user=models.ManyToManyField(User,blank=True)
and then I use a form to save some data to the db. My code inside the view that saves the data is:
new_mark= mark(url=request.POST['url'],
title=request.POST['title'],
user=request.user)
new_mark.save()
Of course I have all the data validation, login required validation, etc.
When I run this it throws me an unexpected
'user' is an invalid keyword argument for this function
on theuser=request.user) line. Any ideas what might be wrong?
Please provide the whole traceback and make sure your view has no function named "mark" etc (You probably also want to change mark to Mark to follow Python and Django style guides.) test via print type(mark) before the "new_mark = …" line.
Also I am not 100% sure if a ManyToMany field allows settings data like that, eg try:
new_mark= mark(url=request.POST['url'],
title=request.POST['title'])
new_mark.user.add(request.user)
new_mark.save()
And since it's an m2m field you probably want to rename the field to users.

Inject errors into already validated form?

After my form.Form validates the user input values I pass them to a separate (external) process for further processing. This external process can potentially find further errors in the values.
Is there a way to inject these errors into the already validated form so they can be displayed via the usual form error display methods (or are there better alternative approaches)?
One suggestions was to include the external processing in the form validation, which is not ideal because the external process does a lot more than merely validate.
For Django 1.7+, you should use form.add_error() instead of accessing form._errors directly.
Documentation: https://docs.djangoproject.com/en/stable/ref/forms/api/#django.forms.Form.add_error
Form._errors can be treated like a standard dictionary. It's considered good form to use the ErrorList class, and to append errors to the existing list:
from django.forms.utils import ErrorList
errors = form._errors.setdefault("myfield", ErrorList())
errors.append(u"My error here")
And if you want to add non-field errors, use django.forms.forms.NON_FIELD_ERRORS (defaults to "__all__") instead of "myfield".
You can add additional error details to the form's _errors attribute directly:
https://docs.djangoproject.com/en/1.5/ref/forms/validation/#described-later
https://docs.djangoproject.com/en/1.6/ref/forms/validation/#modifying-field-errors
Add error to specific field :
form.add_error('fieldName', 'error description')
**Add error to non fields **
form.add_error(None, 'error description')
#Only pass None instead of field name