Is it possible to add form errors (not a field specific errors) to a form that is already validated (form.is_valid() is already called and the result is True)?
I have additional validation logic (with database requests) which I prefer to execute after standard form validation is passed, but it will be better to associate new errors with form
Or maybe I have to call this additional validation within clear() form function?
Try to check it in the clean method of the form after all, but check existing errors before.
Like that..
clean(self):
...
...
if not self._errors:
# your extra check
If this is validation based on database requests, then it shound as model validation.
Related
I had the following code working in Django 1.5:
if formset.is_valid():
formset.save()
The forms inside the formset are performing a check inside clean() to make sure that certain required fields are entered (only known at runtime). This means that although a user might not enter any values the validation would still run and ask for data entry if required.
With Django 1.6 this behaviour does not apply anymore. If there was no change then the form's clean() method does not get called anymore when calling formset.is_valid().
I could not find anything in the changelogs that would explain this new behvaiour. Is there a flag on formset.is_valid() to force the validation even if no changes were made?
It seems as if the validation was skipped since the formset changed the form's edit_permitted attribute.
The following change fixed the issue I had:
# Was required to disable shortcut for validation when
# no data was entered
for form in formset:
form.empty_permitted = False
We are using some complex form workflow where we want to have a kind of Ajax prevalidation verifications all the time before final submission (and validation). So we are making an Ajax request to the server and validating the form, but not saving the results. If there are errors, we display them to the user.
But the issue is with required fields which all get marked as missing values, when they are empty (because the user has not yet finalized the form's content). So my question is, how can we make a validation run on only those fields which are not empty? For final submission we will validate all fields and catch missing fields. But for this partial validation we would like to have only filled fields validated.
One approach would be to iterate through the form fields setting required=False prior to validation. In your ajax view, something like this should work:
for field in myform.fields.itervalues():
field.required = False
json_result = json.dumps({'form_valid': myform.is_valid()})
Django says "Note that if the form hasn’t been validated, calling save() will do so by checking form.errors. A ValueError will be raised if the data in the form doesn’t validate – i.e., if form.errors evaluates to True."
What I am not able to clear out is
Will the validation be run when I call the save on the ModelForm?
And if I have validated the form data using form.is_valid() method than will the save() method do the validation again.(I am asking this as some validation requires I do a database query for Foreign Key validation as it has some restriction)
I am asking this question as if the first one is true than I think it would be a good idea to not to validation at all and rather just call the save method, letting it call the validation method and just catching it.
First question - yes. As the docs say, calling save() accesses form.errors, which triggers validation if the form has not yet been validated.
Second question - no, the validation will not be run again. Once the form has been validated, whether by calling is_valid() or by calling .save(), form.errors is populated and can be read from without re-running validation.
With either approach, validation will be run exactly once.
I'm trying to do this in Django:
When saving an object in the Admin I want to save also another object of a different type based on one of the fields in my fist object.
In order to do this I must check if that second object already exists and return an validation error only for the particular field in the first object if it does.
My problem is that I want the validation error to appear in the field only if the operation is insert.
How do I display a validation error for a particular admin form field based on knowing if the operation is update or insert?
P.S. I know that for a model validation this is impossible since the validator only takes the value parameter, but I think it should be possible for form validation.
This ca be done by writing a clean_[name_of_field] method in a Django Admin Form. The insert or update operation can be checked by testing self.instance.pk.
class EntityAdminForm(forms.ModelForm):
def clean_field(self):
field = self.cleaned_data['field']
insert = self.instance.pk == None
if insert:
raise forms.ValidationError('Some error message!')
else:
pass
return field
class EntityAdmin(admin.ModelAdmin):
form = EntityAdminForm
You have to use then the EntityAdmin class when registering the Entity model with the Django admin:
admin.site.register(Entity, EntityAdmin)
You can write your custom validation at the model level:
#inside your class model ...
def clean(self):
is_insert = self.pk is None
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
#do your business rules
if is_insert:
...
if __some_condition__ :
raise ValidationError('Dups.')
Create a model form for your model. In the clean method, you can set errors for specific fields.
See the docs for cleaning and validating fields that depend on each other for more information.
That is (probably) not an exact answer, but i guess it might help.
Django Admin offers you to override save method with ModelAdmin.save_model method (doc is here)
Also Django api have a get_or_create method (Doc is here). It returns two values, first is the object and second one is a boolean value that represents whether object is created or not (updated an existing record).
Let me say you have FirstObject and SecondObject
In your related admin.py file:
class FirstObjectAdmin(admin.ModelAdmin):
...
...
def save_model(self, request, obj, form, change):
s_obj, s_created = SecondObject.objects.get_or_create(..., defaults={...})
if not s_created:
# second object already exists... We will raise validation error for our first object
...
For the rest, I do not have a clear idea about how to handle it. Since you have the form object at hand, you can call form.fields{'somefield'].validate(value) and write a custom validation for admin. You will probably override clean method and try to trigger a raise ValidationError from ModelAdmin.save_model method. you can call validate and pass a value from there...
You may dig django source to see how django handles this, and try to define some custom validaton steps.
Am I thinking about this all wrong, or am I missing something that's really obvious?
Python's style guide say's less code is better (and I don't think that's subjective... It's a fact), so consider this.
To use forms for all validation means that to write a model field subclass with custom validation, you'd have to:
Subclass models.Field
Subclass forms.Field
Add custom validation to your forms.Field subclass
Set your custom form field as the default form field for the custom model field
Always use a django model form if you want to perform validation
With all validation in the model you'd just have to:
Subclass models.Field
Add custom validation to your models.Field subclass
Now you could use your model field in an API that bypasses all use of web forms, and you'd still have validation at lowest level. If you used web forms, the validation would propagate upwards.
Is there a way to do this without having to write the Django team and wait for them to fix this error?
This sort of thing is already possible in Django's development version:
There are three steps in validating a
model, and all three are called by a
model’s full_clean() method. Most of
the time, this method will be called
automatically by a ModelForm. You should only need to
call full_clean() if you plan to
handle validation errors yourself.
See the docs.
e.g. (as part of your model class):
def clean(self):
from django.core.exceptions import ValidationError
# Don't allow draft entries to have a pub_date.
if self.status == 'draft' and self.pub_date is not None:
raise ValidationError('Draft entries may not have a publication date.')
# Set the pub_date for published items if it hasn't been set already.
if self.status == 'published' and self.pub_date is None:
self.pub_date = datetime.datetime.now()
The ModelForm's validation process will call into your model's clean method, per this:
As part of its validation process,
ModelForm will call the clean() method
of each field on your model that has a
corresponding field on your form. If
you have excluded any model fields,
validation will not be run on those
fields. Also,
your model's clean() method will be
called before any uniqueness checks
are made. See Validating objects for
more information on the model's
clean() hook.
It is already being fixed, and will be in Django 1.2.