Fields, Translation, and Validation¶
This topic describes how to translate and validate parameters from a request.
Translation is the process of converting the incoming parameter (typically
a string) into the desired type
. For example, there may be a
request such as the following: http://mydomain.com/resource?count=20
.
The count parameter would be a string, however we would want to use
it as an integer. Therefore, we would cast/translate it to an integer.
This can encapsulate more than just casting (e.g. turning a JSON string
into a dict
.
Validation takes the translated parameter and ensures that it fulfills
a certain set of expectations. In the previous example, we may wish
to validate that the count
query argument is always greater than 0.
Simple Example¶
This example demonstrates how to perform simple translation and validation on an count parameter
from ripozo import translate, fields, apimethod, ResourceBase, RequestContainer
class MyResource(ResourceBase):
@apimethod(methods=['GET'])
@translate(fields=[fields.IntegerField('count', minimum=1)], validate=True)
def hello(cls, request):
count = request.get('count')
return cls(properties=dict(count=count))
The translate
decorator translates the inputs using a series of fields. If
we pass validate=True
to the translate
decorator, it will perform validation
as well. The list of fields passed to the fields
parameter must all be instances
of BaseField
. BaseField
classes perform
the actual translation and validation for a given field.
>>> req = RequestContainer(query_args=dict(count='3'))
>>> res = MyResource.hello(req)
>>> res.properties['count'] == 3
True
>>> req = RequestContainer(query_args=dict(count='0'))
>>> res = MyResource.hello(req)
Traceback (most recent call last):
...
ValidationException: The field "count" is required and cannot be None
>>> req = RequestContainer(query_args=dict(count='not an integer'))
>>> res = MyResource.hello(req)
Traceback (most recent call last):
...
TranslationException: Not a valid integer type: not an integer
We can see that the string is appropriately cast in the first attempt and passes validation.
The second example raises a ValidationException
because it was id<1 which
we specified as the minimum. Finally, the last example raises a TranslationException
because the id
parameter was not a valid integer.
Specifying the location¶
By default, the translate
decorator will look in the url parameters, query arguments
and body arguments when finding the parameter to check. For example,
the following still works
>>> req = RequestContainer(body_args=dict(count='3'))
>>> res = MyResource.hello(req)
>>> res.properties['count'] == 3
True
However, sometimes we may require that a parameter is only allowed in a specific location. With ripozo, this is very simple.
from ripozo.resources.constants.input_categories import QUERY_ARGS
# we'll declare the fields here for cleanliness
# Note the arg_type parameter which specifies to
# only look in the query args for this field
hello_fields = [fields.IntegerField('count', required=True, minimum=1, arg_type=QUERY_ARGS)]
class MyResource(ResourceBase):
@apimethod(methods=['GET'])
@translate(fields=hello_fields, validate=True)
def hello(cls, request):
count = request.get('count')
return cls(properties=dict(count=count))
The previous example, will no longer work since the field is only allowed to be from the query arguments.
>>> req = RequestContainer(body_args=dict(count='3'))
>>> res = MyResource.hello(req)
Traceback (most recent call last):
...
ValidationException: The field "count" is required and cannot be None
>>> req = RequestContainer(query_args=dict(count='3'))
>>> res = MyResource.hello(req)
>>> res.properties['count'] == 3
True
With this method, we could even translate and validate two fields with the same name as long as they are in different locations.
Creating special fields¶
While there are plenty of fields available in Field types, sometimes, you need something more specific. In this example we’ll show you how to create an email validation field.
To create a new field, you simply need to inherit from ripozo.resources.fields.base.BaseField
and
override the necessary methods, in particular the _translate
and _validate
methods.
from ripozo import fields
from ripozo.exceptions import ValidationException
class EmailField(fields.BaseField):
def _validate(self, obj, **kwargs):
# perform the standard validation such as whether it is required.
obj = super(EmailField, self)._validate(obj, **kwargs)
if obj is None: # In case it wasn't a required field.
return obj
if '@' not in obj:
raise ValidationException('"{0}" is not a valid email address'.format(obj))
return obj
We could then test this by running the following
>>> field = EmailField('email_address', required=True)
>>> field.translate('some@email.com', validate=True)
'some@email.com'
>>> field.translate('not an email', validate=True)
Traceback (most recent call last):
...
ValidationException: "not an email" is not a valid email address
We can then use this new field like the IntegerField
we used previously.
API Documentation¶
Translation and Validation Decorators¶
-
class
ripozo.decorators.
translate
(fields=None, skip_required=False, validate=False, manager_field_validators=False)[source]¶ Decorator for validating the inputs to an apimethod and describing what is allowed for that apimethod to an adapter if necessary.
Example usage:
from ripozo import translate, fields, apimethod, ResourceBase, RequestContainer class MyResource(ResourceBase): @apimethod(methods=['GET']) @translate(fields=[fields.IntegerField('id', required=True)], validate=True) def hello(cls, request): id_ = request.query_args['id'] print(id_)
>>> req = RequestContainer(query_args=dict(id=3)) >>> res = MyResource.hello(req) 3 >>> req = RequestContainer() >>> res = MyResource.hello(req) Traceback (most recent call last): ... ValidationException: The field "id" is required and cannot be None >>> req = RequestContainer(query_args=dict(id='not an integer')) >>> res = MyResource.hello(req) Traceback (most recent call last): ... TranslationException: Not a valid integer type: not an integer
-
__call__
(func)[source]¶ Wraps the function with translation and validation. This allows the inputs to be cast and validated as necessary. Additionally, it provides the adapter with information about what is necessary to successfully make a request to the wrapped apimethod.
Parameters: func (method) – The function to decorate Returns: The wrapped function Return type: function
-
__init__
(fields=None, skip_required=False, validate=False, manager_field_validators=False)[source]¶ Initializes the decorator with the necessary fields. the fields should be instances of FieldBase and should give descriptions of the parameter and how to input them (i.e. query or body parameter). To perform validation of the fields as well, set
validate=True
Parameters: - fields (list) – A list of
FieldBase
instances (or subclasses of FieldBase). - skip_required (bool) – If this flag is set to True, then required fields will be considered optional. This is useful for an update when using the manager_field_validators as this allows the user to skip over required fields like the primary keys which should not be required in the updated arguments.
- validate (bool) – Indicates whether the validations should
be run. If it is
False
, it will only translate the fields and no validation will occur. - manager_field_validators (bool) – (Deprecated: will be removed in v2) A flag indicating that the fields from the Resource’s manager should be used.
- fields (list) – A list of
-
__weakref__
¶ list of weak references to the object (if defined)
-
-
class
ripozo.decorators.
manager_translate
(fields=None, skip_required=False, validate=False, fields_attr=u'fields')[source]¶ A special case translation and validation for using managers. Performs the same actions as ripozo.decorators.translate but it inspects the manager to get the resources necessary.
Additionally, you can tell it what fields to get from the manager via the fields_attr. This will look up the fields on the manager to return.
-
__call__
(func)[source]¶ Wraps the function with translation and validation. This allows the inputs to be cast and validated as necessary. Additionally, it provides the adapter with information about what is necessary to successfully make a request to the wrapped apimethod.
Parameters: f (method) – Returns: The wrapped function Return type: function
-
__init__
(fields=None, skip_required=False, validate=False, fields_attr=u'fields')[source]¶ A special case translation that inspects the manager to get the relevant fields. This is purely for ease of use and may not be maintained
Parameters: - fields (list[ripozo.resources.fields.base.BaseField]) – A list of fields to translate
- skip_required (bool) – If true, it will not require any of the fields. Only relevant when validate is True
- validate (bool) – A flag that indicates whether validation should occur.
- fields_attr (str|unicode) – The name of the attribute to access on the manager to get the fields that are necessary. e.g. ‘create_fields’, ‘list_fields’ or whatever you want. The attribute should be a list of strings
-
__weakref__
¶ list of weak references to the object (if defined)
-
fields
(manager)[source]¶ Gets the fields from the manager
Parameters: manager (ripozo.manager_base.BaseManager) –
-
Field types¶
Contains common field types that may be used.
-
class
ripozo.resources.fields.common.
BooleanField
(name, required=False, error_message=None, arg_type=None)[source]¶ A field used for translating and validating a boolean input It can take either a boolean or a string. If it’s a string it checks if it matches ‘false’ or ‘true’ (case insensitive).
-
class
ripozo.resources.fields.common.
DateTimeField
(name, valid_formats=None, minimum=None, maximum=None, **kwargs)[source]¶ A field for validating and translating a datetime input. By default it accepts the following formats:
%Y-%m-%dT%H:%M:%S.%fZ
If you need other formats simply pass a list of valid formats into the valid_formats parameter on initialization
-
field_type
¶ alias of
datetime
-
-
class
ripozo.resources.fields.common.
DictField
(name, field_list=None, minimum=None, maximum=None, **kwargs)[source]¶ A field for a dictionary of objects. Each named sub-field can be mapped to individual fields (Or even nested DictField’s).
-
__init__
(name, field_list=None, minimum=None, maximum=None, **kwargs)[source]¶ Calls super and sets the field_dict on the object.
Parameters:
-
translate
(obj, **kwargs)[source]¶ Translates and Validates the dictionary field and each of it’s contained fields. It will iterate over this field_dict and translate and validate each of the individual key, value pairs.
Parameters: obj (dict) – The object to translate and validate (if validate=True
)Returns: The translated (and possibly validated) object. Return type: dict
-
-
class
ripozo.resources.fields.common.
FloatField
(name, regex=None, minimum=None, maximum=None, **kwargs)[source]¶ A field used for translating and validating a float input. Pretty much the same as the IntegerField except that it will be cast as an IntegerField.
-
class
ripozo.resources.fields.common.
IntegerField
(name, regex=None, minimum=None, maximum=None, **kwargs)[source]¶ A field used for translating and validating an integer input. While translating it will attempt to cast the object provided as an integer.
-
class
ripozo.resources.fields.common.
ListField
(name, indv_field=None, minimum=None, maximum=None, **kwargs)[source]¶ A field for a list of objects. A field for the individual results can also be provided. This would be run against every individual item in the list that is provided.
-
class
ripozo.resources.fields.common.
StringField
(name, regex=None, minimum=None, maximum=None, **kwargs)[source]¶ Used for casting and validating string fields.
-
__init__
(name, regex=None, minimum=None, maximum=None, **kwargs)[source]¶ A field class for validating string inputs.
Parameters:
-
field_type
¶ alias of
unicode
-
Contains the base field that every other field should inherits from..
-
class
ripozo.resources.fields.base.
BaseField
(name, required=False, maximum=None, minimum=None, arg_type=None, error_message=None)[source]¶ The BaseField class is simply an abstract base class that defines the necessary methods for casting and validating a field.
-
__weakref__
¶ list of weak references to the object (if defined)
-
translate
(obj, skip_required=False, validate=False)[source]¶ A shortcut method to _translate and _validate the object that is being passed in. It returns this object or raises a ValueError.
Parameters: obj (object) – Returns: The translated and validated object Return type: object Raises: ripozo.exceptions.ValidationsException Raises: ripozo.exceptions.TranslationException
-
-
ripozo.resources.fields.base.
translate_fields
(request, fields=None, skip_required=False, validate=False)[source]¶ - Performs the specified action on the field. The action can be a string of
- either _translate, _validate, or translate.
Parameters: - request (RequestContainer) – The request that you are attempting to translate from.
- fields (list) – The list of BaseField instances that are supposed to be validated. Only items in this list will be translated and validated
- skip_required (bool) – A flag that indicates the required fields are not required. This is helpful for updates where fields are not usually required.
- validate (bool) – A flag that indicates whether the field validations should be run. If not, it will just translate the fields.
Returns: Returns the translated url_params, query_args and body_args
Return type: Raises: RestException
Raises: ValidationException
Raises: TranslationException