Logo

dev-resources.site

for different kinds of informations.

Validatorian

Published at
11/14/2024
Categories
webdev
sql
python
flask
Author
gutmaster
Categories
4 categories in total
webdev
open
sql
open
python
open
flask
open
Author
9 person written this
gutmaster
open
Validatorian

Validations are a method of ensuring that our databases only receive the type of information that is appropriate for each attribute. After all, we wouldn't want a surprise type of data to find it's way into our code and cause unexpected behavior. Fortunately, SQLAlchemy has a package that makes validations quick and easy!

Let's look at some simple examples. Imagine we have a simple model, Sandwich. Here we have already initialized our database and are importing it from a configuration file.

from config import db

class Sandwich(db.Model):
    __tablename__ = 'sandwiches'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    description = db.Column(db.String)
    price = db.Column(db.Float)
Enter fullscreen mode Exit fullscreen mode

If we want to add validations to any of these attributes, we'll need to import the validations package first.

from sqlalchemy.orm import validates

And then, write our function with the '@validates' decorator inside the model.

    @validates('name')
    def validate_name(self, key, value):
        if not value:
            raise ValueError('Name cannot be empty.')
        if type(value) != str:
            raise ValueError('Name must be a string.')
        return value
Enter fullscreen mode Exit fullscreen mode

So what's going on here? Let's break it down. The @validates is a decorator that lets our ORM know to pass any values received with the key of 'name' through our validation function before adding them to the database. The value we return is what is finally given to our database. The "key" argument is the key that is being evaluated, in this case 'name', the value is the value of that key, so the actual name(hopefully in text) that we are trying to add. So here we are checking to make sure that the name attribute passed in is not empty, and that is in in fact a string. If not, we raise an error.

We can also run multiple attributes through the same decorator by adding them to it's arguments.

    @validates('name', 'description')
    def validate_text(self, key, value):
        if not value:
            raise ValueError(f'{key} cannot be empty.')
        if type(value) != str:
            raise ValueError(f'{key} must be a string.')
        return value
Enter fullscreen mode Exit fullscreen mode

This function validates our name and description attributes, but we won't usually have the same validations to perform on different attributes. Depending on how different and how many validations we have we can do it a couple of different ways. We can run a separate validator for our other attributes, let's add a length validation for our description and price validations too:

    @validates('name')
    def validate_name(self, key, value):
        if not value:
            raise ValueError('Name cannot be empty.')
        if type(value) != str:
            raise ValueError('Name must be a string.')
        return value

    @validates('description')
        def validate_description(self, key, value):
        if not value:
            raise ValueError('Description cannot be empty.')
        if type(value) != str:
            raise ValueError('Description must be a string.')
        if not 10 <= len(value) <= 200:
                raise ValueError('Description must be between 10 and 200 characters.')
        return value

    @validates('price')
    def validate_price(self, key, value):
        if type(value) != float:
            raise ValueError('Price must be a float.')
        if not 1 <= value <= 15:
            raise ValueError('Price must be between 1 and 15')
Enter fullscreen mode Exit fullscreen mode

Or, we can keep the same validator for both and use the key argument passed in to adjust which validations are run for each attribute.

    @validates('name', 'description', 'price')
    def validate(self, key, value):
        if key != 'price:
            if not value:
                raise ValueError(f'{key} cannot be empty.')
            if type(value) != str:
                raise ValueError(f'{key} must be string.')
            if key == 'description':
                if not 10 <= len(value) <= 200:
                    raise ValueError('Description must be between 10 and 200 characters.')
        else:
            if type(value) != float:
                raise ValueError(f'{key} must be a float.')
            if not 1 <= value <= 15:
                raise ValueError('Price must be between 1 and 15')
        return value
Enter fullscreen mode Exit fullscreen mode

Hmm, this is a little messy, let's refactor into 2 separate validators.

    @validates('name', 'description')
    def validate_text(self, key, value):
       if not value:
            raise ValueError(f'{key} cannot be empty.')
        if type(value) != str:
            raise ValueError(f'{key} must be string.')
        if key == 'description':
            if not 10 <= len(value) <= 200:
                raise ValueError('Description must be between 10 and 200 characters.')

    @validates('price')
    def validate_price(self, key, value):
        if type(value) != float:
            raise ValueError('Price must be a float.')
        if not 1 <= value <= 15:
            raise ValueError('Price must be between 1 and 15')
Enter fullscreen mode Exit fullscreen mode

That's better! Here's our completed model:

from sqlalchemy.orm import validates
from config import db

class Sandwich(db.Model):
    __tablename__ = 'sandwiches'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    description = db.Column(db.String)
    price = db.Column(db.Float)

    @validates('name', 'description')
    def validate_text(self, key, value):
        if not value:
            raise ValueError(f'{key} cannot be empty.')
        if type(value) != str:
            raise ValueError(f'{key} must be string.')
        if key == 'description':
            if not 10 <= len(value) <= 200:
                raise ValueError('Description must be between 10 and 200 characters.')

    @validates('price')
    def validate_price(self, key, value):
        if type(value) != float:
            raise ValueError('Price must be a float.')
        if not 1 <= value <= 15:
            raise ValueError('Price must be between 1 and 15')
Enter fullscreen mode Exit fullscreen mode

That's it! Validations are one easy tool to make sure your databases stay right and proper.

flask Article's
30 articles in total
Favicon
Deploy your Flask API on GCP Cloud Run πŸš€
Favicon
RESTful GET and POST Requests: A Beginners Guide
Favicon
Flask Routes vs Flask-RESTful Routes
Favicon
Bringing Together Containers & SQL
Favicon
Creating a Local Environment to Operate GCS Emulator from Flask
Favicon
Optimising Flask Dockerfiles: Best Practices for DevOps and Developers
Favicon
A beginners guide to Constraints and Validations in Flask, SQLAlchemy
Favicon
Deploying Flask-based Microservices on AWS with ECS Service Connect
Favicon
FastAPI + Uvicorn = Blazing Speed: The Tech Behind the Hype
Favicon
CRUD With Flask And MySql #2 Prepare
Favicon
CRUD With Flask And MySql #1 Introduction
Favicon
Building an Anemia Detection System Using Machine Learning πŸš‘
Favicon
Como usar WebSockets em Flask (How to use WebSockets in Flask)
Favicon
Setup Celery Worker with Supervisord on elastic beanstalk via .ebextensions
Favicon
How to create a simple Flask application
Favicon
Flask
Favicon
Building and Testing the Gemini API with CI/CD Pipeline
Favicon
Crossing the Line before the Finish Line. Also the line before that.
Favicon
Mastering Python Async IO with FastAPI
Favicon
Webinar Sobre Python e InteligΓͺncia Artificial Gratuito da Ebac
Favicon
Is Flask Dead? Is FastAPI the Future?
Favicon
422 Error with @jwt_required() in Flask App Deployed on VPS with Nginx
Favicon
WSGI vs ASGI: The Crucial Decision Shaping Your Web App’s Future in 2025
Favicon
Building a Real-Time Flask and Next.js Application with Redis, Socket.IO, and Docker Compose
Favicon
Carla Simulator 2 : Welcome to the Ride πŸš—πŸοΈ
Favicon
Python: A Comprehensive Overview in One Article
Favicon
Understanding Authentication: Session-Based vs. Token-Based (and Beyond!)
Favicon
Building RESTful APIs with Flask
Favicon
Validatorian
Favicon
LumaFlow

Featured ones: