Parameters

Parameters provide a way to describe and evaluate all request query params that can be used in your API resources.

New parameters are added to resources as class attributes:

from graceful.parameters import StringParam, IntParam
from graceful.resources.base import BaseResource

class SomeResource(BaseResource):
    filter_by_name = StringParam("Filter resource instances by their name")
    depth = IntParam("Set depth of search")

Class attribute names map directly to names expected in the query string. For example the valid query strings in scope of preceding definition could be:

  • filter_by_name=cats
  • filter_by_name=dogs&depth=2

All param classes accept this set of arguments:

  • details (str): verbose description of parameter. Should contain all information that may be important to your API user and will be used for describing resource on OPTIONS requests and .describe() call.

  • label (str): human readable label for this parameter (it will be used for describing resource on OPTIONS requests).

    Note that it is recomended to use parameter names that are self-explanatory intead of relying on param labels.

  • required (bool): if set to True then all GET, POST, PUT, PATCH and DELETE requests will return 400 Bad Request response if query param is not provided.

  • default (str): set default value for param if it is not provided in request as query parameter. This MUST be a raw string value that will be then parsed by .value() handler.

    If default is set and required is True it will raise ValueError as having required parameters with default value has no sense.

  • param (str): set to True if multiple occurences of this parameter can be included in query string, as a result values for this parameter will be always included as a list in params dict. Defaults to False.

    Note

    If many==False and client inlcudes multiple values for this parameter in query string then only one of those values will be returned, and it is undefined which one.

For list of all available parameter classes please refer to graceful.parameters module reference.

If you are using the bare falcon HTTP method handlers and sublcass directly from graceful.resources.base.BaseResource then you can access all deserialized query parameters as dictionary using require_params(req) method:

from graceful.parameters import StringParam, IntParam
from graceful.resources.base import BaseResource

class SomeResource(BaseResource):
    filter_by_name = StringParam("Filter resource instances by their name")
    depth = IntParam("Set depth of search")

    def on_get(self, req, resp):
        params = self.require_params(req)

The self.require_params(req) will try to retrieve all of described query parameters, validate them and populate with defaults if they were not found in the query string. This method will also take care of raising the falcon.errors.HTTPInvalidParam if:

  • parameter specified as required=True was not provided
  • parameter could not be parsed/validated (i.e. value() handler raised ValueError)

Note that you do not need to handle this exception manually. It will be later automatically transformed to 400 Bad Request by falcon if not catched by try .. except clause.

If you are using generic resource classes from graceful.resources.generic like ListAPI or RetrieveAPI the params retrieval step is done automatically and you do not need to care. Parameters dict will be provided in applicable retrieval/modification method handler (list(), update(), retrieve etc.) and these methods will be executed only if call to self.require_params(req) succeeded without raising any exceptions.

Custom parameters

Although graceful ships with some set of predefined parameter classes it is very likely that you need something that is not yet covered because:

  • it is not yet covered
  • is very specific to your application
  • it can be implemented in many ways and it is impossible to decide which is best without being too opinionated.

New parameter types can be created by subclassing BaseParam and and implementing .value(raw_value) method handler. ValueError raised in this handler will eventually result in 400 Bad Request response.

Two additional class-level attributes help making more verbose parameter description:

  • type - string containig name of primitive data type like: “int”, “string”, “float” etc. For most custom parameters this will be simply “string” and it is used only for describtions so make sure it is something truely generic or well described in your API documentation
  • spec - two-tuple containing link name, and link url to any external documentation that you may find helpful for developers.

Here is example of custom parameter that handles validation of alpha2 country codes using pycountry module:

import pycountry

class LanguageParam(BaseParam):
    """
    This param normalizes language code passed to is and checks if it is valid
    """

    type = 'ISO 639-2 alpha2 language code'
    spec = (
        'ISO 639-2 alpha2 code list',
        "http://www.loc.gov/standards/iso639-2/php/code_list.php",
    )

    def value(self, raw_value):
        try:
            # normalize code since we store then lowercase
            normalized = raw_value.lower()
            # first of all check if country so no query will be made if it is
            # invalid
            pycountry.languages.get(alpha2=normalized)

            return normalized

        except KeyError:
            raise ValueError(
                "'{code}' is not valid alpha2 language code"
                "".format(code=raw_value)
            )

Parameter validation

Custom parameters are great for defining new data types that can be passed through HTTP query string or handling very specific cases like country codes, mime types, or even database filters. Still it may be sometimes an overkill to define new parameter class to do something as simple as ensure min/max bounds for numeric value or define as set of allowed choices.

All of basic parameters available in graceful accept validators keyword argument that accepts a list of validation functions. These function will be always called upon parameter retrieval. This functionality allows you to quickly extend the semantic of your parameters without the need of subclassing.

A validator is any callable that accepts single positional argument that will be a value returned from call to the value() handler of parameter class. If validation funtion fails it is supposed to return graceful.errors.ValidationError that will be later translated to proper HTTP error response. Following is example of simple validation function which ensures that parameter string is palindrome:

from graceful.resources.base import BaseResource
from graceful.parameters import StrParam
from graceful.errors import ValidationError

def is_palindrome(value):
    if value != value[::-1]:
        raise ValidationError("{} is not a palindrome")


class FamousPhrases(Resource):
    palindrome_query = StrParam(
        "Palindrome text query", validators=[is_palindrome]
    )

Validators always work on deserialized values and this allows to easily reuse the same code across different types of parameters and also between fields (see: Field validation). Graceful takes advantage of this fact and already provides you with a small set of fully reusable validators that can be used to validate both your parameters and serialization fields. For more details see graceful.validators module reference.

Handling multiple occurences of parameters

The simplest way to allow user to specify multiple occurences of single parameter is to use many keyword argument. It is available for every base parameter class initialization and it is good practice to not override this argument in custom parameter classes using custom initialization.

If many is set to True for given parameter the resulting params dictionary available in main method handlers of generic resources or through self.require_params(req) method will contain list of values for given resource instead of single value.

For instance, if you are building some text search API and expect client to provide multiple search string in single query you can describe your basic API as follows:

from graceful.parameters import StringParam
from graceful.resources.base import BaseResource

class SearchResource(BaseResource):
    search = StringParam("text search string", many=True)

With such definition your client can provide list of multiple values for the search param using multiple instances of search=<value> in his query string e.g:

search=matt&search=damon&search=affleck

Important: if many is set to False the value stored under corresponding key will always represent the single parameter value. It is important to note that providing multiple values for same parameter in the query string by your API client is not considered an error even if parameter is described as many=False. In that case only one value will be included in parameters dictionary and it is not defined which one. When documenting your API you need to take special care when informing which parameter supports muliple value and which not. You should also make sure to inform API users of possibility of undefined behaviour when not following your instructions.

Order of values and ordered data

Remember that multiple values coming from parameter defined using many=True should be always considered independend from each other. This means that order of resulting parameter values is always undefined. If you need to handle parameters that represent specifically ordered list you probably need custom parameter class that that will provide you with required serialization. Such representation is generally independent from the many argument of such custom parameter class.

The reason for that design decision is because when order of data is important then usually the order by itself represents is a named quality or entity.

The best way to undestand this is by example. For instance let’s assume that we are building some simple API that allows to search through some inventory of clothes store. If we would like to allow clients to filter items by their colors it completely makes sense to use following definition of query parameter:

color = StringParam("One of main color items", many=True)

But if you are building some spatial search engine you might want to allow user to search for data in region defined as a polygon. Polygon can be simply represented by just an ordered list of points. But does it makes sense to define your polygon as point parameter with many=True? Probably not. In case where order of data is important you need some custom parameter class that will explicitly define how to handle such parameters. The naive implementation for polygon parameter could be as follows: The naive

from graceful.parameters import BaseParam

class PolygonParam(BaseParam):
    """ Represents polygon parameter in string form of "x1,y1;x2,y2;..."
    """
    type = 'polygon'


    def value(self, raw_value):
        return [
            [float(x) for x in point.split(',')]
            for point in raw_value.split(';')
        ]

Such approach your will eventually make your code and API:

  • Easier to understand - you will end up using parameter names that better explain what you and your API users are dealing with.
  • Easier to document - parameter class can be inspected for the purpose of auto documentation. Their basic attributes (type and spec) are already included in default OPTIONS handler.
  • Easier to extend - if you suddenly realize that you need to support multiple ordered sets of same type of data it is as simple as adding additional many=True to declaration of parameter that represents some data container

Custom containers

With the many=True option multiple values for the same parameter will be returned as list. But sometimes you may want to do additional processing when many option is enables. For instance you may want to concatenate all string searches to single string, make sure all values are unique or join some ORM query sets using logical operator.

Of course it is completely valid approach to make such operation in your HTTP method handler (in case of using BaseResource) or in your specific retrieval/update handler (in case of using generic resource classes). This is usually very simple:

from graceful.parameters import StringParam
from graceful.resources.generic import PaginatedListAPI


class CatList(PaginatedListAPI):
    """
    List of all cats in our API
    """
    breed = StringParam(
        "set this param to filter cats by breed"
        many=True
    )

    def list(self, params, meta, **kwargs):
        unique_breeds = set(param['breed']
        ...

Unfortunately, when you have a lot of different parameters that need similar handling (e.g. various ORM-specific filter objects) this can become tedious and lead to excessive code duplication. The easiest way overcome this problem is to use custom container handler for multiple parameter occurences. This can be done in your custom parameter class by overriding its default container attribute.

The container handler can be both type object or a new method. It must accept list of values as its single positional argument.

Following is an example StringParam re-implementation which additionally makes sure that multiple occurences of the same parameter are all unique. Uniqueness is simply achieved by using built-in set type as its container attribute:

from graceful.parameters import BaseParam

class UniqueStringParam(BaseParam):
    """Same as StringParam but on ``many=True`` returns set of values."""
    container = set

As already said, container handler can be a method too. This is very useful for handling more complex use cases. For instance solrq is a nice utility for creating Apache Solr search engine queries in Python. If your API somehow exposes Solr search it would be nice to make parameter class that converts query string params directly to solrq.Q objects. solrq allows also to easily join multiple query objects using binary AND and OR operators in similar fashion to Django’s queryset filters:

>>> Q(text='cat') | Q(text='dog')
<Q: text:cat OR text:dog>

It really makes sense to take advantage of such feature in your parameter class that wraps GET params in solrq.Q instances whenever many=True option is enabled. Following is example of custom parameter class that allows to collapse multiple values of search queries to single solrq.Q instance with predefined operator:

from graceful.params import StringParam

import operator
from functools import reduce

class FilterQueryParam(StringParam):
    """
    Param that represents Solr filter queries logically
    joined together depending on value of `op` argument
    """
    def __init__(
            self,
            details,
            solr_field,
            op=operator.and_,
            **kwargs
    ):
        if solr_field is None:
            raise ValueError(
                "`solr_field` argument of {} cannot be None"
                "".format(self.__class__.__name__)
            )

        self.solr_field = solr_field
        self.op = op

        super().__init__(details, **kwargs)

    def value(self, raw_value):
        return Q({self.solr_field: raw_value})

    def container(self, values):
        return reduce(self.op, values) if len(values) > 1 else values[0]

With such definition creating simple Solr-backed search API using graceful and without extensive object serialization becomes pretty simple:

import operator

from solrq import Value as V
from pysolr import Solr
from graceful.resources.generic import ListAPI
from graceful.serializers import BaseSerializer

solr = Solr()


class VerbatimSerializer():
    """ Represents object as it is assuming that we deal with simple dicts
    """
    def to_representation(self, obj):
        return obj


class Search(ListAPI):
    serializer = VerbatimSerializer()

    text = FilterQueryParam(
        "Basix text search argumment (many values => AND)",
        many=True,
        solr_field='text'
        default=V('*', safe=True)
    )

    category = StringParam(
        "set this param to filter cats by breed (many values => OR)"
        many=True,
        solr_field='category'
        default=V('*', safe=True),
        op=operator.or_,
    )

    def list(self, params, meta, **kwargs):
        return list(solr.search(params['text'] & params['category']))