Last active
July 23, 2016 10:58
-
-
Save fdemmer/311f7cb1fad16845636f13c15eb73e68 to your computer and use it in GitHub Desktop.
Using marshmallow to parse/validate query parameters with a mixin for Django views
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from marshmallow import Schema, fields | |
from django.utils.functional import cached_property | |
class ParameterMixin(object): | |
""" | |
Mixin for a View-like class for URL parameter parsing and validation using | |
marshmallow (works well with Django views or DRF ViewSets). | |
Inside the class define a marshmallow `Schema` with the URL parameters as | |
fields called "Parameters", eg:: | |
from marshmallow import schema, fields | |
class MyView(View): | |
class Parameters(schema.Schema): | |
activate = fields.Boolean(missing=False) | |
The name of the class can be customized using `self.parameter_schema_name`. | |
Overload `self.get_parameters()` to customize the parameters source. | |
Then use the `self.query` cached_property to access cleaned, validated | |
parameters:: | |
self.query['activate'] | |
Upon first access to `self.query` the parameters are validated. | |
If the parameter schema was initialized in strict mode (default), this | |
may raise a `ValidationError` exception! | |
With DRF it is recommended to overload `handle_error` in the schema | |
class to raise a DRF `APIException` for a proper error response:: | |
from rest_framework import viewsets, exceptions | |
class MyViewSet(ParameterMixin, viewsets.ReadOnlyModelViewSet): | |
class Parameters(schema.Schema): | |
updated_after = fields.DateTime(missing=None) | |
def handle_error(self, error, data): | |
raise exceptions.ValidationError(error.messages) | |
To disable strict validation set `self.parameter_strict` to `False`. | |
""" | |
parameter_schema_name = 'Parameters' | |
parameter_strict = True | |
@cached_property | |
def parameter_schema(self): | |
return self.get_parameter_schema() | |
@property | |
def _list_type_parameters(self): | |
return [ | |
k for k, v in self.parameter_schema.fields.items() | |
if isinstance(v, fields.List) | |
] | |
def get_parameter_schema(self, strict=None): | |
cls = getattr(self, self.parameter_schema_name, Schema) | |
strict = strict if strict is not None else self.parameter_strict | |
return cls(strict=strict) | |
def get_parameters(self): | |
querydict = self.request.GET | |
return { | |
k: v if k in self._list_type_parameters else v[0] | |
for k, v in querydict.iterlists() | |
} | |
@cached_property | |
def query(self): | |
schema = self.get_parameter_schema() | |
# raises validation error, when schema is using strict | |
return schema.load(self.get_parameters()).data |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment