Django: Inject form validation on ModelForm, not Model - django

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.

Related

django generic view update/create: update works but create raises IntegrityError

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.

Django Model Field Metadata

I have a Django model and I wish to denote that some of the model fields are private so that when I display a ModelForm based on this model I can display these fields marked as such.
I'd like this to be specified on the model rather than the form as I think that's where it belongs.
I'm wondering what the best way to do this is. Can I write a decorator #private to do this? Can anyone point me at an example?
Thanks
There's no one feature that perfectly fits your problem, but here's a couple of suggestions:
Add the information to the model's Meta class, which you can then access from your form via the _meta attribute on the model
Utilise the help_text option on your model fields (obviously this won't help you to "decide programatically which fields I should be displaying to other users")
I do agree that it's debatable as to whether this belongs at the model layer; it seems like business logic to me.

Valid use of django formset?

I have a many to many link between Foos and Bars. I don't particularly like the multi select widget so was thinking of having a widget which allows selecting a single Bar and a button to add more choice fields.
From what I've been reading formsets may be the answer. Is it valid to have a formset made up of a form with only one choice field or is there a better way to get the behaviour I'm looking for?
I wouldn't worry about the quantity of fields in the form. If your 'child' model only has the one field, then I'd say it's perfectly valid to use an formset with single field forms in this application.
You should take a look at inline formsets, they should help with exactly what you need. Although I'm not 100% sure they work for M2M...
additional thought: If it doesn't inline forsmet doesn't work directly with M2M, you can just use a model formset, and manually save the relationship in your view after using formset.save(commit=False). docs: formset saving

Model validation. Don't want to repeat myself. Django

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

Django: Would models.Field.validate() for all validation save a lot of code writing?

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.