What is the proper place to put ModelForm validation? - django

I'm using Django's ModelForms and would like to have validation on both models and forms. I'm rendering form using:
{{ form.as_table }}
What is the proper place to put validation on per-field basis (like forms' clean_fieldname) and as a whole (like forms' clean)?
I'd like to minimize amount of code written and not write the same validators twice (in both models and forms). Also, I'd like per-field validation errors to be shown over proper form field, not have all errors placed on top of the form (so not all checks in models' clean).

You might want to use the custom validators introduced in Django 1.2.
Don't forget that unless you are using a ModelForm you have to explicitly performs the model validation with a call to the full_clean() method on your model instance.

Related

Django: Inject form validation on ModelForm, not Model

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.

How to map HTML form data to Django model

I have a (quite complex) HTML form full of logic that triggers based on different choices in the selectpickers with fields that don't have quite the same names as the corresponding fields in the Django model, and sometimes I fear that I will need to add some logic when it comes to going from the data sent from the HTML form and to the Django model. I realise that I can probably not use a ModelForm in Django to handle this and have been looking for some examples of using the standard django.forms.Form to map the HTML form into my model but I haven't really found much. Can someone give me some hints?
It sounds as though your html form already exists and somehow you want to read in the POST data from that form. Without making any further assumptions as to how you ended up with an html form without a django ModelForm or forms.Form output via a view - as long as the form action location is mapped via a route to a view - the view can then process the request.POST data. Again all form validation goodness of django is out the door if you did not use django forms (model or forms based) and you have to do your own validations in the view then. Once the form data has been validated, initialize your model object like this: my_obj = ModelName(field_name1=form_input_data1, field_name2=form_input_data2, ...) and that's it. Then you can do my_obj.save().
Now let's say, it's not so bad. You actually are using the forms.Form inheritance to create your django-istic form class which has no direct relationship with the model. Now you can use the form related validation clean_field and clean steps etc... as well as all the built-in field types internal validation django automatically does. Then when you read in the POST data - do whatever it takes to map the form fields (via any transformation as necessary) to the django model object you are trying to construct, keeping in mind the default values and any model save custom assignments that may happen.
You can not just map a html form into your django model. You need to create a model form first and then render it in html.
If you want to somehow map your html form to model. First render your ModelForm in html. Create the exact copy of the html format of your django form and use it in html. Catch that in your view and process it like model form. But anyway you have to create the ModelForm.

Specifying specific form format in Django

I am using materializecss to give my django site some material elements. I have put together a form (the 'old' way using html) but now realised I need to use a django form instead. The problem is, these forms don't play well with materialises built in column system (they use classes to determine rows and column spacing). Here is an example of the layout I set up so far. However when defining the form through form.py, it spits out one input per layer.
My question is: what can I do to either a) get django to work with the html-defined form or b) make a 'form template' to give the input fields the appropriate classes?
If you want to see the code I can post some but I'm quite a new coder so it's messy.
Thanks!
There are three ways I can think of off the top of my head.
If you want full control over the HTML form, in a Django template or HTML form, simply map the names of your fields to match the underlying field names in the Django form. This way, when POSTed back to your view, Django will automatically link up the POSTed fields with the Django form fields.
For example, if you have a field username in your Django form (or Django model if using ModelForm), you could have an element <input type="text" name="username" maxlength="40"> (that you can style any way you need) on your HTML form that Django will happily parse into your Django form field, assuming your view is plumbed correctly. There is an example of this method in the Django documentation.
Another way is to customize the Django form field widgets in your Django form definition. The Django documentation talks a little bit about how to do this. This is great for one offs, but is probably not the best approach if you expect to reuse widgets.
The final approach would be to subclass Django form field widgets to automatically provide whatever attributes you need. For example, we use Bootstrap and have subclassed nearly all of the widgets we use to take advantage of Bootstrap classes.
class BootstrapTextInput(forms.TextInput):
def __init__(self, attrs=None):
final_attrs = {'class': 'form-control'}
if attrs is not None:
final_attrs.update(attrs)
super().__init__(attrs=final_attrs)
Then it's simply a matter of letting the Django form know which widget to use for your form field.
class UsernameForm(forms.ModelForm):
class Meta:
model = auth.get_user_model()
fields = ['username']
widgets = {'username': BootstrapTextInput()}
Hope this helps. Cheers!

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 validation without a custom form

I am using a django DateField in my model.
class CalWeek(models.Model):
start_monday = models.DateField(verbose_name="Start monday")
I have a custom validation method that is specific to my modelField: I want to make sure that it is actually a Monday. I currently have no reason to use a custom ModelForm in the admin--the one Django generates is just fine. Creating a custom form class just so i can utilize the clean_start_monday(self)1 sugar that django Form classes provide seems like a lot of work just to add some field validation. I realize I can override the model's clean method and raise a ValidationError there. However, this is not ideal: these errors get attributed as non-field errors and end up at the top of the page, not next to the problematic user input--not an ideal UX.
Is there an easy way to validate a specific model field and have your error message show up next to the field in the admin, without having to use a custom form class?
You can look into Django Validators.
https://docs.djangoproject.com/en/dev/ref/validators/
You would put the validator before the class, then set the validator in the Field.
def validate_monday(date):
if date.weekday() != 0:
raise ValidationError("Please select a Monday.")
class CalWeek(models.Model):
start_date = models.DateField(validators=[validate_monday])