aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--djangorestframework/emitters.py147
-rw-r--r--djangorestframework/modelresource.py6
-rw-r--r--djangorestframework/parsers.py4
-rw-r--r--djangorestframework/resource.py27
-rw-r--r--djangorestframework/response.py27
-rw-r--r--djangorestframework/tests/content.py2
-rw-r--r--djangorestframework/tests/emitters.py5
-rw-r--r--djangorestframework/tests/methods.py2
-rw-r--r--djangorestframework/tests/validators.py20
-rw-r--r--djangorestframework/utils.py1
-rw-r--r--djangorestframework/validators.py14
11 files changed, 64 insertions, 191 deletions
diff --git a/djangorestframework/emitters.py b/djangorestframework/emitters.py
index 36feea05..2990d313 100644
--- a/djangorestframework/emitters.py
+++ b/djangorestframework/emitters.py
@@ -4,11 +4,10 @@ by serializing the output along with documentation regarding the Resource, outpu
and providing forms and links depending on the allowed methods, emitters and parsers on the Resource.
"""
from django.conf import settings
-from django.http import HttpResponse
from django.template import RequestContext, loader
from django import forms
-from djangorestframework.response import NoContent, ResponseException
+from djangorestframework.response import ErrorResponse
from djangorestframework.utils import dict2xml, url_resolves
from djangorestframework.markdownwrapper import apply_markdown
from djangorestframework.breadcrumbs import get_breadcrumbs
@@ -18,7 +17,6 @@ from djangorestframework import status
from urllib import quote_plus
import string
import re
-from decimal import Decimal
try:
import json
@@ -26,132 +24,9 @@ except ImportError:
import simplejson as json
-_MSIE_USER_AGENT = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )')
-
-
-class EmitterMixin(object):
- """Adds behaviour for pluggable Emitters to a :class:`.Resource` or Django :class:`View`. class.
-
- Default behaviour is to use standard HTTP Accept header content negotiation.
- Also supports overidding the content type by specifying an _accept= parameter in the URL.
- Ignores Accept headers from Internet Explorer user agents and uses a sensible browser Accept header instead."""
-
- ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params
- REWRITE_IE_ACCEPT_HEADER = True
-
- request = None
- response = None
- emitters = ()
-
- def emit(self, response):
- """Takes a :class:`Response` object and returns a Django :class:`HttpResponse`."""
- self.response = response
-
- try:
- emitter = self._determine_emitter(self.request)
- except ResponseException, exc:
- emitter = self.default_emitter
- response = exc.response
-
- # Serialize the response content
- if response.has_content_body:
- content = emitter(self).emit(output=response.cleaned_content)
- else:
- content = emitter(self).emit()
-
- # Munge DELETE Response code to allow us to return content
- # (Do this *after* we've rendered the template so that we include the normal deletion response code in the output)
- if response.status == 204:
- response.status = 200
-
- # Build the HTTP Response
- # TODO: Check if emitter.mimetype is underspecified, or if a content-type header has been set
- resp = HttpResponse(content, mimetype=emitter.media_type, status=response.status)
- for (key, val) in response.headers.items():
- resp[key] = val
-
- return resp
-
-
- def _determine_emitter(self, request):
- """Return the appropriate emitter for the output, given the client's 'Accept' header,
- and the content types that this Resource knows how to serve.
-
- See: RFC 2616, Section 14 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html"""
-
- if self.ACCEPT_QUERY_PARAM and request.GET.get(self.ACCEPT_QUERY_PARAM, None):
- # Use _accept parameter override
- accept_list = [request.GET.get(self.ACCEPT_QUERY_PARAM)]
- elif self.REWRITE_IE_ACCEPT_HEADER and request.META.has_key('HTTP_USER_AGENT') and _MSIE_USER_AGENT.match(request.META['HTTP_USER_AGENT']):
- accept_list = ['text/html', '*/*']
- elif request.META.has_key('HTTP_ACCEPT'):
- # Use standard HTTP Accept negotiation
- accept_list = request.META["HTTP_ACCEPT"].split(',')
- else:
- # No accept header specified
- return self.default_emitter
-
- # Parse the accept header into a dict of {qvalue: set of media types}
- # We ignore mietype parameters
- accept_dict = {}
- for token in accept_list:
- components = token.split(';')
- mimetype = components[0].strip()
- qvalue = Decimal('1.0')
-
- if len(components) > 1:
- # Parse items that have a qvalue eg text/html;q=0.9
- try:
- (q, num) = components[-1].split('=')
- if q == 'q':
- qvalue = Decimal(num)
- except:
- # Skip malformed entries
- continue
-
- if accept_dict.has_key(qvalue):
- accept_dict[qvalue].add(mimetype)
- else:
- accept_dict[qvalue] = set((mimetype,))
-
- # Convert to a list of sets ordered by qvalue (highest first)
- accept_sets = [accept_dict[qvalue] for qvalue in sorted(accept_dict.keys(), reverse=True)]
-
- for accept_set in accept_sets:
- # Return any exact match
- for emitter in self.emitters:
- if emitter.media_type in accept_set:
- return emitter
-
- # Return any subtype match
- for emitter in self.emitters:
- if emitter.media_type.split('/')[0] + '/*' in accept_set:
- return emitter
-
- # Return default
- if '*/*' in accept_set:
- return self.default_emitter
-
-
- raise ResponseException(status.HTTP_406_NOT_ACCEPTABLE,
- {'detail': 'Could not statisfy the client\'s Accept header',
- 'available_types': self.emitted_media_types})
-
- @property
- def emitted_media_types(self):
- """Return an list of all the media types that this resource can emit."""
- return [emitter.media_type for emitter in self.emitters]
-
- @property
- def default_emitter(self):
- """Return the resource's most prefered emitter.
- (This emitter is used if the client does not send and Accept: header, or sends Accept: */*)"""
- return self.emitters[0]
-
-
# TODO: Rename verbose to something more appropriate
-# TODO: NoContent could be handled more cleanly. It'd be nice if it was handled by default,
+# TODO: Maybe None could be handled more cleanly. It'd be nice if it was handled by default,
# and only have an emitter output anything if it explicitly provides support for that.
class BaseEmitter(object):
@@ -162,10 +37,10 @@ class BaseEmitter(object):
def __init__(self, resource):
self.resource = resource
- def emit(self, output=NoContent, verbose=False):
+ def emit(self, output=None, verbose=False):
"""By default emit simply returns the ouput as-is.
Override this method to provide for other behaviour."""
- if output is NoContent:
+ if output is None:
return ''
return output
@@ -177,8 +52,8 @@ class TemplateEmitter(BaseEmitter):
media_type = None
template = None
- def emit(self, output=NoContent, verbose=False):
- if output is NoContent:
+ def emit(self, output=None, verbose=False):
+ if output is None:
return ''
context = RequestContext(self.request, output)
@@ -276,7 +151,7 @@ class DocumentingTemplateEmitter(BaseEmitter):
return GenericContentForm(resource)
- def emit(self, output=NoContent):
+ def emit(self, output=None):
content = self._get_content(self.resource, self.resource.request, output)
form_instance = self._get_form_instance(self.resource)
@@ -324,8 +199,8 @@ class JSONEmitter(BaseEmitter):
"""Emitter which serializes to JSON"""
media_type = 'application/json'
- def emit(self, output=NoContent, verbose=False):
- if output is NoContent:
+ def emit(self, output=None, verbose=False):
+ if output is None:
return ''
if verbose:
return json.dumps(output, indent=4, sort_keys=True)
@@ -336,8 +211,8 @@ class XMLEmitter(BaseEmitter):
"""Emitter which serializes to XML."""
media_type = 'application/xml'
- def emit(self, output=NoContent, verbose=False):
- if output is NoContent:
+ def emit(self, output=None, verbose=False):
+ if output is None:
return ''
return dict2xml(output)
diff --git a/djangorestframework/modelresource.py b/djangorestframework/modelresource.py
index 7fa37053..b0a4b1c1 100644
--- a/djangorestframework/modelresource.py
+++ b/djangorestframework/modelresource.py
@@ -3,7 +3,7 @@ from django.db.models import Model
from django.db.models.query import QuerySet
from django.db.models.fields.related import RelatedField
-from djangorestframework.response import Response, ResponseException
+from djangorestframework.response import Response, ErrorResponse
from djangorestframework.resource import Resource
from djangorestframework import status, validators
@@ -370,7 +370,7 @@ class ModelResource(Resource):
# Otherwise assume the kwargs uniquely identify the model
instance = self.model.objects.get(**kwargs)
except self.model.DoesNotExist:
- raise ResponseException(status.HTTP_404_NOT_FOUND)
+ raise ErrorResponse(status.HTTP_404_NOT_FOUND)
return instance
@@ -402,7 +402,7 @@ class ModelResource(Resource):
# Otherwise assume the kwargs uniquely identify the model
instance = self.model.objects.get(**kwargs)
except self.model.DoesNotExist:
- raise ResponseException(status.HTTP_404_NOT_FOUND, None, {})
+ raise ErrorResponse(status.HTTP_404_NOT_FOUND, None, {})
instance.delete()
return
diff --git a/djangorestframework/parsers.py b/djangorestframework/parsers.py
index 11adeb78..707b61d5 100644
--- a/djangorestframework/parsers.py
+++ b/djangorestframework/parsers.py
@@ -9,7 +9,7 @@ We need a method to be able to:
and multipart/form-data. (eg also handle multipart/json)
"""
from django.http.multipartparser import MultiPartParser as DjangoMPParser
-from djangorestframework.response import ResponseException
+from djangorestframework.response import ErrorResponse
from djangorestframework import status
from djangorestframework.utils import as_tuple
from djangorestframework.mediatypes import MediaType
@@ -59,7 +59,7 @@ class JSONParser(BaseParser):
try:
return json.load(stream)
except ValueError, exc:
- raise ResponseException(status.HTTP_400_BAD_REQUEST, {'detail': 'JSON parse error - %s' % str(exc)})
+ raise ErrorResponse(status.HTTP_400_BAD_REQUEST, {'detail': 'JSON parse error - %s' % str(exc)})
class DataFlatener(object):
diff --git a/djangorestframework/resource.py b/djangorestframework/resource.py
index 1a02f8b5..f4460c1e 100644
--- a/djangorestframework/resource.py
+++ b/djangorestframework/resource.py
@@ -2,9 +2,8 @@ from django.core.urlresolvers import set_script_prefix
from django.views.decorators.csrf import csrf_exempt
from djangorestframework.compat import View
-from djangorestframework.emitters import EmitterMixin
-from djangorestframework.response import Response, ResponseException
-from djangorestframework.request import RequestMixin, AuthMixin
+from djangorestframework.response import Response, ErrorResponse
+from djangorestframework.mixins import RequestMixin, ResponseMixin, AuthMixin
from djangorestframework import emitters, parsers, authenticators, validators, status
@@ -16,7 +15,7 @@ from djangorestframework import emitters, parsers, authenticators, validators, s
__all__ = ['Resource']
-class Resource(EmitterMixin, AuthMixin, RequestMixin, View):
+class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
"""Handles incoming requests and maps them to REST operations,
performing authentication, input deserialization, input validation, output serialization."""
@@ -81,7 +80,7 @@ class Resource(EmitterMixin, AuthMixin, RequestMixin, View):
def not_implemented(self, operation):
"""Return an HTTP 500 server error if an operation is called which has been allowed by
allowed_methods, but which has not been implemented."""
- raise ResponseException(status.HTTP_500_INTERNAL_SERVER_ERROR,
+ raise ErrorResponse(status.HTTP_500_INTERNAL_SERVER_ERROR,
{'detail': '%s operation on this resource has not been implemented' % (operation, )})
@@ -89,15 +88,15 @@ class Resource(EmitterMixin, AuthMixin, RequestMixin, View):
"""Ensure the request method is permitted for this resource, raising a ResourceException if it is not."""
if not method in self.callmap.keys():
- raise ResponseException(status.HTTP_501_NOT_IMPLEMENTED,
+ raise ErrorResponse(status.HTTP_501_NOT_IMPLEMENTED,
{'detail': 'Unknown or unsupported method \'%s\'' % method})
if not method in self.allowed_methods:
- raise ResponseException(status.HTTP_405_METHOD_NOT_ALLOWED,
+ raise ErrorResponse(status.HTTP_405_METHOD_NOT_ALLOWED,
{'detail': 'Method \'%s\' not allowed on this resource.' % method})
if auth is None and not method in self.anon_allowed_methods:
- raise ResponseException(status.HTTP_403_FORBIDDEN,
+ raise ErrorResponse(status.HTTP_403_FORBIDDEN,
{'detail': 'You do not have permission to access this resource. ' +
'You may need to login or otherwise authenticate the request.'})
@@ -172,7 +171,7 @@ class Resource(EmitterMixin, AuthMixin, RequestMixin, View):
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
response.cleaned_content = self.cleanup_response(response.raw_content)
- except ResponseException, exc:
+ except ErrorResponse, exc:
response = exc.response
except:
@@ -183,8 +182,12 @@ class Resource(EmitterMixin, AuthMixin, RequestMixin, View):
#
# TODO - this isn't actually the correct way to set the vary header,
# also it's currently sub-obtimal for HTTP caching - need to sort that out.
- response.headers['Allow'] = ', '.join(self.allowed_methods)
- response.headers['Vary'] = 'Authenticate, Accept'
+ try:
+ response.headers['Allow'] = ', '.join(self.allowed_methods)
+ response.headers['Vary'] = 'Authenticate, Accept'
- return self.emit(response)
+ return self.emit(response)
+ except:
+ import traceback
+ traceback.print_exc()
diff --git a/djangorestframework/response.py b/djangorestframework/response.py
index 809e1754..545a5834 100644
--- a/djangorestframework/response.py
+++ b/djangorestframework/response.py
@@ -1,24 +1,16 @@
from django.core.handlers.wsgi import STATUS_CODE_TEXT
-__all__ =['NoContent', 'Response', ]
-
-
-
-class NoContent(object):
- """Used to indicate no body in http response.
- (We cannot just use None, as that is a valid, serializable response object.)
-
- TODO: On reflection I'm going to get rid of this and just not support serialized 'None' responses.
- """
- pass
+__all__ =['Response', 'ErrorResponse']
+# TODO: remove raw_content/cleaned_content and just use content?
class Response(object):
- def __init__(self, status=200, content=NoContent, headers={}):
+ """An HttpResponse that may include content that hasn't yet been serialized."""
+ def __init__(self, status=200, content=None, headers={}):
self.status = status
- self.has_content_body = not content is NoContent # TODO: remove and just use content
- self.raw_content = content # content prior to filtering - TODO: remove and just use content
- self.cleaned_content = content # content after filtering TODO: remove and just use content
+ self.has_content_body = content is not None
+ self.raw_content = content # content prior to filtering
+ self.cleaned_content = content # content after filtering
self.headers = headers
@property
@@ -28,6 +20,7 @@ class Response(object):
return STATUS_CODE_TEXT.get(self.status, '')
-class ResponseException(BaseException):
- def __init__(self, status, content=NoContent, headers={}):
+class ErrorResponse(BaseException):
+ """An exception representing an HttpResponse that should be returned immediatley."""
+ def __init__(self, status, content=None, headers={}):
self.response = Response(status, content=content, headers=headers)
diff --git a/djangorestframework/tests/content.py b/djangorestframework/tests/content.py
index b99f30f7..6695bf68 100644
--- a/djangorestframework/tests/content.py
+++ b/djangorestframework/tests/content.py
@@ -3,7 +3,7 @@ Tests for content parsing, and form-overloaded content parsing.
"""
from django.test import TestCase
from djangorestframework.compat import RequestFactory
-from djangorestframework.request import RequestMixin
+from djangorestframework.mixins import RequestMixin
from djangorestframework.parsers import FormParser, MultipartParser, PlainTextParser
diff --git a/djangorestframework/tests/emitters.py b/djangorestframework/tests/emitters.py
index 7d024ccf..21a7eb95 100644
--- a/djangorestframework/tests/emitters.py
+++ b/djangorestframework/tests/emitters.py
@@ -2,7 +2,8 @@ from django.conf.urls.defaults import patterns, url
from django import http
from django.test import TestCase
from djangorestframework.compat import View
-from djangorestframework.emitters import EmitterMixin, BaseEmitter
+from djangorestframework.emitters import BaseEmitter
+from djangorestframework.mixins import ResponseMixin
from djangorestframework.response import Response
DUMMYSTATUS = 200
@@ -11,7 +12,7 @@ DUMMYCONTENT = 'dummycontent'
EMITTER_A_SERIALIZER = lambda x: 'Emitter A: %s' % x
EMITTER_B_SERIALIZER = lambda x: 'Emitter B: %s' % x
-class MockView(EmitterMixin, View):
+class MockView(ResponseMixin, View):
def get(self, request):
response = Response(DUMMYSTATUS, DUMMYCONTENT)
return self.emit(response)
diff --git a/djangorestframework/tests/methods.py b/djangorestframework/tests/methods.py
index 7f6acf4f..0e74dc94 100644
--- a/djangorestframework/tests/methods.py
+++ b/djangorestframework/tests/methods.py
@@ -1,6 +1,6 @@
from django.test import TestCase
from djangorestframework.compat import RequestFactory
-from djangorestframework.request import RequestMixin
+from djangorestframework.mixins import RequestMixin
class TestMethodOverloading(TestCase):
diff --git a/djangorestframework/tests/validators.py b/djangorestframework/tests/validators.py
index a091cf29..b6563db6 100644
--- a/djangorestframework/tests/validators.py
+++ b/djangorestframework/tests/validators.py
@@ -3,7 +3,7 @@ from django.db import models
from django.test import TestCase
from djangorestframework.compat import RequestFactory
from djangorestframework.validators import BaseValidator, FormValidator, ModelFormValidator
-from djangorestframework.response import ResponseException
+from djangorestframework.response import ErrorResponse
class TestValidatorMixinInterfaces(TestCase):
@@ -81,7 +81,7 @@ class TestNonFieldErrors(TestCase):
content = {'field1': 'example1', 'field2': 'example2'}
try:
FormValidator(view).validate(content)
- except ResponseException, exc:
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'errors': [MockForm.ERROR_TEXT]})
else:
self.fail('ResourceException was not raised') #pragma: no cover
@@ -115,14 +115,14 @@ class TestFormValidation(TestCase):
def validation_failure_raises_response_exception(self, validator):
"""If form validation fails a ResourceException 400 (Bad Request) should be raised."""
content = {}
- self.assertRaises(ResponseException, validator.validate, content)
+ self.assertRaises(ErrorResponse, validator.validate, content)
def validation_does_not_allow_extra_fields_by_default(self, validator):
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
broken clients more easily (eg submitting content with a misnamed field)"""
content = {'qwerty': 'uiop', 'extra': 'extra'}
- self.assertRaises(ResponseException, validator.validate, content)
+ self.assertRaises(ErrorResponse, validator.validate, content)
def validation_allows_extra_fields_if_explicitly_set(self, validator):
"""If we include an allowed_extra_fields paramater on _validate, then allow fields with those names."""
@@ -139,7 +139,7 @@ class TestFormValidation(TestCase):
content = {}
try:
validator.validate(content)
- except ResponseException, exc:
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'field-errors': {'qwerty': ['This field is required.']}})
else:
self.fail('ResourceException was not raised') #pragma: no cover
@@ -149,7 +149,7 @@ class TestFormValidation(TestCase):
content = {'qwerty': ''}
try:
validator.validate(content)
- except ResponseException, exc:
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'field-errors': {'qwerty': ['This field is required.']}})
else:
self.fail('ResourceException was not raised') #pragma: no cover
@@ -159,7 +159,7 @@ class TestFormValidation(TestCase):
content = {'qwerty': 'uiop', 'extra': 'extra'}
try:
validator.validate(content)
- except ResponseException, exc:
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'field-errors': {'extra': ['This field does not exist.']}})
else:
self.fail('ResourceException was not raised') #pragma: no cover
@@ -169,7 +169,7 @@ class TestFormValidation(TestCase):
content = {'qwerty': '', 'extra': 'extra'}
try:
validator.validate(content)
- except ResponseException, exc:
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'field-errors': {'qwerty': ['This field is required.'],
'extra': ['This field does not exist.']}})
else:
@@ -286,14 +286,14 @@ class TestModelFormValidator(TestCase):
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
broken clients more easily (eg submitting content with a misnamed field)"""
content = {'qwerty': 'example', 'uiop':'example', 'readonly': 'read only', 'extra': 'extra'}
- self.assertRaises(ResponseException, self.validator.validate, content)
+ self.assertRaises(ErrorResponse, self.validator.validate, content)
def test_validate_requires_fields_on_model_forms(self):
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
broken clients more easily (eg submitting content with a misnamed field)"""
content = {'readonly': 'read only'}
- self.assertRaises(ResponseException, self.validator.validate, content)
+ self.assertRaises(ErrorResponse, self.validator.validate, content)
def test_validate_does_not_require_blankable_fields_on_model_forms(self):
"""Test standard ModelForm validation behaviour - fields with blank=True are not required."""
diff --git a/djangorestframework/utils.py b/djangorestframework/utils.py
index d45e5acf..8b12294c 100644
--- a/djangorestframework/utils.py
+++ b/djangorestframework/utils.py
@@ -14,6 +14,7 @@ except ImportError:
# """Adds the ADMIN_MEDIA_PREFIX to the request context."""
# return {'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX}
+MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )')
def as_tuple(obj):
"""Given obj return a tuple"""
diff --git a/djangorestframework/validators.py b/djangorestframework/validators.py
index 609e6d36..c612de55 100644
--- a/djangorestframework/validators.py
+++ b/djangorestframework/validators.py
@@ -1,7 +1,7 @@
"""Mixin classes that provide a validate(content) function to validate and cleanup request content"""
from django import forms
from django.db import models
-from djangorestframework.response import ResponseException
+from djangorestframework.response import ErrorResponse
from djangorestframework.utils import as_tuple
@@ -13,7 +13,7 @@ class BaseValidator(object):
def validate(self, content):
"""Given some content as input return some cleaned, validated content.
- Typically raises a ResponseException with status code 400 (Bad Request) on failure.
+ Typically raises a ErrorResponse with status code 400 (Bad Request) on failure.
Must be overridden to be implemented."""
raise NotImplementedError()
@@ -32,11 +32,11 @@ class FormValidator(BaseValidator):
def validate(self, content):
"""Given some content as input return some cleaned, validated content.
- Raises a ResponseException with status code 400 (Bad Request) on failure.
+ Raises a ErrorResponse with status code 400 (Bad Request) on failure.
Validation is standard form validation, with an additional constraint that no extra unknown fields may be supplied.
- On failure the ResponseException content is a dict which may contain 'errors' and 'field-errors' keys.
+ On failure the ErrorResponse content is a dict which may contain 'errors' and 'field-errors' keys.
If the 'errors' key exists it is a list of strings of non-field errors.
If the 'field-errors' key exists it is a dict of {field name as string: list of errors as strings}."""
return self._validate(content)
@@ -97,7 +97,7 @@ class FormValidator(BaseValidator):
detail[u'field-errors'] = field_errors
# Return HTTP 400 response (BAD REQUEST)
- raise ResponseException(400, detail)
+ raise ErrorResponse(400, detail)
def get_bound_form(self, content=None):
@@ -139,14 +139,14 @@ class ModelFormValidator(FormValidator):
# TODO: be really strict on fields - check they match in the handler methods. (this isn't a validator thing tho.)
def validate(self, content):
"""Given some content as input return some cleaned, validated content.
- Raises a ResponseException with status code 400 (Bad Request) on failure.
+ Raises a ErrorResponse with status code 400 (Bad Request) on failure.
Validation is standard form or model form validation,
with an additional constraint that no extra unknown fields may be supplied,
and that all fields specified by the fields class attribute must be supplied,
even if they are not validated by the form/model form.
- On failure the ResponseException content is a dict which may contain 'errors' and 'field-errors' keys.
+ On failure the ErrorResponse content is a dict which may contain 'errors' and 'field-errors' keys.
If the 'errors' key exists it is a list of strings of non-field errors.
If the 'field-errors' key exists it is a dict of {field name as string: list of errors as strings}."""
return self._validate(content, allowed_extra_fields=self._property_fields_set)