Form validation grows rapidly when we add more fields. Therefore, we need a different way to validate our forms than to put the validation logic into the view functions.
This post is part of my journey to learn Python. You can find the other parts of this series here.
A ViewModel for our form
ViewModels are little helper objects that contain all the data we need for a page. They help us to hide the complexity of our data structures by providing just the required data we need on our web page (like a string with the address for an order and not the whole customer and address objects we need in the background).
We can create a ViewModel for our form that not only contains the form data but also has the logic to validate it. By moving the logic for the validation into the ViewModel, we no longer need it in the view function. What sounds like a zero-sum game in theory is surprisingly helpful in practice.
Our starting place
The validation for a form with just three values ended up with this amount of code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@app.route('/contact', methods=['POST']) def contact_post(): name = request.form['name'] email = request.form['email'] message = request.form['message'] error = None if not name or not name.strip(): error = 'name is missing' if not email or not email.strip() or '@' not in email: error = 'email is missing' if not message or not message.strip(): error = 'message is missing' if error: return render_template('contact.html', error = error, name = name, email = email, message = message) print(f"{name} [{email}]: {message}") resp = flask.redirect('/thanks') return resp |
Creating the ViewModel
Since we need a ViewModel for the contact form, I create a class ContactViewModel
inside the folder viewmodels
. I could name it differently, but this way I can find them without much thinking.
We can copy most of the code we need from the view function. There are a few notable changes and additions:
- In the constructor I create properties for all form fields. This way I can use them in the other methods.
- The
__str__(self)
gives me a more useful representation of my ViewModel when I use print(). - With the
to_dict(self)
method I can turn my ViewModel into a dictionary (for the case that a validation error happens and I need to send the data back to the user).
The code for my ContactViewModel class inside the file contact_viewmodel.py
looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import flask class ContactViewModel(): def __init__(self): """ Create the ViewModel with the form data from the request """ request = flask.request self.name = request.form['name'] self.email = request.form['email'] self.message = request.form['message'] self.error = None def validate(self): """ validate the form """ if not self.name or not self.name.strip(): self.error = 'name is missing' if not self.email or not self.email.strip() or '@' not in self.email: self.error = 'email is missing' if not self.message or not self.message.strip(): self.error = 'message is missing' def __str__(self): """ create a readable output for the data """ return f"{self.name} [{self.email}]: {self.message}" def to_dict(self): """ turn this object into a dictionary """ return self.__dict__ |
The lean view function
After moving the whole validation to the ContactViewModel
class, our view function only needs to instantiate our class and call validate()
on it. The same checks ensure that the data is correct, but those checks are hidden in the validate()
method and no longer cluttering the view function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from viewmodels.contact_viewmodel import ContactViewModel @app.route('/contact', methods=['POST']) def contact_post(): vm = ContactViewModel() vm.validate() if vm.error: return render_template('contact.html', **vm.to_dict()) print(vm) resp = flask.redirect('/thanks') return resp |
Do not forget to import your ContactViewModel
, otherwise you will get this error:
NameError: name ‘ContactViewModel’ is not defined
Next
As my Flask application grows so does my hello.py
file with the view functions. It is now time to look for ways to structure a Flask application in a way that allows it to grow further.
1 thought on “Python Friday #40: Form Validation for Your Flask App With ViewModels”