This module provides convenient API for verifying query parameters.
Project description
Qval | Query param validation library
Installation
$ pip install qval
Basic usage
You can use Qval as both a function and a decorator. Function validate()
accepts 3 positional arguments and 1 named:
# qval.py
def validate(
# Request instance. Must be a dictionary or support request interface.
request: Union[Request, Dict[str, str]],
# Dictionary of (param_name -> `Validator()` object).
validators: Dict[str, Validator] = None,
# Provide true if you want to access all parameters from the request through the context object.
box_all: bool = True,
# Factories that will be used to convert parameters to python objects (callable[str, any] -> object).
**factories,
) -> QueryParamValidator:
Imagine you have a RESTful calculator with an endpoint called /api/divide
. You can use validate()
to automatically convert parameters to python objects and then validate them:
from qval import validate
...
def division_view(request):
"""
GET /api/divide?
param a : int
param b : int, nonzero
param token : string, length = 12
Example: GET /api/divide?a=10&b=2&token=abcdefghijkl -> 200, {"answer": 5}
"""
# Parameter validation occurs in the context manager.
# If validation fails or user code throws an error, context manager
# will raise InvalidQueryParamException or APIException respectively.
# In Django Rest Framework, these exceptions will be processed and result
# in error codes (400 and 500) on the client side.
params = (
# `a` and `b` must be integers
# Note: in order to get a nice error message on the client side,
# you factory should raise either ValueError or TypeError
validate(request, a=int, b=int)
# `b` must be anything but zero
.nonzero("b")
# The `transform` callable will be applied to parameter before the check.
# In this case we'll get `token`'s length and check if it is equal to 12.
.eq("token", 12, transform=len)
)
with params as p:
return Response({"answer": p.a // p.b})
// GET /api/divide?a=10&b=2&token=abcdefghijkl
// Browser:
{
"answer": 5
}
Sending b = 0 to this endpoint will result in the following message on the client side:
// GET /api/divide?a=10&b=0&token=abcdefghijkl
{
"error": "Invalid `b` value: 0."
}
If you have many parameters and custom validators, it's better to use the @qval()
decorator:
# validators.py
from decimal import Decimal
from qval import Validator
...
purchase_factories = {"price": Decimal, "item_id": int, "token": None}
purchase_validators = {
"price": Validator(lambda x: x > 0),
"token": Validator(lambda x: len(x) == 12),
"item_id": Validator(lambda x: x >= 0),
}
# views.py
from qval import qval
from validators import *
...
# Any function or method wrapped with `qval()` must accept request as
# either first or second argument, and parameters as last.
@qval(purchase_factories, purchase_validators)
def purchase_view(request, params):
"""
GET /api/purchase?
param item_id : int, positive
param price : float, greater than zero
param token : string, len == 12
Example: GET /api/purchase?item_id=1&price=5.8&token=abcdefghijkl
"""
print(f"{params.item_id} costs {params.price}$.")
...
Framework-specific instructions:
-
Django Rest Framework works straight out of the box. Simply add
@qval()
to your views or usevalidate()
inside. -
For Django without DRF you may need to add exception handler to
settings.MIDDLEWARE
. Qval attempts to do it automatically ifDJANO_SETTINGS_MODULE
is set. Otherwise you'll see the following message:WARNING:root:Unable to add APIException middleware to the MIDDLEWARE list. Django does not support APIException handling without DRF integration. Define DJANGO_SETTINGS_MODULE or add 'qval.framework_integration.HandleAPIExceptionDjango' to the MIDDLEWARE list.
Take a look at plain Django example here.
-
If you are using Flask, you will need to setup exception handlers:
from flask import Flask from qval.framework_integration import setup_flask_error_handlers ... app = Flask(__name__) setup_flask_error_handlers(app)
Since
request
in Flask is a global object, you may want to curry@qval()
before usage:from flask import request from qval import qval_curry # Firstly, curry `qval()` qval = qval_curry(request) ... # Then use it as decorator. # Note: you view now must accept request as first argument @qval(...) def view(request, params): ...
Check out the full Flask example in
examples/flask-example.py
.You can run the example using the command below:
$ PYTHONPATH=. FLASK_APP=examples/flask-example.py flask run
TODO:
- Write docs
- Add better error messages
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.