I have a model M that has a field num=models.IntegerField()
I have a modelform called F for model M.
I want to ensure that num is never negative.
If I do validation in my form class, F, then I can do clean_num():
if negative then throw ValidationError('Num can never be negative').
This ValidationError will be automatically redisplayed to the user by
redirecting him to back to the form that he submitted and displaying
the 'Num can never be negative' message on top of the num field.
Thats all done automatically by django as soon as I throw the
ValidationError from the clean_fieldname method.
I would like to be able to do all that, but in the model class.
F is the ModelForm created from a model class M. M defines that
the field num can never be negative.
When I'm calling is_valid() on a form, I want the functions defined in the model
to check for validation for any ModelForm that references this model.
How can I achieve this?
See Model validation (Django 1.2+ only).
You could also use PositiveIntegerField for this particular problem.
If your validation depends only on field value, you can implement your own field type as described here: https://docs.djangoproject.com/en/dev/howto/custom-model-fields/
Thanks to everyone who posted an answer. But i found exactly what i asked about so if you're interested:
You can define the proper validators just once for the model. All the forms using this model will have the ValidationError('cant use this name') be appended to their field_name.errors list.
Note, that they will be added to the field in the form for which the model field validator is running.
Anyway, check this out:
Django: how to cleanup form fields and avoid code duplication
Related
I have a form in Django that is used in both a CreateView and an UpdateView. One of the field is hours_allocated which cannot be < 0. In my model, I have declared it as:
hours_allocated= models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(Decimal('0.0')), MaxValueValidator(Decimal('24.00'))])
When the record is updated (UpdateView) this is fine. However, in the CreateView, I would like to ensure that the value is > 0 (not = 0.0 as allowed in UpdateView). If I add a clean_hours_allocated() method to check if the value is > 0, it applies to both the CreateView and UpdateView.
Is there a way to have different validation at the form level based on whether the form is used in an UpdateView or CreateView? Or, is there a way to properly handle validation in the view itself?
I'm not an expert on Django. However, I think you could do something like this in forms.py.
def clean_hours_allocated(self):
if not self.instance.pk: # Update instances have a pk and wouldn't go in this.
## Logic to validate Create Instances
return hours_allocated
I don't think this is the perfect solution, but I believe this should fulfill your task.
I believe one approach would be adding a hidden field to your form called something like mode which must be one of {'create', 'update'}. Then you set the value of this field appropriately based on the view which instantiates the form.
Once this is in place, you could check cleaned_data['mode'] in your desired validation method (see Django's docs for a detailed explanation of the options). Depending on its value, an if-else branch would allow you to apply validation logic differently
Question: Is there a way to inject form field validation on the ModelForm instead of the Model?
Justification: I have three ModelForm's that update the same Model instance, which have default conditions for blank. I should have designed three different Models for each form, but I'm to far in to make a change.
Please assist!
Thanks,
Neel
ModelForm is a kind of Form (as it inherits from BaseForm), so you can use Form field validation methods to do it because clean() method is inheried from BaseForm. So for field named foo you use clean_foo() method to do cleanup
leotrubach's answer is the way to go, I just want to add that the django documentation on the subject is a good read.
I'm using CreateView and UpdateView directely into urls.py of my application whose name is dydict. In the file forms.py I'm using ModelForm and I'm exluding a couple of fields from being shown, some of which should be set when either creating or updating. So, as mentioned in the title, update part works but create part doesn't which is obvious because required fields that I have exluded are sent empty which is not allowed in my case. So the question here is, how should I do to fill exluded fields into the file forms.py so that I don't have to override CreateView?
Thanks in advance.
Well, you have to set your required fields somewhere. If you don't want them to be shown or editable in the form, your options are to set them in the view (by using a custom subclass of CreateView) or if appropriate to your design in the save method of the model class. Or declare an appropriate default value on the field in the model.
It would also work to allow the fields into the form, but set them to use HiddenInput widgets. That's not safe against malicious input, so I wouldn't do that for purely automated fields.
You cannot exclude fields, which are set as required in the model definition. You need to define blank=True/null=True for each of these model fields.
If this doesn't solve your issue, then please show us the model and form definitions, so we know exactly what the code looks like.
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
Can anyone explain this? It's really unintuitive that a form validation method would update the related model. I understand it doesn't commit to the database, but why even modify the model in memory? Why not do it with form.save()?
I believe the reason is this:
The first time you call is_valid() or
access the errors attribute of a
ModelForm has always triggered form
validation, but as of Django 1.2, it
will also trigger model validation.
This has the side-effect of cleaning
the model you pass to the ModelForm
constructor. For instance, calling
is_valid() on your form will convert
any date fields on your model to
actual date objects.
From here. Logically, this does actually make sense. Suppose I have a model like this:
class SomeModel(models.Model):
Somefield = models.CharField(unique=True)
Then as far as the modelform is concerned, validating the field TextInputField would work for any value. However, from the model's perspective, not any value will do - that value must be unique.
The validation mechanisms are described here. Basically, calling full_clean on a model causes that model to check its own fields as per the OO paradigm. So for this reason, the in memory image of the model can be updated and validating it will check it can actually be written to the database.
Thus it makes sense, because your guard:
if form.is_valid():
Really ought to ensure that:
form.save()
Can actually happen.