'SQLAlchemy: What is the best way to validate a model before inserting or updating

I'm trying to validate a SQLAlchemy model before it is inserted or updated, e.g

class MyModel(db.Model):
    foo = db.Column(db.String(255))
    bar = db.Column(db.String(255))

I've tried a few approaches, but none seem to work. One possibility was to listen to before_insert and before_update events, e.g

@event.listens_for(MyModel, 'before_insert')
@event.listens_for(MyModel, 'before_update')
def validate_my_model(mapper, connection, model):
    if not is_valid(model):
        raise Exception("the model isn't valid")

This works okay, but in tests I get this error unless I roll back the session.

This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback()

I could call session.rollback() in the tests, but I that seems incorrect, since the test is just issuing PUT/POST requests and shouldn't really know anything about the session or any SQLAlchemy internals.

Another option was to listen to init events, instead of before_insert and before_update, but that doesn't handle the update case.

I also tried using the @validates, e.g

@validates('foo', 'bar')
def validate(self, key, val):
    ...

However, the validation relies on both properties, while the validate method is called individually for each property. This means that if I try to set both foo and bar on the model, it attempts to validate the intermediate state where one property has been set but the other hasn't.



Solution 1:[1]

try flask-marshmallow and marshmallow_sqlalchemy, it is based on marshmallow validation package. it allows you to validate based on your database model. create a schema file

import MyModel
import db
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema

class MyModelSchema(SQLAlchemyAutoSchema):
    class Meta:
        sqla_session = Session
        load_instance = True
        model = MyModel

adding model inside the Meta class will help to validate based on the model and load_instance will let us load the object inside the API as a model instance

from schemas import MyModelSchema

model_schema = MyModelSchema()

# API post route
def post(self):
    model_json = request.get_json()
    model = model_schema.load(model_json)

    db.session(model)
    db.session.commit() 

    return {"message": "model created"}, 201

last to user marshmallow inside flask app and to return a validation error message if the body sent not validated, inside application root file add

from flask_marshmallow import Marshmallow
from marshmallow import ValidationError

# marshmallow config
ma = Marshmallow(app)

# marshamallow global exception handler
@app.errorhandler(ValidationError)
def handle_marshmallow_validation(err):
    return jsonify(err.messages), 400

I hope you find this useful.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 alim91