diff --git a/CHANGES.txt b/CHANGES.txt index 3e7d2795..b713f1ca 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,7 +5,7 @@ CHANGELOG 4.1.0 (unreleased) ================== -- Nothing changed yet. +- Add support for passing keyword arguments to marshmallow validator. 4.0.1 (2019-12-02) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index f9ce6d12..a836657d 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -29,6 +29,7 @@ Cornice: * Gabriela Surita * Gael Pasgrimaud * George V. Reilly +* George Mamalakis * Graham Higgins * G.Tjebbes * Guillaume Gauvrit diff --git a/cornice/validators/_marshmallow.py b/cornice/validators/_marshmallow.py index 4f950334..b8dee7bf 100644 --- a/cornice/validators/_marshmallow.py +++ b/cornice/validators/_marshmallow.py @@ -26,6 +26,9 @@ def _validator(request, schema=None, deserializer=None, **kwargs): The content of the location is deserialized, validated and stored in the ``request.validated`` attribute. + Keyword arguments to be included when initialising the marshmallow + schema can be passed as a dict in ``kwargs['schema_kwargs']`` variable. + .. note:: If no schema is defined, this validator does nothing. @@ -46,10 +49,12 @@ def _validator(request, schema=None, deserializer=None, **kwargs): if schema is None: return - schema = _instantiate_schema(schema) + # see if the user wants to set any keyword arguments for their schema + schema_kwargs = kwargs.get('schema_kwargs', {}) + schema = _instantiate_schema(schema, **schema_kwargs) class ValidatedField(marshmallow.fields.Field): - def _deserialize(self, value, attr, data): + def _deserialize(self, value, attr, data, **kwargs): schema.context.setdefault('request', request) deserialized = schema.load(value) # marshmallow 2.x returns a tuple, 3/x will always throw @@ -185,8 +190,17 @@ def validator(request, schema=None, deserializer=None, **kwargs): request.validated.update(deserialized) -def _instantiate_schema(schema): +def _instantiate_schema(schema, **kwargs): + """ + Returns an object of the given marshmallow schema. + + :param schema: The marshmallow schema class with which the request should + be validated + :param kwargs: The keyword arguments that will be provided to the + marshmallow schema's constructor + :return: The object of the marshmallow schema + """ if not inspect.isclass(schema): raise ValueError('You need to pass Marshmallow class instead ' 'of schema instance') - return schema() + return schema(**kwargs) diff --git a/tests/validationapp.py b/tests/validationapp.py index 7f4e0658..260c21da 100644 --- a/tests/validationapp.py +++ b/tests/validationapp.py @@ -357,16 +357,6 @@ class Meta: unknown = EXCLUDE username = marshmallow.fields.String() - class MSignupGroupSchema(marshmallow.Schema): - class Meta: - strict = True - unknown = EXCLUDE - username = marshmallow.fields.String() - - def __init__(self, *args, **kwargs): - kwargs['many'] = True - marshmallow.Schema.__init__(self, *args, **kwargs) - import random class MNeedsContextSchema(marshmallow.Schema): @@ -392,8 +382,15 @@ def m_bound_post(request): def signup_post(request): return request.validated - @m_group_signup.post( - schema=MSignupGroupSchema, validators=(marshmallow_body_validator,)) + # callback that returns a validator with keyword arguments for marshmallow + # schema initialisation. In our case it passes many=True to the desired + # schema + def get_my_marshmallow_validator_with_kwargs(request, **kwargs): + kwargs['schema'] = MSignupSchema + kwargs['schema_kwargs'] = {'many': True} + return marshmallow_body_validator(request, **kwargs) + + @m_group_signup.post(validators=(get_my_marshmallow_validator_with_kwargs,)) def m_group_signup_post(request): return {'data': request.validated} @@ -520,6 +517,7 @@ class Meta: def m_form(request): return request.validated + def includeme(config): config.include("cornice") config.scan("tests.validationapp")