Flask-Sieve¶
A requests validator for Flask inspired by Laravel.This package provides an approach to validating incoming requests using powerful and composable rules.
Quickstart¶
To learn about these powerful validation features, let’s look at a complete example of validating a form and displaying the error messages back to the user.
Auto-validation of Requests¶
Suppose you had a simple application with an endpoint to register a user.
flask-app/
__init__.py
app.py
app_requests.py
We are going to create validations for this endpoint.
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/', methods=('POST',))
def register():
return jsonify({'message': 'Registered!'}), 200
app.run()
To validate incoming requests to this endpoint, we create a class with validation rules of registering a user as follows:
# app_requests.py
from flask_sieve import FormRequest
class RegisterRequest(FormRequest):
def rules(self):
return {
'email': ['required', 'email'],
'username': ['required', 'string', 'min:6'],
'password': ['required', 'min:6', 'confirmed']
}
Now, using this class, we can guard our endpoint using a validate
decorator.
# app.py
from flask import Flask
from flask_sieve import Sieve, validate
from .app_requests import RegisterRequest
app = Flask(__name__)
Sieve(app)
@app.route('/', methods=('POST',))
@validate(RegisterRequest)
def register():
return jsonify({'message': 'Registered!'}), 200
app.run()
Note the initialization of Sieve
with the application instance. This is required
for setting up the necessary mechanism to autorespond with the error messages.
Manual Validation of Requests¶
Sometimes you might not wish to rely on the default auto-response made by Flask-Sieve. In this case,
you can create an instance of Validator
and set the rules yourself.
Using the same application shown above, this is how you would go about it:
# app.py
from flask import Flask, jsonify, request
from flask_sieve import Sieve, Validator
app = Flask(__name__)
Sieve(app)
@app.route('/', methods=('POST',))
def register():
rules = {
'email': ['required', 'email'],
'avatar': ['image', 'dimensions:200x200'],
'username': ['required', 'string', 'min:6'],
}
messages = {
'email.required': 'Yikes! The email is required',
'avatar.dimensions': 'Please provide an avatar with the right dimensions'
}
validator = Validator(rules=rules, messages=messages, request=request)
if validator.passes():
return jsonify({'message': 'Registered!'}), 200
return jsonify(validator.messages()), 400
app.run()
This would allow you to make the correct response in cases where you would not want to rely on the response format provided by Flask-Sieve.
Digging Deeper¶
Flask-Sieve supports various approaches to validating requests. Here we will make an in-depth tour of the functionalities offered.
Form vs JSON Requests¶
To address the differences in requests with form requests (with Content-Type: 'multipart/form-data'
)
and JSON requests (with Content-Type: 'application/json'
) Flask-Sieve supports two kinds of auto-validating
requests:
Form Requests¶
To validate form requests, you have to inherit from FormRequest
on the validation request. Example:
from flask_sieve import FormRequest
class PostRequest(FormRequest):
def rules(self):
return {
'image': ['file'],
'username': ['required', 'string', 'min:6'],
}
JSON Requests¶
To validate this format you will have to inherit from JsonRequest
.
Before validating the request, this checks that the request is indeed a JSON request (with Content-Type: 'application/json'
).
from flask_sieve import JsonRequest
class PostRequest(JsonRequest):
def rules(self):
return {
'email': ['required', 'email'],
}
Error Messages Format¶
In case validation fails to pass, the following is the default format of the generated response:
{
success: False,
message: 'Validation error',
errors: {
'email': [
'The email is required.',
'The email must be a valid email address.'
],
'password': [
'The password confirmation does not match.'
]
}
}
This format can be configured using the following flask configurations:
SIEVE_RESPONSE_MESSAGE
- Set this to modify your default error message e.g “Invalid Input”.SIEVE_INCLUDE_SUCCESS_KEY
- Set this to False to remove the success key from the response.SIEVE_RESPONSE_WRAPPER
- Set this to wrap your response e.g.data
.
Response Status Code¶
By default, all validation error messages will have a HTTP error status code 400. This can be configured by setting the flask config SIEVE_INVALID_STATUS_CODE
.
app.config['SIEVE_INVALID_STATUS_CODE'] = 422
Stopping on First Validation Failure¶
Sometimes you may wish to stop running validation rules on an attribute after the first validation failure. To do so, assign the bail
rule to the attribute:
# app_requests.py
# ... omitted for brevity ...
def rules(self):
return {
'body': ['required'],
'title': ['bail', 'string', 'required', 'max:255'],
}
In this example, if the string
rule on the title
attribute fails, the max
rule will not be checked. Rules will be validated in the order they are assigned.
A Note on Nested Attributes¶
If your HTTP request contains “nested” parameters, you may specify them in your validation rules using “dot” syntax:
# app_requests.py
# ... omitted for brevity ...
def rules(self):
return {
'author.name': ['required'],
'author.description': ['required'],
}
Customizing the Error Messages¶
You may customize the error messages used by the form request by overriding the messages
method. This method should return an array of attribute / rule pairs and their corresponding error messages:
# app_requests.py
from flask_sieve import FormRequest
class RegisterRequest(FormRequest):
def messages(self):
return {
'email.required': 'The email is required',
'password.confirmed': 'Password must be at least 6 characters'
}
def rules(self):
return {
'email': ['required', 'email'],
'username': ['required', 'string', 'min:6'],
'password': ['required', 'min:6', 'confirmed',]
}
Adding Custom Rules¶
Besides the rules offered by default, you can extend the validator with your own custom rules. You can
do this either when defining the Form/JSON Request class or when you instantiate a Validator
.
Custom Rule Handler¶
A rule handler is a predicate (a method returning either True
or False
) that you can use to add
your validations to Flask-Sieve.
This method must satisfy the following conditions:
It must start with the
validate_
keyword.It will receive keyword the following keyword parameters:
value
- the value of the request field being validated.attribute
- the field name being validated.params
- a list of parameters passed to the rule. For instance, for the inbuilt rulebetween:min,max
, the list will be[min, max]
.nullable
- a boolean marking whether this field has been specified asnullable
or not.rules
- a list containing all the rules passed on the field.
Tip: In case your handler does not need all these parameters, you can simply ignore the ones you don’t need with **kwargs
.
For example:
def validate_odd(value, **kwargs):
return int(value) % 2
Custom Rules on Form/JSON Requests¶
To define a custom rule validator on a Form/JSON Rquest, you will have to provide it in a method named
custom_handlers
as follows:
from flask_sieve import FormRequest
def validate_odd(value, **kwargs):
return int(value) % 2
class RegisterRequest(FormRequest):
# ... omitted for brevity ...
def custom_handlers(self):
return [{
'handler': validate_odd, # the rule handler
'message': 'Number must be odd', # the message to display when this rule fails
'params_count': 0 # the number of parameters the rule expects
}]
Custom Rules on Validator
Instance¶
To add a custom rule handler to a Validator
instance, you have will have to use
register_rule_handler
method as shown below:
from flask import Flask, jsonify, request
from flask_sieve import Sieve, Validator
app = Flask(__name__)
Sieve(app)
def validate_odd(value, **kwargs):
return int(value) % 2
@app.route('/', methods=('POST',))
def register():
rules = {'avatar': ['image', 'dimensions:200x200']}
validator = Validator(rules=rules, request=request)
validator.register_rule_handler(
handler=validate_odd,
message='Must be odd',
params_count=0
)
if validator.passes():
return jsonify({'message': 'Registered!'}), 200
return jsonify(validator.messages()), 400
Available Validations¶
accepted¶
The field under validation must be yes, on, 1, or true. This is useful for validating “Terms of Service” acceptance.
active_url¶
The field under validation must be active and responds to a request from requests
Python package.
after:date¶
The field under validation must be a value after a given date. The dates will be passed into the parse
function from python-dateutil
Python
'start_date': ['required', 'date', 'after:2018-02-10']
after_or_equal:date¶
The field under validation must be a value after or equal to the given date.
alpha¶
The field under validation must be entirely alphabetic characters.
alpha_dash¶
The field under validation may have alpha-numeric characters, as well as dashes and underscores.
alpha_num¶
The field under validation must be entirely alpha-numeric characters.
array¶
The field under validation must be an array
string.
bail¶
Stop running validation rules after the first validation failure.
before:date¶
The field under validation must be a value preceding the given date. The dates will be passed into the Python python-dateutil
package.
before_or_equal:date¶
The field under validation must be a value preceding or equal to the given date. The dates will be passed into the parse
function from python-dateutil
Python package.
between:min,max¶
The field under validation must have a size between the given min and max. Strings, numerics, arrays, and files are evaluated in the same fashion as the size
rule.
boolean¶
The field under validation must be able to be cast as a boolean. Accepted input are true
, false
, 1
, 0
, "1"
, and "0"
.
confirmed¶
The field under validation must have a matching field of foo_confirmation
. For example, if the field under validation is password
, a matching password_confirmation
field must be present in the input.
date¶
The field under validation must be a valid, non-relative date according to the parse
function of python-dateutil
.
date_equals:date¶
The field under validation must be equal to the given date. The dates will be passed into the parse
function of python-dateutil.
different:field¶
The field under validation must have a different value than field.
digits:value¶
The field under validation must be numeric and must have an exact length of value.
digits_between:min,max¶
The field under validation must have a length between the given min and max.
dimensions¶
The file under validation must be an image meeting the dimension constraints specified as WidthxHeight
'avatar': ['dimensions:200x200']
distinct¶
When working with arrays, the field under validation must not have any duplicate values.
'foo': ['distinct']
email¶
The field under validation must be formatted as an e-mail address.
file¶
The field under validation must be a successfully uploaded file.
filled¶
The field under validation must not be empty when it is present.
gt:field¶
The field under validation must be greater than the given field. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the size
rule.
gte:field¶
The field under validation must be greater than or equal to the given field. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the size
rule.
image¶
The file under validation must be an image (jpeg, png, bmp, gif, tif, or svg)
in:foo,bar,…¶
The field under validation must be included in the given list of values.
in_array:anotherfield¶
The field under validation must exist in anotherfield’s values.
integer¶
The field under validation must be an integer.
ip¶
The field under validation must be an IP address.
ipv4¶
The field under validation must be an IPv4 address.
ipv6¶
The field under validation must be an IPv6 address.
json¶
The field under validation must be a valid JSON string.
lt:field¶
The field under validation must be less than the given field. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the size
rule.
lte:field¶
The field under validation must be less than or equal to the given field. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the size
rule.
max:value¶
The field under validation must be less than or equal to a maximum value. Strings, numerics, arrays, and files are evaluated in the same fashion as the size
rule.
mime_types:text/plain,…¶
The file under validation must match one of the given MIME types:
'video': ['mime_types:video/avi,video/mpeg,video/quicktime']
To determine the MIME type of the uploaded file, the file’s contents will be read and the framework will attempt to guess the MIME type, which may be different from the client provided MIME type.
min:value¶
The field under validation must have a minimum value. Strings, numerics, arrays, and files are evaluated in the same fashion as the size
rule.
not_in:foo,bar,…¶
The field under validation must not be included in the given list of values.
not_regex:pattern¶
The field under validation must not match the given regular expression.
nullable¶
The field under validation may be None
. This is particularly useful when validating primitive such as strings and integers that can contain None
values.
numeric¶
The field under validation must be numeric.
present¶
The field under validation must be present in the input data but can be empty.
regex:pattern¶
The field under validation must match the given regular expression.
required¶
The field under validation must be present in the input data and not empty. A field is considered “empty” if one of the following conditions are true:
The value is
None
.The value is an empty string.
The value is an empty array.
required_if:anotherfield,value,…¶
The field under validation must be present and not empty if the anotherfield field is equal to any value.
required_unless:anotherfield,value,…¶
The field under validation must be present and not empty unless the anotherfield field is equal to any value.
required_with:foo,bar,…¶
The field under validation must be present and not empty only if any of the other specified fields are present.
required_with_all:foo,bar,…¶
The field under validation must be present and not empty only if all of the other specified fields are present.
required_without:foo,bar,…¶
The field under validation must be present and not empty only when any of the other specified fields are not present.
required_without_all:foo,bar,…¶
The field under validation must be present and not empty only when all of the other specified fields are not present.
same:field¶
The given field must match the field under validation.
size:value¶
The field under validation must have a size matching the given value. For string data, value corresponds to the number of characters. For numeric data, value corresponds to a given integer value. For an array, size corresponds to the count
of the array. For files, size corresponds to the file size in kilobytes.
starts_with:foo,bar,…¶
The field under validation must start with one of the given values.
string¶
The field under validation must be a string. If you would like to allow the field to also be None
, you should assign the nullable
rule to the field.
timezone¶
The field under validation must be a valid timezone identifier according to the pytz
Python package.
url¶
The field under validation must be a valid URL.
uuid¶
The field under validation must be a valid RFC 4122 (version 1, 3, 4, or 5) universally unique identifier (UUID).
Contributions¶
Contributions and bugfixes are welcome!