Custom html form django
(Picture: https://www.pexels.com/photo/business-coffee-composition-computer-265667/)
Dealing with user input is a very common task in any Web application or Web site. The standard way to do it is through HTML forms, where the user input some data, submit it to the server, and then the server does something with it. Now, the chances are that you might have already heard that quote: “All input is evil!” I don’t know who said that first, but it was very well said. Truth is, every input in your application is a door, a potential attack vector. So you better secure all doors! To make your life easier, and to give you some peace of mind, Django offers a very rich, reliable and secure forms API. And you should definitely use it, no matter how simple your HTML form is.
Managing user input, form processing is a fairly complex task, because it involves interacting with many layers of your application. It have to access the database; clean, validate, transform, and guarantee the integrity of the data; sometimes it needs to interact with multiple models, communicate human readable error messages, and then finally it also have to translate all the Python code that represents your models into HTML inputs. In some cases, those HTML inputs may involve JavaScript and CSS code (a custom date picker, or an auto-complete field for example).
The thing is, Django does very well the server-side part. But it doesn’t mess much with the client-side part. The HTML forms automatically generated by Django is fully functional and can be used as it is. But it’s very crude, it’s just plain HTML, no CSS and no JavaScripts. It was done that way so you can have total control on how to present the forms so to match with your application’s Web design. On the server-side is a little bit different, as thing are more standardized, so most of the functionalities offered by the forms API works out-of-the-box. And for the special cases, it provide many ways to customize it.
In this tutorial I will show you how to work with the rendering part, using custom CSS and making your forms prettier.
Here is the table of contents of this article:
Working Example
Throughout the whole tutorial I will be using the following form definition to illustrate the examples:
If you have a look in the _html_output private method defined in the BaseForm , which is used by all the as_*() methods, you will see that it’s a fairly complex method with 76 lines of code and it does lots of things. It’s okay because this method is well tested and it’s part of the core of the forms API, the underlying mechanics that make things work. When working on your own form rendering logic you won’t need to write Python code to do the job. It’s much better to do it using the Django Templates engine, as you can achieve a more clean and easier to maintain code.
I’m mentioning the _html_output method here because we can use it to analyze what kind of code it’s generating, what it’s really doing, so we can mimic it using the template engine. It’s also a very good exercise to read the source code and get more comfortable with it. It’s a great source of information. Even though Django’s documentation is very detailed and extensive, there are always some hidden bits here and there. You also get the chance to see by examples how smart coders solved specific problems. After all, it’s an open source project with a mature development process that many have contributed, so the chances are you are reading an optimal code.
Anyway, here it is, in a nutshell, what the _html_output does:
- Aggregate the errors that are not attached to specific fields (non field errors) and errors from hidden fields;
- Place the non field errors and hidden field errors on top of the form;
- Iterate through all the form fields;
- Render the form fields one by one;
- Render the field name in a label tag;
- If there are field errors, render a HTML list with errors first;
- Render the HTML input for the field;
- If there is a help text, render it after the field.
Here is what the second state of the form looks like, triggering all the validation errors:
Now that we know what it’s doing, we can try to mimic the same behavior using the template engine. This way, we will have much more control over the rendering process:
Now that we know how to “expand” the < < form >> markup, let’s try to make it look prettier. Perhaps using the Bootstrap 4 library.
Accessing the Form Fields Individually
We don’t need a for loop to expose the form fields. But it’s a very convenient way to do it, specially if you don’t have any special requirements for the elements positioning.
Here is how we can refer to the form fields one by one:
Add it to the INSTALLED_APPS :
This part of the article will be more to-the-point, as I won’t explore the particularities of the Bootstrap 4 implementation. Their documentation is great and rich in examples. If you are not very familiar, you can jump to the Documentation / Components / Forms section for further information.
Let’s first focus on the presentation of the inputs, we will get to the errors part later. Here is how we can represent the same form using the Bootstrap 4 tags:
Much better. Now let’s see the validation and errors situation. I’m going to use an alert component for the non field errors, and for the fields I will just play with the right CSS classes that Bootstrap 4 provides.
Then now, our form definition could be as simple as:
method="post" novalidate> csrf_token %> include 'includes/bs4_form.html' with form=form %> type="submit" class="btn btn-primary">Submit
For example, using the code snippet above, we use it to process the UserCreationForm , which is a built-in form that lives inside the django.contrib.auth module. Below, the result:
Conclusions
This article become bigger than I anticipated. I first thought about writing just a quick tutorial about form rendering. Then I remembered that I already had a to-the-point tutorial explaining how to use the django-widget-tweaks. So, instead I decided to dive deep into the details and explore some of the mechanics of the forms API.
I will have a follow-up article focusing on complex forms, rendering all together checkboxes, select fields, date picker and also about developing your own custom widgets.
I hope you learned something new or enjoying reading this article. If you may have any questions or want to discuss further about the topic, please leave a comment below!
As usual, you can find the source code and all the examples on GitHub.