Django Rendering Fields Manually

In lesson Displaying Forms in Django we have learned various ways to display forms in Django. Although the methods discussed allow us to quickly create a form, it also gives us least control over how they are rendered as HTML. If you want to have pixel perfect control over your forms read on.

Open feedback.html template from the blog app. It should look something like this:

Notice that the value of <input> tag's name attribute is same as that of field names defined in the Feedback model.

Point your browser to http://127.0.0.1:8000/feedback/. Enter some data in the name and email field (it doesn't matter whether the data is valid or not, just fill it) and leave the next two fields empty (i.e subject and comment). Finally hit the submit button. You will be displayed an empty form again.

Wait a minute! What's going on? All our form fields are required but Why it's now showing any errors?

The problem is that we are hardcoding our HTML, we are not using form methods like as_p() or as_table() to display the form fields (as well as errors associated with them). As a result, we are unable to show the bound state of the form to the user.

If we had been using f.as_p or f.as_table instead of hardcoding individual form field, we would get the errors as follows:

In addition to errors, the form is also not pre-populating data (valid or invalid) we entered in the
name and email field while submitting the form in the last request.

In the following section, we will learn how to rectify all these problems.

Recall that we can override Form's clean() method to add validation which requires access to more than one fields. The errors raised by clean() method are not specific to any field, in fact, the errors belong to the whole form. In Django terminology we call such errors Non-field errors. To access non-field errors inside the template we use form.non_field_errors variable. For example, we can use the following code to display non-field errors in our feedback form.

The biggest usability problem in our form is that in the bound state, it doesn't display any data that user has submitted in the previous request. For example, enter django in the name field and hit submit. You should see a form like this:

Notice that the name field has no data in it. Django provides bound field value in the variable form.field_name.value. So to display the data in the name field set it's value attribute to {{ form.name.value }}.

But there is one small problem. If the form is in unbound state the {{ form.name.value }} will print None. That's why you must always use the if tag to check the existence of a value in form.name.value variable first before displaying anything. For example:

We can also print the value of help_text attribute, if it is defined for a field in the model or form class. To print help_text we use {{ form.field_name.help_text }}. Recall that, we have defined help_text attribute for the name field while creating Feedback model:

As usual, this method doesn't output <form> tag and submit button (<input type="submit">), you have to add them manually in your code.

So as you can see, there is a trade-off between the amount of code you type and the control you get. The less code affords us the least control. We will be using some of the variables we learned here while creating forms for the cadmin app.

Note: To checkout this version of the repository type git checkout 23a.