Skip to main content

Cerberus validation support for Morepath

Project description

more.cerberus: validation and normalization support for Morepath

This package provides Morepath integration for the Cerberus data validation library:

Cerberus can automate user input validation and normalization in a HTTP API.

Schema

You can define a schema simply as a Python dict:

user_schema = {
  'name': {'type': 'string', 'minlength' : 3, 'required': True},
  'age': {'type': 'integer', 'min': 0, 'required': True}
}

Altenatively you can define the schema in yaml and load it with pyyaml:

user:
  name:
    type: string
    minlength: 3
    required: true
  age:
    type: integer
    min: 0
    required: true
import yaml

with open('schema.yml') as schema:
    schema = yaml.load(schema)

user_schema = schema['user']

Validate

The more.cerberus integration helps with validation of the request body as it is POSTed or PUT to a view. First we must create a loader for our schema:

from more.cerberus import loader

user_schema_load = loader(user_schema)

We can use this loader to handle a PUT or POST request for instance:

@App.json(model=User, request_method='POST', load=user_schema_load)
def user_post(self, request, json):
    # json is now a validated and normalized dict of whatever got
    # POST onto this view that you can use to update
    # self

Update models

By default in PUT or PATCH requests the load function sets the update flag of the validate() method to True, so required fields won’t be checked. For other requests like POST update is False.

You can set this manually by passing the update argument to the load function:

user_schema_load = loader(user_schema, update=False)

@App.json(model=User, request_method='PUT', load=user_schema_load)
def user_put(self, request, json):

Customize the Validator

With Cerberus you can customize the rules, data types, validators, coercers (for normalization) and default setters by subclassing CerberusValidator:

import re
from more.cerberus import CerberusValidator

class CustomValidator(CerberusValidator):
    def _validator_validate_email(self, field, value):
      match = re.match(
        '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$',value
      )
      if match == None:
        self._error(field, 'Not valid email')

    def _normalize_coerce_normalize_email(self, value):
        parts = value.split('@')
        if len(parts) != 2:
          return value
        else:
          domain = parts[1].lower
          if domain == 'googlemail.com':
            domain = 'gmail.com'
          return parts[0] + '@' + domain

You have to pass the custom Validator class to the load function:

user_schema_load = loader(user_schema, validator=CustomValidator)

Now you can use the new email validator and normalizer in your schema:

user_schema = {
  'name': {'type': 'string', 'minlength' : 3, 'required': True},
  'email': {'type': 'string', 'validator': 'validate_email',
            'coerce': 'normalize_email','required': True}
}

or with YAML:

user:
  name:
    type: string
    minlength: 3
    required: true
  email:
    type: string
    validator: validate_email
    coerce: normalize_email
    required: true

For more information how to customize the Validator take a look at the Cerberus documentation.

Use the request or app instance in your custom validator

In CerberusValidator you can access the request through self.request and the app through self.request.app. Like this you can use e.g. Morepath settings and services when extending rules.

Here an example from auth-boilerplate for custom email validation and normalization using a service based on email_validator:

from more.cerberus import CerberusValidator
from email_validator import EmailSyntaxError, EmailUndeliverableError


class Validator(CerberusValidator):
    def _validator_verify_email(self, field, value):
        email_validation_service = self.request.app.service(
            name='email_validation'
        )
        try:
            email_validation_service.verify(value)

        except EmailSyntaxError:
            self._error(field, 'Not valid email')

        except EmailUndeliverableError:
            self._error(field, 'Email could not be delivered')

    def _normalize_coerce_normalize_email(self, value):
        email_validation_service = self.request.app.service(
            name='email_validation'
        )
        return email_validation_service.normalize(value)

Error handling

If validation fails due to a validation error (a required field is missing, or a field is of the wrong datatype, for instance), you want to show some kind of error message. The load function created by more.cerberus raises the more.cerberus.ValidationError exception in case of errors.

This exception object has an errors attribute with the validation errors. You must define an exception view for it, otherwise validation errors are returned as “500 internal server error” to API users.

This package provides a default exception view implementation. If you subclass your application from more.cerberus.CerberusApp then you get a default error view for ValidationError that has a 422 status code with a JSON response with the Cerberus errors structure:

from more.cerberus import CerberusApp

class App(CerberusApp):
    pass

Now your app has reasonable error handling built-in.

If you want a different error view you can instead create it by yourself, e.g.:

from more.cerberus.error import ValidationError

from .app import App


@App.json(model=ValidationError)
def validation_error(self, request):
    @request.after
    def set_status(response):
        response.status = 422

    errors = list(self.errors.values())[0][0]

    return {
        'errors': errors
    }

This could be used to extract the errors from a schema wrapped into a dictionary like:

article-schema:
  article:
    type: dict
    schema:
      title:
        type: string
        required: true
      body:
        type: string
        required: true

CHANGES

0.2 (2018-02-11)

  • Add Python 3.6 support.

  • Add example for creating a custom error view to README.

  • Some smaller fixes.

0.1 (2017-03-17)

  • initial public release.

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

more.cerberus-0.2.tar.gz (7.9 kB view details)

Uploaded Source

Built Distribution

more.cerberus-0.2-py2.py3-none-any.whl (11.1 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file more.cerberus-0.2.tar.gz.

File metadata

  • Download URL: more.cerberus-0.2.tar.gz
  • Upload date:
  • Size: 7.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for more.cerberus-0.2.tar.gz
Algorithm Hash digest
SHA256 eb893f72162c5f29b6e3e23af24fd79c2c7f0b960492f243422c3779134ee4da
MD5 00ce2922befd599f13511f7fbec0eaf1
BLAKE2b-256 e4fea23ba56e85347c84a5fdab353fa09cf3d885ec982a5d8620d8917149fb1c

See more details on using hashes here.

Provenance

File details

Details for the file more.cerberus-0.2-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for more.cerberus-0.2-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 070812796f37549d2c19d103b3934349c080d9e71888d63e8269bd9e8b58d16b
MD5 3d0cb7fc6e2975f8a5639a9e5b073eb8
BLAKE2b-256 8bfbc2b7981665cf0c6e6e6a157bb80841bbd280f845dfb8bce5369c16d7a42e

See more details on using hashes here.

Provenance

Supported by

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