aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--djangorestframework/authentication.py19
-rw-r--r--djangorestframework/mixins.py47
-rw-r--r--djangorestframework/validators.py228
-rw-r--r--docs/conf.py1
-rw-r--r--docs/index.rst3
-rw-r--r--docs/library/validators.rst5
7 files changed, 39 insertions, 265 deletions
diff --git a/AUTHORS b/AUTHORS
index 85f577bf..103423ab 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,6 +4,7 @@ Paul Bagwell <pbgwl> - Suggestions & bugfixes.
Marko Tibold <markotibold> - Contributions & Providing the Hudson CI Server.
Sébastien Piquemal <sebpiq> - Contributions.
Carmen Wick <cwick> - Bugfixes.
+Alex Ehlke <aehlke> - Design Contributions.
THANKS TO:
Jesper Noehr <jespern> & the django-piston contributors for providing the starting point for this project.
diff --git a/djangorestframework/authentication.py b/djangorestframework/authentication.py
index e3e44ffb..1c5c832f 100644
--- a/djangorestframework/authentication.py
+++ b/djangorestframework/authentication.py
@@ -22,24 +22,20 @@ __all__ = (
class BaseAuthenticaton(object):
"""
All authentication classes should extend BaseAuthentication.
-
- :param view: :class:`Authentication` classes are always passed the current view on creation.
"""
def __init__(self, view):
"""
+ :param view: :class:`Authentication` classes are always passed the current view on creation.
"""
self.view = view
def authenticate(self, request):
"""
:param request: Request to be authenticated
- :rtype: :obj:`User` object or None [*]_
-
- .. Note::
- This function must be overridden to be implemented.
-
- .. [*] The authentication context *will* typically be a :obj:`User` object,
+ :rtype: :obj:`User` or None [*]_
+
+ .. [*] The authentication context *will* typically be a :obj:`User`,
but it need not be. It can be any user-like object so long as the
permissions classes on the view can handle the object and use
it to determine if the request has the required permissions or not.
@@ -57,6 +53,10 @@ class BasicAuthenticaton(BaseAuthenticaton):
"""
def authenticate(self, request):
+ """
+ Returns a :obj:`User` if a correct username and password have been supplied
+ using HTTP Basic authentication. Otherwise returns `None`.
+ """
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
if 'HTTP_AUTHORIZATION' in request.META:
@@ -84,6 +84,9 @@ class UserLoggedInAuthenticaton(BaseAuthenticaton):
"""
def authenticate(self, request):
+ """
+ Returns a :obj:`User` if the request session currently has a logged in user, otherwise `None`.
+ """
# TODO: Switch this back to request.POST, and let FormParser/MultiPartParser deal with the consequences.
if getattr(request, 'user', None) and request.user.is_active:
# If this is a POST request we enforce CSRF validation.
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py
index 278d4d4d..e101b788 100644
--- a/djangorestframework/mixins.py
+++ b/djangorestframework/mixins.py
@@ -408,28 +408,6 @@ class AuthMixin(object):
permission.check_permission(user)
-##########
-
-class InstanceMixin(object):
- """
- Mixin class that is used to identify a view class as being the canonical identifier
- for the resources it is mapped too.
- """
-
- @classmethod
- def as_view(cls, **initkwargs):
- """
- Store the callable object on the resource class that has been associated with this view.
- """
- view = super(InstanceMixin, cls).as_view(**initkwargs)
- if 'resource' in initkwargs:
- # We do a little dance when we store the view callable...
- # we need to store it wrapped in a 1-tuple, so that inspect will treat it
- # as a function when we later look it up (rather than turning it into a method).
- # This makes sure our URL reversing works ok.
- initkwargs['resource'].view_callable = (view,)
- return view
-
########## Resource Mixin ##########
class ResourceMixin(object):
@@ -449,6 +427,9 @@ class ResourceMixin(object):
@property
def CONTENT(self):
+ """
+ Returns the cleaned, validated request content.
+ """
if not hasattr(self, '_content'):
self._content = self.validate_request(self.DATA, self.FILES)
return self._content
@@ -474,6 +455,28 @@ class ResourceMixin(object):
+##########
+
+class InstanceMixin(object):
+ """
+ Mixin class that is used to identify a view class as being the canonical identifier
+ for the resources it is mapped too.
+ """
+
+ @classmethod
+ def as_view(cls, **initkwargs):
+ """
+ Store the callable object on the resource class that has been associated with this view.
+ """
+ view = super(InstanceMixin, cls).as_view(**initkwargs)
+ if 'resource' in initkwargs:
+ # We do a little dance when we store the view callable...
+ # we need to store it wrapped in a 1-tuple, so that inspect will treat it
+ # as a function when we later look it up (rather than turning it into a method).
+ # This makes sure our URL reversing works ok.
+ initkwargs['resource'].view_callable = (view,)
+ return view
+
########## Model Mixins ##########
diff --git a/djangorestframework/validators.py b/djangorestframework/validators.py
deleted file mode 100644
index bef85af7..00000000
--- a/djangorestframework/validators.py
+++ /dev/null
@@ -1,228 +0,0 @@
-"""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 ErrorResponse
-from djangorestframework.utils import as_tuple
-
-
-class BaseValidator(object):
- """Base class for all Validator classes, which simply defines the interface they provide."""
-
- def __init__(self, view):
- self.view = view
-
- def validate(self, content):
- """Given some content as input return some cleaned, validated content.
- Typically raises a ErrorResponse with status code 400 (Bad Request) on failure.
-
- Must be overridden to be implemented."""
- raise NotImplementedError()
-
-
-class FormValidator(BaseValidator):
- """Validator class that uses forms for validation.
- Also provides a get_bound_form() method which may be used by some renderers.
-
- The view class should provide `.form` attribute which specifies the form classmethod
- to be used for validation.
-
- On calling validate() this validator may set a `.bound_form_instance` attribute on the
- view, which may be used by some renderers."""
-
-
- def validate(self, content):
- """
- Given some content as input return some cleaned, validated content.
- 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 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)
-
- def _validate(self, content, allowed_extra_fields=()):
- """
- Wrapped by validate to hide the extra_fields option that the ModelValidatorMixin uses.
- extra_fields is a list of fields which are not defined by the form, but which we still
- expect to see on the input.
- """
- bound_form = self.get_bound_form(content)
-
- if bound_form is None:
- return content
-
- self.view.bound_form_instance = bound_form
-
- seen_fields_set = set(content.keys())
- form_fields_set = set(bound_form.fields.keys())
- allowed_extra_fields_set = set(allowed_extra_fields)
-
- # In addition to regular validation we also ensure no additional fields are being passed in...
- unknown_fields = seen_fields_set - (form_fields_set | allowed_extra_fields_set)
-
- # Check using both regular validation, and our stricter no additional fields rule
- if bound_form.is_valid() and not unknown_fields:
- # Validation succeeded...
- cleaned_data = bound_form.cleaned_data
-
- cleaned_data.update(bound_form.files)
-
- # Add in any extra fields to the cleaned content...
- for key in (allowed_extra_fields_set & seen_fields_set) - set(cleaned_data.keys()):
- cleaned_data[key] = content[key]
-
- return cleaned_data
-
- # Validation failed...
- detail = {}
-
- if not bound_form.errors and not unknown_fields:
- detail = {u'errors': [u'No content was supplied.']}
-
- else:
- # Add any non-field errors
- if bound_form.non_field_errors():
- detail[u'errors'] = bound_form.non_field_errors()
-
- # Add standard field errors
- field_errors = dict((key, map(unicode, val))
- for (key, val)
- in bound_form.errors.iteritems()
- if not key.startswith('__'))
-
- # Add any unknown field errors
- for key in unknown_fields:
- field_errors[key] = [u'This field does not exist.']
-
- if field_errors:
- detail[u'field-errors'] = field_errors
-
- # Return HTTP 400 response (BAD REQUEST)
- raise ErrorResponse(400, detail)
-
-
- def get_bound_form(self, content=None):
- """Given some content return a Django form bound to that content.
- If form validation is turned off (form class attribute is None) then returns None."""
- form_cls = getattr(self.view, 'form', None)
-
- if not form_cls:
- return None
-
- if content is not None:
- if hasattr(content, 'FILES'):
- return form_cls(content, content.FILES)
- return form_cls(content)
- return form_cls()
-
-
-class ModelFormValidator(FormValidator):
- """Validator class that uses forms for validation and otherwise falls back to a model form if no form is set.
- Also provides a get_bound_form() method which may be used by some renderers."""
-
- """The form class that should be used for validation, or None to use model form validation."""
- form = None
-
- """The model class from which the model form should be constructed if no form is set."""
- model = None
-
- """The list of fields we expect to receive as input. Fields in this list will may be received with
- raising non-existent field errors, even if they do not exist as fields on the ModelForm.
-
- Setting the fields class attribute causes the exclude_fields class attribute to be disregarded."""
- fields = None
-
- """The list of fields to exclude from the Model. This is only used if the fields class attribute is not set."""
- exclude_fields = ('id', 'pk')
-
-
- # TODO: test the different validation here to allow for get get_absolute_url to be supplied on input and not bork out
- # 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 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 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)
-
-
- def get_bound_form(self, content=None):
- """Given some content return a Django form bound to that content.
-
- If the form class attribute has been explicitly set then use that class to create a Form,
- otherwise if model is set use that class to create a ModelForm, otherwise return None."""
-
- form_cls = getattr(self.view, 'form', None)
- model_cls = getattr(self.view.resource, 'model', None)
-
- if form_cls:
- # Use explict Form
- return super(ModelFormValidator, self).get_bound_form(content)
-
- elif model_cls:
- # Fall back to ModelForm which we create on the fly
- class OnTheFlyModelForm(forms.ModelForm):
- class Meta:
- model = model_cls
- #fields = tuple(self._model_fields_set)
-
- # Instantiate the ModelForm as appropriate
- if content and isinstance(content, models.Model):
- # Bound to an existing model instance
- return OnTheFlyModelForm(instance=content)
- elif not content is None:
- if hasattr(content, 'FILES'):
- return OnTheFlyModelForm(content, content.FILES)
- return OnTheFlyModelForm(content)
- return OnTheFlyModelForm()
-
- # Both form and model not set? Okay bruv, whatevs...
- return None
-
-
- @property
- def _model_fields_set(self):
- """Return a set containing the names of validated fields on the model."""
- resource = self.view.resource
- model = getattr(resource, 'model', None)
- fields = getattr(resource, 'fields', self.fields)
- exclude_fields = getattr(resource, 'exclude_fields', self.exclude_fields)
-
- model_fields = set(field.name for field in model._meta.fields)
-
- if fields:
- return model_fields & set(as_tuple(fields))
-
- return model_fields - set(as_tuple(exclude_fields))
-
- @property
- def _property_fields_set(self):
- """Returns a set containing the names of validated properties on the model."""
- resource = self.view.resource
- model = getattr(resource, 'model', None)
- fields = getattr(resource, 'fields', self.fields)
- exclude_fields = getattr(resource, 'exclude_fields', self.exclude_fields)
-
- property_fields = set(attr for attr in dir(model) if
- isinstance(getattr(model, attr, None), property)
- and not attr.startswith('_'))
-
- if fields:
- return property_fields & set(as_tuple(fields))
-
- return property_fields - set(as_tuple(exclude_fields))
-
-
-
diff --git a/docs/conf.py b/docs/conf.py
index 1a87b024..503b4059 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -60,6 +60,7 @@ version = '0.1'
release = '0.1'
autodoc_member_order='bysource'
+
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
diff --git a/docs/index.rst b/docs/index.rst
index 75569d45..3b4e9c49 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -18,7 +18,7 @@ Features:
* Automatically provides an awesome Django admin style `browse-able self-documenting API <http://api.django-rest-framework.org>`_.
* Clean, simple, views for Resources, using Django's new `class based views <http://docs.djangoproject.com/en/dev/topics/class-based-views/>`_.
* Support for ModelResources with out-of-the-box default implementations and input validation.
-* Pluggable :mod:`.emitters`, :mod:`parsers`, :mod:`validators` and :mod:`authenticators` - Easy to customise.
+* Pluggable :mod:`.parsers`, :mod:`renderers`, :mod:`authentication` and :mod:`permissions` - Easy to customise.
* Content type negotiation using HTTP Accept headers.
* Optional support for forms as input validation.
* Modular architecture - MixIn classes can be used without requiring the :class:`.Resource` or :class:`.ModelResource` classes.
@@ -149,7 +149,6 @@ Library Reference
library/resource
library/response
library/status
- library/validators
library/views
Examples Reference
diff --git a/docs/library/validators.rst b/docs/library/validators.rst
deleted file mode 100644
index 5d8b1dd7..00000000
--- a/docs/library/validators.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-:mod:`validators`
-=================
-
-.. automodule:: validators
- :members: