Django, form.is_valid() What does it check? - python

I do not use the form of models.form
I received and stored each items of request.POST and request.FILES
I will make my validation function.
So I wonder what validation it does. (ex. input is empty, etc.)

Manually processing request.POST and request.FILES is really a bad idea: the data can be "forged", and the HTML specifications on how the request is constructed with a <form> have some peculiarities. For example if a checkbox <input type="checkbox" name="somename"> is not checked, then it does not appear in the request, whereas if it is checked, it appears with no value in the request.
A first problem is thus that not per se all fields of the form will appear in the request.POST, that is not per se a problem: some fields might not be required. The Django form will thus check for all fields that are required, if the value appears in request.POST and request.FILES.
Next the form will start cleaning the data. This means that it converts the data, which are all strings, to a more suitable datatype. For example for a DateField, it will try to convert this to a date object. It will normally try all formats in the DATE_INPUT_FORMAT setting [Django-doc], but you can customize that when you construct a DateField to thus use a different format for that specific field. Some fields, like a BooleanField will for example look if the field is indeed in the request.POST and then return True and otherwise False, so this is not just a simple mapping of the value in request.POST, since that value might not be there.
Cleaning does not only map strings on corresponding Python objects, it can also do some (small) post-processing on these fields. For example a CharField will by default trim the string, and thus remove leading and trailing spaces, whereas a ModelChoiceField will pick the corresponding model object for that primary key from the database. A form field has also a list of empty values: values that are considered to be empty. For example for a CharField, you can set '/' as an empty value. It will then thus consider the slash as the field being empty.
One can create extra form fields than the ones that are defined, and this thus means that it is easy to construct for example an EANField where you implement the cleaning once, but that would mean that you need to do that for every view. Form fields are thus used to make cleaning more reusable by implementing the cleaning process into the field.
Next in line are validations on the form fields. While a text field in HTML can have a <input type="text" minlength="6" maxlength="24">, you can nevery rely on what the browser will post as values: indeed POST requests can be forged, so while the HTML form might intent to prevent people from picking a username with less than six characters, or more than twenty-four characters, you can not be sure that this will be the case in the POST request. A form field can be passed validators=… [Django-doc]: a set of callables that raise a validation error in case the item is not valid. For example for a CharField, if you pass a value for the min_length=… and/or max_length=… validators, it will thus check these. It will add all ValidationErrors to a list, which is often cumbersome to implement in a view: most views that validate data will check a certain condition, for example the minimum length of the characters for a password, and if that fails, report an error, whereas with a Form, it can also add an extra error that you should use at least one digit. The form will do that for all form fields, and thus construct a dictionary mapping the name of the fields to a list of errors.
If all the form fields successfully return validate the data, then the form will create an attribute .cleaned_data [Django-doc] that maps the names of the fields to the corresponding value that has been cleaned by that form field. One can however further add additional cleaning and validation to the Form.
Then there are validations at the form level itself. If you want to slightly customize the form, then form a field named field_name, you can implement a clean_field_name method. This method can access the .cleaned_data dictionary, and then run additional validation on that field and raise a ValidationError, and furthermore "clean" the function, for example by returing an object of a different type, or doing some post-processing on the data that is then returned. The Form will thus collect these values and update the dictionary with these cleaned values. Furthermore if the method raises a ValidationError, the error will be added to the .errors for that specific field.
After that, the form will call the .clean() method [Django-doc]. This will perform cleaning on the entire form. Indeed, now that all fields are cleaned individually, it might still be possible that there are errors about the combination of the fields. For example whether the two password fields match. Usually the clean method will thus work with multiple fields in the .cleaned_data attribute, and return for example that dictionary, or another object. If you raise a ValidationError here, the error will be put in the .errors under the '__all__' key, since this is not related to a specific field.
For a ModelForm [Django-doc], the validation is not over yet. Indeed, the model itself can still have some validation. It will update the instance wrapped in the form with the values of the cleaned data, and call the .full_clean() method [Django-doc] on the instance, and later also call the .validate_unique(…) method [Django-doc] to validate that for fields that should be unique (have unique=True), these are indeed unique. That last task is often more complicated than it looks at first sight, since if the instance is edited in the form, the check in the database, should exclude the current instance you are editing.
Only after all these validation, the Form or ModelForm will accept the change, and then .is_valid() will return True. If in one of the steps thus fails, the error will be added to the .errors, and thus .errors will try to construct a dictionary that tries to report all the problems with the data. This is useful if you re-render the form, since then the errors can be put next to the corresponding field, and the user thus gets feedback on all the problems with the data, not the first one encountered by the form, since that could result in a lot of attempts to eventually fix the form.
To summarize: there is a lot of logic in place in a Form to validate and clean data. Trying to implement that in a view is a popular mistake. It is cumbersome to do it correctly in a view, but it is also not very flexible: if you want to add an extra validator for a password for example, it will take a lot of work to implement that, especially if you want to that in multiple views. Forms are created to work with reusable components like form fields, validators, uniqness checks, etc. It is also quite robust since it is aimed to validate request data that might be forged, and thus was not validated properly by the HTML attributes.

Related

Django - Custom Form Field Example

I have a project in which I need to add a custom form field to a form (or formset) in which depending on the choice in the custom field selected, an integer in a database field is changed.
I can't seem to find any examples or prior questions which imply how to modify a database field via a custom field. I suspect it is done by overwriting the save() function in a ModelForm but cannot work out how.
Any advice on solving this problem would be greatly appreciated.
An Example:
My class has an integer field which is the field that needs updating.
class Employee(Model):
years = models.IntegerField()
However this field cannot be updated as it is, instead what is needed is a ChoiceField (I think) with different options that, depending on the selection, changes the field with a +1, -1, or reset to zero.
As previously mentioned this is where I imagine some work with the save() function needs to be done but am unsure.
As a side note in case this affects what can be done, eventually this ModelForm will need to be used in a formset so I can edit multiple objects on one page.

How I can use correctly django ModelChoiceField

I'm a newbie and right now I'm using Django forms to show some fields. One of these fields is a ModelChoiceField this show correctly model data, but I don´t know how I can fill a CharField later of select a option in the ModelChoiceField.
How I can make send the value of option selected for obtain the data that i need for later show this in a CharField, this is possible make directly from a djangoForm or do I need a new view to return the value?
There are two ways to achieve what you want I think:
Provide an initial value for your form value.
Bind your form to some data.
See this related stack overflow to provide initial values.
See Django's documentation on forms for binding data. There are two kinds of form states: bound and unbound. To create a bound form, you just need to create an instance of your form with some data: MyForm({'data-key1': 'data-value1', ...}).

Django Form prefix vs. Form set

There exist two ways in Django to process the several forms in one request:
Form prefix
Form sets
In which use case is each one preferable?
In my specific case, the form lists the fields for an object to be updated from a diff. For each field, an action can be defined (like "update value", "keep value"). The page contains forms for several objects.
If you have several different forms classes with same input names, like a PetForm and an OwnerForm with both a name input in the same page, then you have to use a prefix as explained in the documentation you linked.
If you want to have the same form class repeated, ie. to render a table of TicketForm, then you can use Formsets directly: Formsets use the form prefix feature internally and you don't have to worry about it.

How to factorize common fields with deform/colander ?

I need to display two forms depending on the context.
The second form should contain a name and email text input followed by the first form content in that order, but only when the user is not authenticated.
I tried with Python class inheritance but in every tested cases the two new fields were displayed after the content of the first form.
Is there a way to correcly factorise common fields in deform forms or to specify a different display order?
The solution is probably to define the schema imperatively:
http://docs.pylonsproject.org/projects/colander/en/latest/basics.html#defining-a-schema-imperatively

Django Save Incomplete Progress on Form

I have a django webapp with multiple users logging in and fill in a form.
Some users may start filling in a form and lack some required data (e.g., a grant #) needed to validate the form (and before we can start working on it). I want them to be able to fill out the form and have an option to save the partial info (so another day they can log back in and complete it) or submit the full info undergoing validation.
Currently I'm using ModelForm for all the forms I use, and the Model has constraints to ensure valid data (e.g., the grant # has to be unique). However, I want them to be able to save this intermediary data without undergoing any validation.
The solution I've thought of seems rather inelegant and un-django-ey: create a "Save Partial Form" button that saves the POST dictionary converts it to a shelf file and create a "SavedPartialForm" model connecting the user to partial forms saved in the shelf. Does this seem sensible? Is there a better way to save the POST dict directly into the db? Or is an add-on module that does this partial-save of a form (which seems to be a fairly common activity with webforms)?
My biggest concern with my method is I want to eventually be able to do this form-autosave automatically (say every 10 minutes) in some ajax/jquery method without actually pressing a button and sending the POST request (e.g., so the user isn't redirected off the page when autosave is triggered). I'm not that familiar with jquery and am wondering if it would be possible to do this.
before Saving:
for field in form.fields:
form.fields[field].required = False
then:
form.save()
The issue is that you have multiple Forms.
Partial. Incomplete. Complete. Ready for this. Ready for that.
Indeed, you have a Form-per-stage of a workflow.
Nothing wrong with this at all.
Figure out where in the workflow you are.
Populate and present the form for the next stage.
Forms can inherit from each other to save repeating validation methods.
Place the following into your form __init__
for field in form.fields:
form.fields[field].required = False
For example:
class MySexyForm(Form):
def __init__(self, *args, **kwargs):
super(MySexyForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].required = False
Then call:
form = MySexyForm(...)
form.save()
However you'll need to make sure your clean() method can handle any missing attributes by conditionally checking if they exist in cleaned_data. For example, if another form field validation relies on customer_id but your partial form have not specified one, then customer_id would not be in cleaned_data.
If this is for a model form, you could check if the value was in cleaned_data, and fallback onto instance.field if it was missing, for example;
def clean(self):
inst = self.instance
customer_id_new = self.cleaned_data.get('customer_id', None)
customer_id_old = getattr(self.instance, 'customer_id') if inst else None
customer_id = customer_id_new if customer_id_new else customer_id_old
Remember that the value new value will almost certainly not be in the same format as the old value, for example customer_id could actually be a RelatedField on the model instance but an pk int on the form data. Again, you'll need to handle these type differences within your clean.
This is one area where Django Forms really are lacking sadly.

Categories