Validation¶
Validating inputs at the filters/fields level is crucial to be in accordance of what the database expects and prevent unexpected errors.
SQLAlchemy filters plus provides multiple level of validations.
How it works¶
Field validation is ensuring that a specific field value is what we expect it to be. There are multiple field types that are predefined and can be used to validate the desired fields.
Let’s see an example of how we can apply that into our example:
from sqlalchemy_filters import Filter, DateField
class MyFilter(Filter):
birth_date = DateField() # can take a format parameter, default is "%Y-%m-%d"
class Meta:
model = User
session = my_session
The above defines a filter with a single DateField
field. This will ensure
that the passed value is a datetime value or can be parsed as a datetime.
Otherwise a FilterValidationError
exception will be thrown.
>>> from sqlalchemy_filters.exceptions import FilterValidationError
>>> try:
>>> MyFilter(data={"birth_date": "abc"}).apply()
>>> except FilterValidationError as exc:
>>> print(exc.json())
[
{"birth_date": "time data 'abc' does not match format '%Y-%m-%d'"}
]
This exception encapsulates all the field errors that were encountered, it also provides a
json()
method to make it human readable which gives
the possibility of returning it as a response in a REST API.
It’s also a wrapper around the FieldValidationError
exception, you can get the full list of wrapped exceptions
by accessing to fields_errors
attribute
>>> exc.field_errors
Custom Schema Validation¶
SQLAlchemy filters plus support custom validation with Marhsmallow.
The Marshmallow schema will provide a validation for the whole Filter
class.
Let’s define our fist Marshmallow schema
from marshmallow import Schema, fields, validate
class FirstNameSchema(Schema):
first_name = fields.String(validate=validate.OneOf(["john", "james"]), required=True)
First define a Marshmallow schema, then we can inject it into the Filter class using 2 approaches:
The first one is using Meta.marshmallow_schema attribute:
from sqlalchemy_filters import Filter, StringField class MyFilter(Filter): class Meta: model = User fields = ["first_name"] session = my_session marshmallow_schema = FirstNameSchema >>> MyFilter(data={"first_name": "test"}).apply() marshmallow.exceptions.ValidationError: {'first_name': ['Must be one of: john, james.']}
Or pass it as an argument at the instantiation level of the filter class
>>> MyFilter(data={"first_name": "test"}, marshmallow_schema=FirstNameSchema).apply() marshmallow.exceptions.ValidationError: {'first_name': ['Must be one of: john, james.']}
Define custom field and validation¶
Field validation is performed by the validate
method. The Filter class
calls the validate method for each defined field.
To create a custom field validation we can inherit from the Field
class or any other class that inherits
from the Field class (example: StringField, DateField…) and redefine the validate method,
the return value will be used to filter the column with, or an FieldValidationError
exception can be raised
Example:
from sqlalchemy_filters.fields import StringField from sqlalchemy_filters.exceptions import FieldValidationError class EmailField(StringField): def validate(self, value): value = super().validate(value) if "@mydomain.com" not in value: raise FieldValidationError("Only emails from mydomain.com are allowed.") return value