Constraints & Validations

RMAG news

All This Data

In my previous blog post, I wrote about objects and classes and how they can be used to represent entities, objects, and relationships in code. As part of that post I touched on object relational mapping, the concept of using classes to map relational databases. Using these structures to store, visualize, and manipulate data is extremely powerful, but doesn’t mean much either without data, or without the right kind of data. When we write applications, we design them to perform any number of actions (ranging from simple to complex) on the data that they’re given. If they are unable to perform those actions in the intended manner, or at all, errors and unexpected behavior can occur. Not only can this have a negative impact on how our application runs, it can also present security risks, especially in the case of user generated data. All of this begs the question, how do we make sure that the data our application receives is of the type(s) we originally intended? The answer lies in constraints and validations.

Ports of Entry

By using constraints and validations we can check what kind(s) of data are coming into our application, prevent unwanted data from being used, and return useful messages to the user to help them understand where they went wrong. There are multiple points at which one might validate the data in an application. The first is in the front end where a user is inputting data. The second is server side, in the the classes used to instantiate objects. The third is at the database level, when the data is being committed. The first two use validations, while the third uses constraints.

Client Side Validation

When dealing with user input, two of the major concerns are human error and security. Client side validation isn’t particularly good at addressing the latter of the two, but is fantastic for the former. It allows us to check that whatever the user inputs in a form is what our application is looking for, and if it’s not, giving immediate feedback and preventing that data from making it to other parts of the application. While it’s entirely possible to build validator functions of one’s own, there are libraries such as Formik that can help us streamline the process.

Server Side Validation

As mentioned before, client side validation isn’t the most useful from a security perspective. After all, someone who’s intentionally trying to feed unwanted data to an application may very well bypass the front end altogether, rendering form validation completely useless. We can implement validations in our ORMs to protect against such attacks, as well as ensure that the data coming through our ORM is what our application is designed to handle. Once again, while we can write our own validator functions, frameworks such as Flask provide validation features to streamline the process.

Database Constraints

A database constraint is functionally distinct from an ORM validation. While a validation is only checked when data is being added or updated through the ORM, constraints are checked any and every time changes or updates are made to the database. In the case of SQLalchemy there are certain constrains such as “nullable” and “unique” which are set with boolean values on columns, and others called “check constraints”. As their names suggest, nullable ensures that a value is required for that column, and unique makes sure there are no repeated values in a column. On the other hand, check constraints allow us to create our own conditions, either for a specific column or for the entire table, to be checked when data is added or changed. It’s worth noting that not all databases (such as mySQL) support check constraints. Additionally, there can be limitations such as with later versions of SQLalchemy where check constraints cannot be added to an existing table.

Conclusion

Validations and constraints are essential tools in order to keep our applications secure and running smoothly. Guarding against user error, bad actors, and data inconsistencies are all important parts of development. As such, there are many different tools and techniques at our disposal to streamline the process and make things easier on ourselves as developers both when writing and servicing our (and others’) code. Depending on an application’s intended use, these checks can be stacked on one another to create a system of redundancies to ensure that we’re getting the kind(s) of data we desire, and nothing else.