Skip to main content

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:

  1. Django Rest Framework works straight out of the box. Simply add @qval() to your views or use validate() inside.

  2. For Django without DRF you may need to add exception handler to settings.MIDDLEWARE. Qval attempts to do it automatically if DJANO_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.

  3. 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
    
  4. Similarly to Flask, with Falcon you will need to setup error handlers:

    import falcon
    from qval.framework_integration import setup_falcon_error_handlers
    ...
    app = falcon.API()
    setup_falcon_error_handlers(app)
    

    Full Falcon example can be found here: examples/falcon-example.py.

    Use the following command to run the app:

    $ PYTHONPATH=. python examples/falcon-example.py
    

TODO:

  1. Write docs
  2. Add better error messages

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qval-0.1.6.tar.gz (17.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qval-0.1.6-py2.py3-none-any.whl (34.0 kB view details)

Uploaded Python 2Python 3

File details

Details for the file qval-0.1.6.tar.gz.

File metadata

  • Download URL: qval-0.1.6.tar.gz
  • Upload date:
  • Size: 17.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.20.1

File hashes

Hashes for qval-0.1.6.tar.gz
Algorithm Hash digest
SHA256 66b3ca64af978fffbd079328f4167b9e26937ee645a61760fc92e0845ef44e0f
MD5 57e04b3e7b4a7053d346d1c1120ec6a6
BLAKE2b-256 d3116b3499370c27479095c89fb9ea838d6edfcb07e7040b23ab7200d951e2f7

See more details on using hashes here.

File details

Details for the file qval-0.1.6-py2.py3-none-any.whl.

File metadata

  • Download URL: qval-0.1.6-py2.py3-none-any.whl
  • Upload date:
  • Size: 34.0 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.20.1

File hashes

Hashes for qval-0.1.6-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 9fe70b543ac5fa5f14ab0aaa32b00666c3f5b4adfdff50594f804bd4c214b6f4
MD5 fc01454a851aedc2ea1abc39f2d66cef
BLAKE2b-256 5ef5f0c9f0a5e66328a1aa710219e326897ec7acb00a6fd2c0b185d3efecc0d7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page