Skip to content

Commit

Permalink
Merge pull request #288 from mozilla-services/option-request-binding
Browse files Browse the repository at this point in the history
Add option to disable schema request binding
  • Loading branch information
almet committed Apr 14, 2015
2 parents 0a6fd78 + 3c21533 commit 825f232
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 7 deletions.
9 changes: 5 additions & 4 deletions cornice/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ class SchemaError(Exception):
class CorniceSchema(object):
"""Defines a cornice schema"""

def __init__(self, _colander_schema):
def __init__(self, _colander_schema, bind_request=True):
self._colander_schema = _colander_schema
self._colander_schema_runtime = None
self._bind_request = bind_request

@property
def colander_schema(self):
Expand All @@ -30,7 +31,7 @@ def colander_schema(self):

def bind_attributes(self, request=None):
schema = self.colander_schema
if request:
if request and self._bind_request:
schema = schema.bind(request=request)
return schema.children

Expand Down Expand Up @@ -88,8 +89,8 @@ def flatten(self, data):
return self.colander_schema.flatten(data)

@classmethod
def from_colander(klass, colander_schema):
return CorniceSchema(colander_schema)
def from_colander(klass, colander_schema, **kwargs):
return CorniceSchema(colander_schema, **kwargs)


def validate_colander_schema(schema, request):
Expand Down
15 changes: 15 additions & 0 deletions cornice/tests/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,21 @@ def test_colander_bound_schema_rebinds_to_new_request(self):
field = the_schema.get_attributes(request=other_dummy_request)[3]
self.assertEqual(field.validator.choices, ['c', 'd'])

def test_colander_request_is_bound_by_default(self):
the_schema = CorniceSchema.from_colander(ToBoundSchema)
dummy_request = {'x-foo': 'version_a'}
field = the_schema.get_attributes(request=dummy_request)[3]
# Deferred are resolved
self.assertNotEqual(type(field.validator), deferred)

def test_colander_request_is_not_bound_if_disabled(self):
the_schema = CorniceSchema.from_colander(ToBoundSchema,
bind_request=False)
dummy_request = {'x-foo': 'version_a'}
field = the_schema.get_attributes(request=dummy_request)[3]
# Deferred are not resolved
self.assertEqual(type(field.validator), deferred)

def test_imperative_colander_schema(self):
# not specifying body should act the same way as specifying it
schema = CorniceSchema.from_colander(imperative_schema)
Expand Down
37 changes: 34 additions & 3 deletions docs/source/validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,47 @@ To describe a schema, using colander and cornice, here is how you can do::

You can even use Schema-Inheritance as introduced by Colander 0.9.9.

If you want the schema to be dynamic, i.e. you want to chose which one to use per request you can define it as a property on your class and it will be used instead. In this case, however, you must explitly bind it yourself. For example::

If you want to access the request within the the schema nodes during validation,
you can use the `deferred feature of Colander <http://docs.pylonsproject.org/projects/colander/en/latest/binding.html>`_,
since Cornice binds the schema with the current request::

def deferred_validator(node, kw):
request = kw['request']
if request['x-foo'] == 'version_a':
return OneOf(['a', 'b'])
else:
return OneOf(['c', 'd'])

class FooBarSchema(MappingSchema):
choice = SchemaNode(String(), validator=deferred_validator)

.. note::

Since binding on request has a cost, it can be disabled
by specifying ``bind_request`` as ``False``::

@property
def schema(self):
return CorniceSchema.from_colander(FooBarSchema(),
bind_request=False)


If you want the schema to be dynamic, i.e. you want to chose which one to use
per request you can define it as a property on your class and it will be used
instead. For example::

@property
def schema(self):
if self.request.method == 'POST':
schema = foo_schema
elif self.request.method == 'PUT':
schema = bar_schema
schema = schema().bind(context=self.context, request=self.request)
return CorniceSchema(schema.children)
schema = CorniceSchema.from_colander(schema)
# Custom additional context
schema = schema.bind(context=self.context)
return schema


Cornice provides built-in support for JSON and HTML forms
(``application/x-www-form-urlencoded``) input validation using Colander. If
Expand Down

0 comments on commit 825f232

Please sign in to comment.