aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2012-08-24 22:11:00 +0100
committerTom Christie2012-08-24 22:11:00 +0100
commitaed26b218ea39110489e85abc6f412399a1774a1 (patch)
tree0b461ba4f7f5266056b6463e9b80214e5e273502
parent87b363f7bc5f73d850df123a61895d65ec0b05e7 (diff)
downloaddjango-rest-framework-aed26b218ea39110489e85abc6f412399a1774a1.tar.bz2
Drop out resources & mixins
-rw-r--r--djangorestframework/mixins.py388
-rw-r--r--djangorestframework/permissions.py14
-rw-r--r--djangorestframework/resources.py343
-rw-r--r--djangorestframework/serializer.py283
-rw-r--r--djangorestframework/tests/files.py51
-rw-r--r--djangorestframework/tests/mixins.py565
-rw-r--r--djangorestframework/tests/modelviews.py132
-rw-r--r--djangorestframework/tests/serializer.py297
-rw-r--r--djangorestframework/tests/throttling.py14
-rw-r--r--djangorestframework/tests/validators.py639
-rw-r--r--djangorestframework/tests/views.py263
-rw-r--r--djangorestframework/views.py88
12 files changed, 980 insertions, 2097 deletions
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py
index 28fa5847..e69de29b 100644
--- a/djangorestframework/mixins.py
+++ b/djangorestframework/mixins.py
@@ -1,388 +0,0 @@
-"""
-The :mod:`mixins` module provides a set of reusable `mixin`
-classes that can be added to a `View`.
-"""
-
-from django.core.paginator import Paginator
-from django.db.models.fields.related import ForeignKey
-from urlobject import URLObject
-
-from djangorestframework import status
-from djangorestframework.renderers import BaseRenderer
-from djangorestframework.resources import Resource, FormResource, ModelResource
-from djangorestframework.response import Response, ImmediateResponse
-
-
-__all__ = (
- # Base behavior mixins
- 'ResourceMixin',
- # Model behavior mixins
- 'ReadModelMixin',
- 'CreateModelMixin',
- 'UpdateModelMixin',
- 'DeleteModelMixin',
- 'ListModelMixin',
- 'PaginatorMixin'
-)
-
-
-########## Resource Mixin ##########
-
-class ResourceMixin(object):
- """
- Provides request validation and response filtering behavior.
-
- Should be a class as described in the :mod:`resources` module.
-
- The :obj:`resource` is an object that maps a view onto it's representation on the server.
-
- It provides validation on the content of incoming requests,
- and filters the object representation into a serializable object for the response.
- """
- resource = None
-
- @property
- def CONTENT(self):
- """
- Returns the cleaned, validated request content.
-
- May raise an :class:`response.ImmediateResponse` with status code 400 (Bad Request).
- """
- if not hasattr(self, '_content'):
- self._content = self.validate_request(self.request.DATA, self.request.FILES)
- return self._content
-
- @property
- def PARAMS(self):
- """
- Returns the cleaned, validated query parameters.
-
- May raise an :class:`response.ImmediateResponse` with status code 400 (Bad Request).
- """
- return self.validate_request(self.request.GET)
-
- @property
- def _resource(self):
- if self.resource:
- return self.resource(self)
- elif getattr(self, 'model', None):
- return ModelResource(self)
- elif getattr(self, 'form', None):
- return FormResource(self)
- elif getattr(self, '%s_form' % self.request.method.lower(), None):
- return FormResource(self)
- return Resource(self)
-
- def validate_request(self, data, files=None):
- """
- Given the request *data* and optional *files*, return the cleaned, validated content.
- May raise an :class:`response.ImmediateResponse` with status code 400 (Bad Request) on failure.
- """
- return self._resource.validate_request(data, files)
-
- def filter_response(self, obj):
- """
- Given the response content, filter it into a serializable object.
- """
- return self._resource.filter_response(obj)
-
- def get_bound_form(self, content=None, method=None):
- if hasattr(self._resource, 'get_bound_form'):
- return self._resource.get_bound_form(content, method=method)
- else:
- return None
-
-
-########## Model Mixins ##########
-
-class ModelMixin(object):
- """ Implements mechanisms used by other classes (like *ModelMixin group) to
- define a query that represents Model instances the Mixin is working with.
-
- If a *ModelMixin is going to retrive an instance (or queryset) using args and kwargs
- passed by as URL arguments, it should provied arguments to objects.get and objects.filter
- methods wrapped in by `build_query`
-
- If a *ModelMixin is going to create/update an instance get_instance_data
- handles the instance data creation/preaparation.
- """
-
- queryset = None
-
- def get_query_kwargs(self, *args, **kwargs):
- """
- Return a dict of kwargs that will be used to build the
- model instance retrieval or to filter querysets.
- """
-
- kwargs = dict(kwargs)
-
- # If the URLconf includes a .(?P<format>\w+) pattern to match against
- # a .json, .xml suffix, then drop the 'format' kwarg before
- # constructing the query.
- if BaseRenderer._FORMAT_QUERY_PARAM in kwargs:
- del kwargs[BaseRenderer._FORMAT_QUERY_PARAM]
-
- return kwargs
-
- def get_instance_data(self, model, content, **kwargs):
- """
- Returns the dict with the data for model instance creation/update.
-
- Arguments:
- - model: model class (django.db.models.Model subclass) to work with
- - content: a dictionary with instance data
- - kwargs: a dict of URL provided keyword arguments
-
- The create/update queries are created basicly with the contet provided
- with POST/PUT HTML methods and kwargs passed in the URL. This methods
- simply merges the URL data and the content preaparing the ready-to-use
- data dictionary.
- """
-
- tmp = dict(kwargs)
-
- for field in model._meta.fields:
- if isinstance(field, ForeignKey) and field.name in tmp:
- # translate 'related_field' kwargs into 'related_field_id'
- tmp[field.name + '_id'] = tmp[field.name]
- del tmp[field.name]
-
- all_kw_args = dict(content.items() + tmp.items())
-
- return all_kw_args
-
- def get_instance(self, **kwargs):
- """
- Get a model instance for read/update/delete requests.
- """
- return self.get_queryset().get(**kwargs)
-
- def get_queryset(self):
- """
- Return the queryset for this view.
- """
- return getattr(self.resource, 'queryset',
- self.resource.model.objects.all())
-
- def get_ordering(self):
- """
- Return the ordering for this view.
- """
- return getattr(self.resource, 'ordering', None)
-
-
-class ReadModelMixin(ModelMixin):
- """
- Behavior to read a `model` instance on GET requests
- """
- def get(self, request, *args, **kwargs):
- model = self.resource.model
- query_kwargs = self.get_query_kwargs(request, *args, **kwargs)
-
- try:
- self.model_instance = self.get_instance(**query_kwargs)
- except model.DoesNotExist:
- raise ImmediateResponse(status=status.HTTP_404_NOT_FOUND)
-
- return Response(self.model_instance)
-
-
-class CreateModelMixin(ModelMixin):
- """
- Behavior to create a `model` instance on POST requests
- """
- def post(self, request, *args, **kwargs):
- model = self.resource.model
-
- # Copy the dict to keep self.CONTENT intact
- content = dict(self.CONTENT)
- m2m_data = {}
-
- for field in model._meta.many_to_many:
- if field.name in content:
- m2m_data[field.name] = (
- field.m2m_reverse_field_name(), content[field.name]
- )
- del content[field.name]
-
- instance = model(**self.get_instance_data(model, content, *args, **kwargs))
- instance.save()
-
- for fieldname in m2m_data:
- manager = getattr(instance, fieldname)
-
- if hasattr(manager, 'add'):
- manager.add(*m2m_data[fieldname][1])
- else:
- data = {}
- data[manager.source_field_name] = instance
-
- for related_item in m2m_data[fieldname][1]:
- data[m2m_data[fieldname][0]] = related_item
- manager.through(**data).save()
-
- response = Response(instance, status=status.HTTP_201_CREATED)
-
- # Set headers
- if hasattr(self.resource, 'url'):
- response['Location'] = self.resource(self).url(instance)
- return response
-
-
-class UpdateModelMixin(ModelMixin):
- """
- Behavior to update a `model` instance on PUT requests
- """
- def put(self, request, *args, **kwargs):
- model = self.resource.model
- query_kwargs = self.get_query_kwargs(request, *args, **kwargs)
-
- # TODO: update on the url of a non-existing resource url doesn't work
- # correctly at the moment - will end up with a new url
- try:
- self.model_instance = self.get_instance(**query_kwargs)
-
- for (key, val) in self.CONTENT.items():
- setattr(self.model_instance, key, val)
- except model.DoesNotExist:
- self.model_instance = model(**self.get_instance_data(model, self.CONTENT, *args, **kwargs))
- self.model_instance.save()
- return Response(self.model_instance)
-
-
-class DeleteModelMixin(ModelMixin):
- """
- Behavior to delete a `model` instance on DELETE requests
- """
- def delete(self, request, *args, **kwargs):
- model = self.resource.model
- query_kwargs = self.get_query_kwargs(request, *args, **kwargs)
-
- try:
- instance = self.get_instance(**query_kwargs)
- except model.DoesNotExist:
- raise ImmediateResponse(status=status.HTTP_404_NOT_FOUND)
-
- instance.delete()
- return Response()
-
-
-class ListModelMixin(ModelMixin):
- """
- Behavior to list a set of `model` instances on GET requests
- """
-
- def get(self, request, *args, **kwargs):
- queryset = self.get_queryset()
- ordering = self.get_ordering()
- query_kwargs = self.get_query_kwargs(request, *args, **kwargs)
-
- queryset = queryset.filter(**query_kwargs)
- if ordering:
- queryset = queryset.order_by(*ordering)
-
- return Response(queryset)
-
-
-########## Pagination Mixins ##########
-
-class PaginatorMixin(object):
- """
- Adds pagination support to GET requests
- Obviously should only be used on lists :)
-
- A default limit can be set by setting `limit` on the object. This will also
- be used as the maximum if the client sets the `limit` GET param
- """
- limit = 20
-
- def get_limit(self):
- """
- Helper method to determine what the `limit` should be
- """
- try:
- limit = int(self.request.GET.get('limit', self.limit))
- return min(limit, self.limit)
- except ValueError:
- return self.limit
-
- def url_with_page_number(self, page_number):
- """
- Constructs a url used for getting the next/previous urls
- """
- url = URLObject(self.request.get_full_path())
- url = url.set_query_param('page', str(page_number))
-
- limit = self.get_limit()
- if limit != self.limit:
- url = url.set_query_param('limit', str(limit))
-
- return url
-
- def next(self, page):
- """
- Returns a url to the next page of results (if any)
- """
- if not page.has_next():
- return None
-
- return self.url_with_page_number(page.next_page_number())
-
- def previous(self, page):
- """ Returns a url to the previous page of results (if any) """
- if not page.has_previous():
- return None
-
- return self.url_with_page_number(page.previous_page_number())
-
- def serialize_page_info(self, page):
- """
- This is some useful information that is added to the response
- """
- return {
- 'next': self.next(page),
- 'page': page.number,
- 'pages': page.paginator.num_pages,
- 'per_page': self.get_limit(),
- 'previous': self.previous(page),
- 'total': page.paginator.count,
- }
-
- def filter_response(self, obj):
- """
- Given the response content, paginate and then serialize.
-
- The response is modified to include to useful data relating to the number
- of objects, number of pages, next/previous urls etc. etc.
-
- The serialised objects are put into `results` on this new, modified
- response
- """
-
- # We don't want to paginate responses for anything other than GET requests
- if self.request.method.upper() != 'GET':
- return self._resource.filter_response(obj)
-
- paginator = Paginator(obj, self.get_limit())
-
- try:
- page_num = int(self.request.GET.get('page', '1'))
- except ValueError:
- raise ImmediateResponse(
- {'detail': 'That page contains no results'},
- status=status.HTTP_404_NOT_FOUND)
-
- if page_num not in paginator.page_range:
- raise ImmediateResponse(
- {'detail': 'That page contains no results'},
- status=status.HTTP_404_NOT_FOUND)
-
- page = paginator.page(page_num)
-
- serialized_object_list = self._resource.filter_response(page.object_list)
- serialized_page_info = self.serialize_page_info(page)
-
- serialized_page_info['results'] = serialized_object_list
-
- return serialized_page_info
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index 4a2b1b00..ec008bd9 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -18,7 +18,6 @@ __all__ = (
'IsUserOrIsAnonReadOnly',
'PerUserThrottling',
'PerViewThrottling',
- 'PerResourceThrottling'
)
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
@@ -253,16 +252,3 @@ class PerViewThrottling(BaseThrottle):
def get_cache_key(self):
return 'throttle_view_%s' % self.view.__class__.__name__
-
-
-class PerResourceThrottling(BaseThrottle):
- """
- Limits the rate of API calls that may be used against all views on
- a given resource.
-
- The class name of the resource is used as a unique identifier to
- throttle against.
- """
-
- def get_cache_key(self):
- return 'throttle_resource_%s' % self.view.resource.__class__.__name__
diff --git a/djangorestframework/resources.py b/djangorestframework/resources.py
deleted file mode 100644
index 3f2e5a09..00000000
--- a/djangorestframework/resources.py
+++ /dev/null
@@ -1,343 +0,0 @@
-from django import forms
-from djangorestframework.response import ImmediateResponse
-from djangorestframework.serializer import Serializer
-from djangorestframework.utils import as_tuple
-
-
-class BaseResource(Serializer):
- """
- Base class for all Resource classes, which simply defines the interface
- they provide.
- """
- fields = None
- include = None
- exclude = None
-
- def __init__(self, view=None, depth=None, stack=[], **kwargs):
- super(BaseResource, self).__init__(depth, stack, **kwargs)
- self.view = view
- self.request = getattr(view, 'request', None)
-
- def validate_request(self, data, files=None):
- """
- Given the request content return the cleaned, validated content.
- Typically raises a :exc:`response.ImmediateResponse` with status code
- 400 (Bad Request) on failure.
- """
- return data
-
- def filter_response(self, obj):
- """
- Given the response content, filter it into a serializable object.
- """
- return self.serialize(obj)
-
-
-class Resource(BaseResource):
- """
- A Resource determines how a python object maps to some serializable data.
- Objects that a resource can act on include plain Python object instances,
- Django Models, and Django QuerySets.
- """
-
- # The model attribute refers to the Django Model which this Resource maps to.
- # (The Model's class, rather than an instance of the Model)
- model = None
-
- # By default the set of returned fields will be the set of:
- #
- # 0. All the fields on the model, excluding 'id'.
- # 1. All the properties on the model.
- # 2. The absolute_url of the model, if a get_absolute_url method exists for the model.
- #
- # If you wish to override this behaviour,
- # you should explicitly set the fields attribute on your class.
- fields = None
-
-
-class FormResource(Resource):
- """
- Resource class that uses forms for validation.
- Also provides a :meth:`get_bound_form` method which may be used by some renderers.
-
- On calling :meth:`validate_request` this validator may set a :attr:`bound_form_instance` attribute on the
- view, which may be used by some renderers.
- """
-
- form = None
- """
- The :class:`Form` class that should be used for request validation.
- This can be overridden by a :attr:`form` attribute on the :class:`views.View`.
- """
-
- allow_unknown_form_fields = False
- """
- Flag to check for unknown fields when validating a form. If set to false and
- we receive request data that is not expected by the form it raises an
- :exc:`response.ImmediateResponse` with status code 400. If set to true, only
- expected fields are validated.
- """
-
- def validate_request(self, data, files=None):
- """
- Given some content as input return some cleaned, validated content.
- Raises a :exc:`response.ImmediateResponse` 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
- if :attr:`self.allow_unknown_form_fields` is ``False``.
-
- On failure the :exc:`response.ImmediateResponse` content is a dict which may contain :obj:`'errors'` and :obj:`'field-errors'` keys.
- If the :obj:`'errors'` key exists it is a list of strings of non-field errors.
- If the :obj:`'field-errors'` key exists it is a dict of ``{'field name as string': ['errors as strings', ...]}``.
- """
- return self._validate(data, files)
-
- def _validate(self, data, files, allowed_extra_fields=(), fake_data=None):
- """
- Wrapped by validate to hide the extra flags that are used in the implementation.
-
- allowed_extra_fields is a list of fields which are not defined by the form, but which we still
- expect to see on the input.
-
- fake_data is a string that should be used as an extra key, as a kludge to force .errors
- to be populated when an empty dict is supplied in `data`
- """
-
- # We'd like nice error messages even if no content is supplied.
- # Typically if an empty dict is given to a form Django will
- # return .is_valid() == False, but .errors == {}
- #
- # To get around this case we revalidate with some fake data.
- if fake_data:
- data[fake_data] = '_fake_data'
- allowed_extra_fields = tuple(allowed_extra_fields) + ('_fake_data',)
-
- bound_form = self.get_bound_form(data, files)
-
- if bound_form is None:
- return data
-
- self.view.bound_form_instance = bound_form
-
- data = data and data or {}
- files = files and files or {}
-
- seen_fields_set = set(data.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)
- unknown_fields = unknown_fields - set(('csrfmiddlewaretoken', '_accept', '_method')) # TODO: Ugh.
-
- # Check using both regular validation, and our stricter no additional fields rule
- if bound_form.is_valid() and (self.allow_unknown_form_fields or not unknown_fields):
- # Validation succeeded...
- cleaned_data = bound_form.cleaned_data
-
- # 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] = data[key]
-
- return cleaned_data
-
- # Validation failed...
- detail = {}
-
- if not bound_form.errors and not unknown_fields:
- # is_valid() was False, but errors was empty.
- # If we havn't already done so attempt revalidation with some fake data
- # to force django to give us an errors dict.
- if fake_data is None:
- return self._validate(data, files, allowed_extra_fields, '_fake_data')
-
- # If we've already set fake_dict and we're still here, fallback gracefully.
- 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 ImmediateResponse(detail, status=400)
-
- def get_form_class(self, method=None):
- """
- Returns the form class used to validate this resource.
- """
- # A form on the view overrides a form on the resource.
- form = getattr(self.view, 'form', None) or self.form
-
- # Use the requested method or determine the request method
- if method is None and hasattr(self.view, 'request') and hasattr(self.view, 'method'):
- method = self.view.method
- elif method is None and hasattr(self.view, 'request'):
- method = self.view.request.method
-
- # A method form on the view or resource overrides the general case.
- # Method forms are attributes like `get_form` `post_form` `put_form`.
- if method:
- form = getattr(self, '%s_form' % method.lower(), form)
- form = getattr(self.view, '%s_form' % method.lower(), form)
-
- return form
-
- def get_bound_form(self, data=None, files=None, method=None):
- """
- Given some content return a Django form bound to that content.
- If form validation is turned off (:attr:`form` class attribute is :const:`None`) then returns :const:`None`.
- """
- form = self.get_form_class(method)
-
- if not form:
- return None
-
- if data is not None or files is not None:
- return form(data, files)
-
- return form()
-
-
-class ModelResource(FormResource):
- """
- Resource class that uses forms for validation and otherwise falls back to a model form if no form is set.
- Also provides a :meth:`get_bound_form` method which may be used by some renderers.
- """
-
- form = None
- """
- The form class that should be used for request validation.
- If set to :const:`None` then the default model form validation will be used.
-
- This can be overridden by a :attr:`form` attribute on the :class:`views.View`.
- """
-
- model = None
- """
- The model class which this resource maps to.
-
- This can be overridden by a :attr:`model` attribute on the :class:`views.View`.
- """
-
- fields = None
- """
- The list of fields to use on the output.
-
- May be any of:
-
- The name of a model field. To view nested resources, give the field as a tuple of ("fieldName", resource) where `resource` may be any of ModelResource reference, the name of a ModelResourc reference as a string or a tuple of strings representing fields on the nested model.
- The name of an attribute on the model.
- The name of an attribute on the resource.
- The name of a method on the model, with a signature like ``func(self)``.
- The name of a method on the resource, with a signature like ``func(self, instance)``.
- """
-
- exclude = ('id', 'pk')
- """
- The list of fields to exclude. This is only used if :attr:`fields` is not set.
- """
-
- include = ()
- """
- The list of extra fields to include. This is only used if :attr:`fields` is not set.
- """
-
- def __init__(self, view=None, depth=None, stack=[], **kwargs):
- """
- Allow :attr:`form` and :attr:`model` attributes set on the
- :class:`View` to override the :attr:`form` and :attr:`model`
- attributes set on the :class:`Resource`.
- """
- super(ModelResource, self).__init__(view, depth, stack, **kwargs)
-
- self.model = getattr(view, 'model', None) or self.model
-
- def validate_request(self, data, files=None):
- """
- Given some content as input return some cleaned, validated content.
- Raises a :exc:`response.ImmediateResponse` 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 ImmediateResponse content is a dict which may contain :obj:`'errors'` and :obj:`'field-errors'` keys.
- If the :obj:`'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(data, files, allowed_extra_fields=self._property_fields_set)
-
- def get_bound_form(self, data=None, files=None, method=None):
- """
- Given some content return a ``Form`` instance bound to that content.
-
- If the :attr:`form` class attribute has been explicitly set then that class will be used
- to create the Form, otherwise the model will be used to create a ModelForm.
- """
- form = self.get_form_class(method)
-
- if not form and self.model:
- # Fall back to ModelForm which we create on the fly
- class OnTheFlyModelForm(forms.ModelForm):
- class Meta:
- model = self.model
- #fields = tuple(self._model_fields_set)
-
- form = OnTheFlyModelForm
-
- # Both form and model not set? Okay bruv, whatevs...
- if not form:
- return None
-
- # Instantiate the ModelForm as appropriate
- if data is not None or files is not None:
- if issubclass(form, forms.ModelForm) and hasattr(self.view, 'model_instance'):
- # Bound to an existing model instance
- return form(data, files, instance=self.view.model_instance)
- else:
- return form(data, files)
-
- return form()
-
- @property
- def _model_fields_set(self):
- """
- Return a set containing the names of validated fields on the model.
- """
- model_fields = set(field.name for field in self.model._meta.fields)
-
- if self.fields:
- return model_fields & set(as_tuple(self.fields))
-
- return model_fields - set(as_tuple(self.exclude))
-
- @property
- def _property_fields_set(self):
- """
- Returns a set containing the names of validated properties on the model.
- """
- property_fields = set(attr for attr in dir(self.model) if
- isinstance(getattr(self.model, attr, None), property)
- and not attr.startswith('_'))
-
- if self.fields:
- return property_fields & set(as_tuple(self.fields))
-
- return property_fields.union(set(as_tuple(self.include))) - set(as_tuple(self.exclude))
diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py
deleted file mode 100644
index 5dea37e8..00000000
--- a/djangorestframework/serializer.py
+++ /dev/null
@@ -1,283 +0,0 @@
-"""
-Customizable serialization.
-"""
-from django.db import models
-from django.db.models.query import QuerySet
-from django.utils.encoding import smart_unicode, is_protected_type, smart_str
-
-import inspect
-import types
-
-
-# We register serializer classes, so that we can refer to them by their
-# class names, if there are cyclical serialization heirachys.
-_serializers = {}
-
-
-def _field_to_tuple(field):
- """
- Convert an item in the `fields` attribute into a 2-tuple.
- """
- if isinstance(field, (tuple, list)):
- return (field[0], field[1])
- return (field, None)
-
-
-def _fields_to_list(fields):
- """
- Return a list of field tuples.
- """
- return [_field_to_tuple(field) for field in fields or ()]
-
-
-class _SkipField(Exception):
- """
- Signals that a serialized field should be ignored.
- We use this mechanism as the default behavior for ensuring
- that we don't infinitely recurse when dealing with nested data.
- """
- pass
-
-
-class _RegisterSerializer(type):
- """
- Metaclass to register serializers.
- """
- def __new__(cls, name, bases, attrs):
- # Build the class and register it.
- ret = super(_RegisterSerializer, cls).__new__(cls, name, bases, attrs)
- _serializers[name] = ret
- return ret
-
-
-class Serializer(object):
- """
- Converts python objects into plain old native types suitable for
- serialization. In particular it handles models and querysets.
-
- The output format is specified by setting a number of attributes
- on the class.
-
- You may also override any of the serialization methods, to provide
- for more flexible behavior.
-
- Valid output types include anything that may be directly rendered into
- json, xml etc...
- """
- __metaclass__ = _RegisterSerializer
-
- fields = ()
- """
- Specify the fields to be serialized on a model or dict.
- Overrides `include` and `exclude`.
- """
-
- include = ()
- """
- Fields to add to the default set to be serialized on a model/dict.
- """
-
- exclude = ()
- """
- Fields to remove from the default set to be serialized on a model/dict.
- """
-
- rename = {}
- """
- A dict of key->name to use for the field keys.
- """
-
- related_serializer = None
- """
- The default serializer class to use for any related models.
- """
-
- depth = None
- """
- The maximum depth to serialize to, or `None`.
- """
-
- def __init__(self, depth=None, stack=[], **kwargs):
- if depth is not None:
- self.depth = depth
- self.stack = stack
-
- def get_fields(self, obj):
- fields = self.fields
-
- # If `fields` is not set, we use the default fields and modify
- # them with `include` and `exclude`
- if not fields:
- default = self.get_default_fields(obj)
- include = self.include or ()
- exclude = self.exclude or ()
- fields = set(default + list(include)) - set(exclude)
-
- return fields
-
- def get_default_fields(self, obj):
- """
- Return the default list of field names/keys for a model instance/dict.
- These are used if `fields` is not given.
- """
- if isinstance(obj, models.Model):
- opts = obj._meta
- return [field.name for field in opts.fields + opts.many_to_many]
- else:
- return obj.keys()
-
- def get_related_serializer(self, info):
- # If an element in `fields` is a 2-tuple of (str, tuple)
- # then the second element of the tuple is the fields to
- # set on the related serializer
- if isinstance(info, (list, tuple)):
- class OnTheFlySerializer(self.__class__):
- fields = info
- return OnTheFlySerializer
-
- # If an element in `fields` is a 2-tuple of (str, Serializer)
- # then the second element of the tuple is the Serializer
- # class to use for that field.
- elif isinstance(info, type) and issubclass(info, Serializer):
- return info
-
- # If an element in `fields` is a 2-tuple of (str, str)
- # then the second element of the tuple is the name of the Serializer
- # class to use for that field.
- #
- # Black magic to deal with cyclical Serializer dependancies.
- # Similar to what Django does for cyclically related models.
- elif isinstance(info, str) and info in _serializers:
- return _serializers[info]
-
- # Otherwise use `related_serializer` or fall back to `Serializer`
- return getattr(self, 'related_serializer') or Serializer
-
- def serialize_key(self, key):
- """
- Keys serialize to their string value,
- unless they exist in the `rename` dict.
- """
- return self.rename.get(smart_str(key), smart_str(key))
-
- def serialize_val(self, key, obj, related_info):
- """
- Convert a model field or dict value into a serializable representation.
- """
- related_serializer = self.get_related_serializer(related_info)
-
- if self.depth is None:
- depth = None
- elif self.depth <= 0:
- return self.serialize_max_depth(obj)
- else:
- depth = self.depth - 1
-
- if any([obj is elem for elem in self.stack]):
- return self.serialize_recursion(obj)
- else:
- stack = self.stack[:]
- stack.append(obj)
-
- return related_serializer(depth=depth, stack=stack).serialize(obj)
-
- def serialize_max_depth(self, obj):
- """
- Determine how objects should be serialized once `depth` is exceeded.
- The default behavior is to ignore the field.
- """
- raise _SkipField
-
- def serialize_recursion(self, obj):
- """
- Determine how objects should be serialized if recursion occurs.
- The default behavior is to ignore the field.
- """
- raise _SkipField
-
- def serialize_model(self, instance):
- """
- Given a model instance or dict, serialize it to a dict..
- """
- data = {}
-
- fields = self.get_fields(instance)
-
- # serialize each required field
- for fname, related_info in _fields_to_list(fields):
- try:
- # we first check for a method 'fname' on self,
- # 'fname's signature must be 'def fname(self, instance)'
- meth = getattr(self, fname, None)
- if (inspect.ismethod(meth) and
- len(inspect.getargspec(meth)[0]) == 2):
- obj = meth(instance)
- elif hasattr(instance, '__contains__') and fname in instance:
- # then check for a key 'fname' on the instance
- obj = instance[fname]
- elif hasattr(instance, smart_str(fname)):
- # finally check for an attribute 'fname' on the instance
- obj = getattr(instance, fname)
- else:
- continue
-
- key = self.serialize_key(fname)
- val = self.serialize_val(fname, obj, related_info)
- data[key] = val
- except _SkipField:
- pass
-
- return data
-
- def serialize_iter(self, obj):
- """
- Convert iterables into a serializable representation.
- """
- return [self.serialize(item) for item in obj]
-
- def serialize_func(self, obj):
- """
- Convert no-arg methods and functions into a serializable representation.
- """
- return self.serialize(obj())
-
- def serialize_manager(self, obj):
- """
- Convert a model manager into a serializable representation.
- """
- return self.serialize_iter(obj.all())
-
- def serialize_fallback(self, obj):
- """
- Convert any unhandled object into a serializable representation.
- """
- return smart_unicode(obj, strings_only=True)
-
- def serialize(self, obj):
- """
- Convert any object into a serializable representation.
- """
-
- if isinstance(obj, (dict, models.Model)):
- # Model instances & dictionaries
- return self.serialize_model(obj)
- elif isinstance(obj, (tuple, list, set, QuerySet, types.GeneratorType)):
- # basic iterables
- return self.serialize_iter(obj)
- elif isinstance(obj, models.Manager):
- # Manager objects
- return self.serialize_manager(obj)
- elif inspect.isfunction(obj) and not inspect.getargspec(obj)[0]:
- # function with no args
- return self.serialize_func(obj)
- elif inspect.ismethod(obj) and len(inspect.getargspec(obj)[0]) <= 1:
- # bound method
- return self.serialize_func(obj)
-
- # Protected types are passed through as is.
- # (i.e. Primitives like None, numbers, dates, and Decimals.)
- if is_protected_type(obj):
- return obj
-
- # All other values are converted to string.
- return self.serialize_fallback(obj)
diff --git a/djangorestframework/tests/files.py b/djangorestframework/tests/files.py
index bbdff70b..90a613b9 100644
--- a/djangorestframework/tests/files.py
+++ b/djangorestframework/tests/files.py
@@ -1,35 +1,34 @@
-from django.test import TestCase
-from django import forms
+# from django.test import TestCase
+# from django import forms
-from djangorestframework.compat import RequestFactory
-from djangorestframework.views import View
-from djangorestframework.resources import FormResource
-from djangorestframework.response import Response
+# from djangorestframework.compat import RequestFactory
+# from djangorestframework.views import View
+# from djangorestframework.response import Response
-import StringIO
+# import StringIO
-class UploadFilesTests(TestCase):
- """Check uploading of files"""
- def setUp(self):
- self.factory = RequestFactory()
- def test_upload_file(self):
+# class UploadFilesTests(TestCase):
+# """Check uploading of files"""
+# def setUp(self):
+# self.factory = RequestFactory()
- class FileForm(forms.Form):
- file = forms.FileField()
+# def test_upload_file(self):
- class MockView(View):
- permissions = ()
- form = FileForm
+# class FileForm(forms.Form):
+# file = forms.FileField()
- def post(self, request, *args, **kwargs):
- return Response({'FILE_NAME': self.CONTENT['file'].name,
- 'FILE_CONTENT': self.CONTENT['file'].read()})
+# class MockView(View):
+# permissions = ()
+# form = FileForm
- file = StringIO.StringIO('stuff')
- file.name = 'stuff.txt'
- request = self.factory.post('/', {'file': file})
- view = MockView.as_view()
- response = view(request)
- self.assertEquals(response.raw_content, {"FILE_CONTENT": "stuff", "FILE_NAME": "stuff.txt"})
+# def post(self, request, *args, **kwargs):
+# return Response({'FILE_NAME': self.CONTENT['file'].name,
+# 'FILE_CONTENT': self.CONTENT['file'].read()})
+# file = StringIO.StringIO('stuff')
+# file.name = 'stuff.txt'
+# request = self.factory.post('/', {'file': file})
+# view = MockView.as_view()
+# response = view(request)
+# self.assertEquals(response.raw_content, {"FILE_CONTENT": "stuff", "FILE_NAME": "stuff.txt"})
diff --git a/djangorestframework/tests/mixins.py b/djangorestframework/tests/mixins.py
index 25c57bd6..05ce655d 100644
--- a/djangorestframework/tests/mixins.py
+++ b/djangorestframework/tests/mixins.py
@@ -1,286 +1,285 @@
-"""Tests for the mixin module"""
-from django.test import TestCase
-from django.utils import simplejson as json
-from djangorestframework import status
-from djangorestframework.compat import RequestFactory
-from django.contrib.auth.models import Group, User
-from djangorestframework.mixins import CreateModelMixin, PaginatorMixin, ReadModelMixin
-from djangorestframework.resources import ModelResource
-from djangorestframework.response import Response, ImmediateResponse
-from djangorestframework.tests.models import CustomUser
-from djangorestframework.tests.testcases import TestModelsTestCase
-from djangorestframework.views import View
-
-
-class TestModelRead(TestModelsTestCase):
- """Tests on ReadModelMixin"""
-
- def setUp(self):
- super(TestModelRead, self).setUp()
- self.req = RequestFactory()
-
- def test_read(self):
- Group.objects.create(name='other group')
- group = Group.objects.create(name='my group')
-
- class GroupResource(ModelResource):
- model = Group
-
- request = self.req.get('/groups')
- mixin = ReadModelMixin()
- mixin.resource = GroupResource
-
- response = mixin.get(request, id=group.id)
- self.assertEquals(group.name, response.raw_content.name)
-
- def test_read_404(self):
- class GroupResource(ModelResource):
- model = Group
-
- request = self.req.get('/groups')
- mixin = ReadModelMixin()
- mixin.resource = GroupResource
-
- self.assertRaises(ImmediateResponse, mixin.get, request, id=12345)
-
-
-class TestModelCreation(TestModelsTestCase):
- """Tests on CreateModelMixin"""
-
- def setUp(self):
- super(TestModelsTestCase, self).setUp()
- self.req = RequestFactory()
-
- def test_creation(self):
- self.assertEquals(0, Group.objects.count())
-
- class GroupResource(ModelResource):
- model = Group
-
- form_data = {'name': 'foo'}
- request = self.req.post('/groups', data=form_data)
- mixin = CreateModelMixin()
- mixin.resource = GroupResource
- mixin.CONTENT = form_data
-
- response = mixin.post(request)
- self.assertEquals(1, Group.objects.count())
- self.assertEquals('foo', response.raw_content.name)
-
- def test_creation_with_m2m_relation(self):
- class UserResource(ModelResource):
- model = User
-
- def url(self, instance):
- return "/users/%i" % instance.id
-
- group = Group(name='foo')
- group.save()
-
- form_data = {
- 'username': 'bar',
- 'password': 'baz',
- 'groups': [group.id]
- }
- request = self.req.post('/groups', data=form_data)
- cleaned_data = dict(form_data)
- cleaned_data['groups'] = [group]
- mixin = CreateModelMixin()
- mixin.resource = UserResource
- mixin.CONTENT = cleaned_data
-
- response = mixin.post(request)
- self.assertEquals(1, User.objects.count())
- self.assertEquals(1, response.raw_content.groups.count())
- self.assertEquals('foo', response.raw_content.groups.all()[0].name)
-
- def test_creation_with_m2m_relation_through(self):
- """
- Tests creation where the m2m relation uses a through table
- """
- class UserResource(ModelResource):
- model = CustomUser
-
- def url(self, instance):
- return "/customusers/%i" % instance.id
-
- form_data = {'username': 'bar0', 'groups': []}
- request = self.req.post('/groups', data=form_data)
- cleaned_data = dict(form_data)
- cleaned_data['groups'] = []
- mixin = CreateModelMixin()
- mixin.resource = UserResource
- mixin.CONTENT = cleaned_data
+# """Tests for the mixin module"""
+# from django.test import TestCase
+# from djangorestframework import status
+# from djangorestframework.compat import RequestFactory
+# from django.contrib.auth.models import Group, User
+# from djangorestframework.mixins import CreateModelMixin, PaginatorMixin, ReadModelMixin
+# from djangorestframework.resources import ModelResource
+# from djangorestframework.response import Response, ImmediateResponse
+# from djangorestframework.tests.models import CustomUser
+# from djangorestframework.tests.testcases import TestModelsTestCase
+# from djangorestframework.views import View
+
+
+# class TestModelRead(TestModelsTestCase):
+# """Tests on ReadModelMixin"""
+
+# def setUp(self):
+# super(TestModelRead, self).setUp()
+# self.req = RequestFactory()
+
+# def test_read(self):
+# Group.objects.create(name='other group')
+# group = Group.objects.create(name='my group')
+
+# class GroupResource(ModelResource):
+# model = Group
+
+# request = self.req.get('/groups')
+# mixin = ReadModelMixin()
+# mixin.resource = GroupResource
+
+# response = mixin.get(request, id=group.id)
+# self.assertEquals(group.name, response.raw_content.name)
+
+# def test_read_404(self):
+# class GroupResource(ModelResource):
+# model = Group
+
+# request = self.req.get('/groups')
+# mixin = ReadModelMixin()
+# mixin.resource = GroupResource
+
+# self.assertRaises(ImmediateResponse, mixin.get, request, id=12345)
+
+
+# class TestModelCreation(TestModelsTestCase):
+# """Tests on CreateModelMixin"""
+
+# def setUp(self):
+# super(TestModelsTestCase, self).setUp()
+# self.req = RequestFactory()
+
+# def test_creation(self):
+# self.assertEquals(0, Group.objects.count())
+
+# class GroupResource(ModelResource):
+# model = Group
+
+# form_data = {'name': 'foo'}
+# request = self.req.post('/groups', data=form_data)
+# mixin = CreateModelMixin()
+# mixin.resource = GroupResource
+# mixin.CONTENT = form_data
+
+# response = mixin.post(request)
+# self.assertEquals(1, Group.objects.count())
+# self.assertEquals('foo', response.raw_content.name)
+
+# def test_creation_with_m2m_relation(self):
+# class UserResource(ModelResource):
+# model = User
+
+# def url(self, instance):
+# return "/users/%i" % instance.id
+
+# group = Group(name='foo')
+# group.save()
+
+# form_data = {
+# 'username': 'bar',
+# 'password': 'baz',
+# 'groups': [group.id]
+# }
+# request = self.req.post('/groups', data=form_data)
+# cleaned_data = dict(form_data)
+# cleaned_data['groups'] = [group]
+# mixin = CreateModelMixin()
+# mixin.resource = UserResource
+# mixin.CONTENT = cleaned_data
+
+# response = mixin.post(request)
+# self.assertEquals(1, User.objects.count())
+# self.assertEquals(1, response.raw_content.groups.count())
+# self.assertEquals('foo', response.raw_content.groups.all()[0].name)
+
+# def test_creation_with_m2m_relation_through(self):
+# """
+# Tests creation where the m2m relation uses a through table
+# """
+# class UserResource(ModelResource):
+# model = CustomUser
+
+# def url(self, instance):
+# return "/customusers/%i" % instance.id
+
+# form_data = {'username': 'bar0', 'groups': []}
+# request = self.req.post('/groups', data=form_data)
+# cleaned_data = dict(form_data)
+# cleaned_data['groups'] = []
+# mixin = CreateModelMixin()
+# mixin.resource = UserResource
+# mixin.CONTENT = cleaned_data
- response = mixin.post(request)
- self.assertEquals(1, CustomUser.objects.count())
- self.assertEquals(0, response.raw_content.groups.count())
+# response = mixin.post(request)
+# self.assertEquals(1, CustomUser.objects.count())
+# self.assertEquals(0, response.raw_content.groups.count())
- group = Group(name='foo1')
- group.save()
+# group = Group(name='foo1')
+# group.save()
- form_data = {'username': 'bar1', 'groups': [group.id]}
- request = self.req.post('/groups', data=form_data)
- cleaned_data = dict(form_data)
- cleaned_data['groups'] = [group]
- mixin = CreateModelMixin()
- mixin.resource = UserResource
- mixin.CONTENT = cleaned_data
-
- response = mixin.post(request)
- self.assertEquals(2, CustomUser.objects.count())
- self.assertEquals(1, response.raw_content.groups.count())
- self.assertEquals('foo1', response.raw_content.groups.all()[0].name)
-
- group2 = Group(name='foo2')
- group2.save()
-
- form_data = {'username': 'bar2', 'groups': [group.id, group2.id]}
- request = self.req.post('/groups', data=form_data)
- cleaned_data = dict(form_data)
- cleaned_data['groups'] = [group, group2]
- mixin = CreateModelMixin()
- mixin.resource = UserResource
- mixin.CONTENT = cleaned_data
-
- response = mixin.post(request)
- self.assertEquals(3, CustomUser.objects.count())
- self.assertEquals(2, response.raw_content.groups.count())
- self.assertEquals('foo1', response.raw_content.groups.all()[0].name)
- self.assertEquals('foo2', response.raw_content.groups.all()[1].name)
-
-
-class MockPaginatorView(PaginatorMixin, View):
- total = 60
-
- def get(self, request):
- return Response(range(0, self.total))
-
- def post(self, request):
- return Response({'status': 'OK'}, status=status.HTTP_201_CREATED)
-
-
-class TestPagination(TestCase):
- def setUp(self):
- self.req = RequestFactory()
-
- def test_default_limit(self):
- """ Tests if pagination works without overwriting the limit """
- request = self.req.get('/paginator')
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
-
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(MockPaginatorView.total, content['total'])
- self.assertEqual(MockPaginatorView.limit, content['per_page'])
-
- self.assertEqual(range(0, MockPaginatorView.limit), content['results'])
-
- def test_overwriting_limit(self):
- """ Tests if the limit can be overwritten """
- limit = 10
-
- request = self.req.get('/paginator')
- response = MockPaginatorView.as_view(limit=limit)(request)
- content = response.raw_content
-
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(content['per_page'], limit)
-
- self.assertEqual(range(0, limit), content['results'])
-
- def test_limit_param(self):
- """ Tests if the client can set the limit """
- from math import ceil
-
- limit = 5
- num_pages = int(ceil(MockPaginatorView.total / float(limit)))
-
- request = self.req.get('/paginator/?limit=%d' % limit)
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
-
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(MockPaginatorView.total, content['total'])
- self.assertEqual(limit, content['per_page'])
- self.assertEqual(num_pages, content['pages'])
-
- def test_exceeding_limit(self):
- """ Makes sure the client cannot exceed the default limit """
- from math import ceil
-
- limit = MockPaginatorView.limit + 10
- num_pages = int(ceil(MockPaginatorView.total / float(limit)))
-
- request = self.req.get('/paginator/?limit=%d' % limit)
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
-
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(MockPaginatorView.total, content['total'])
- self.assertNotEqual(limit, content['per_page'])
- self.assertNotEqual(num_pages, content['pages'])
- self.assertEqual(MockPaginatorView.limit, content['per_page'])
-
- def test_only_works_for_get(self):
- """ Pagination should only work for GET requests """
- request = self.req.post('/paginator', data={'content': 'spam'})
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
-
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(None, content.get('per_page'))
- self.assertEqual('OK', content['status'])
-
- def test_non_int_page(self):
- """ Tests that it can handle invalid values """
- request = self.req.get('/paginator/?page=spam')
- response = MockPaginatorView.as_view()(request)
-
- self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
-
- def test_page_range(self):
- """ Tests that the page range is handle correctly """
- request = self.req.get('/paginator/?page=0')
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
- self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
-
- request = self.req.get('/paginator/')
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(range(0, MockPaginatorView.limit), content['results'])
-
- num_pages = content['pages']
-
- request = self.req.get('/paginator/?page=%d' % num_pages)
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(range(MockPaginatorView.limit*(num_pages-1), MockPaginatorView.total), content['results'])
-
- request = self.req.get('/paginator/?page=%d' % (num_pages + 1,))
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
- self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
-
- def test_existing_query_parameters_are_preserved(self):
- """ Tests that existing query parameters are preserved when
- generating next/previous page links """
- request = self.req.get('/paginator/?foo=bar&another=something')
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertTrue('foo=bar' in content['next'])
- self.assertTrue('another=something' in content['next'])
- self.assertTrue('page=2' in content['next'])
-
- def test_duplicate_parameters_are_not_created(self):
- """ Regression: ensure duplicate "page" parameters are not added to
- paginated URLs. So page 1 should contain ?page=2, not ?page=1&page=2 """
- request = self.req.get('/paginator/?page=1')
- response = MockPaginatorView.as_view()(request)
- content = response.raw_content
- self.assertTrue('page=2' in content['next'])
- self.assertFalse('page=1' in content['next'])
+# form_data = {'username': 'bar1', 'groups': [group.id]}
+# request = self.req.post('/groups', data=form_data)
+# cleaned_data = dict(form_data)
+# cleaned_data['groups'] = [group]
+# mixin = CreateModelMixin()
+# mixin.resource = UserResource
+# mixin.CONTENT = cleaned_data
+
+# response = mixin.post(request)
+# self.assertEquals(2, CustomUser.objects.count())
+# self.assertEquals(1, response.raw_content.groups.count())
+# self.assertEquals('foo1', response.raw_content.groups.all()[0].name)
+
+# group2 = Group(name='foo2')
+# group2.save()
+
+# form_data = {'username': 'bar2', 'groups': [group.id, group2.id]}
+# request = self.req.post('/groups', data=form_data)
+# cleaned_data = dict(form_data)
+# cleaned_data['groups'] = [group, group2]
+# mixin = CreateModelMixin()
+# mixin.resource = UserResource
+# mixin.CONTENT = cleaned_data
+
+# response = mixin.post(request)
+# self.assertEquals(3, CustomUser.objects.count())
+# self.assertEquals(2, response.raw_content.groups.count())
+# self.assertEquals('foo1', response.raw_content.groups.all()[0].name)
+# self.assertEquals('foo2', response.raw_content.groups.all()[1].name)
+
+
+# class MockPaginatorView(PaginatorMixin, View):
+# total = 60
+
+# def get(self, request):
+# return Response(range(0, self.total))
+
+# def post(self, request):
+# return Response({'status': 'OK'}, status=status.HTTP_201_CREATED)
+
+
+# class TestPagination(TestCase):
+# def setUp(self):
+# self.req = RequestFactory()
+
+# def test_default_limit(self):
+# """ Tests if pagination works without overwriting the limit """
+# request = self.req.get('/paginator')
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(MockPaginatorView.total, content['total'])
+# self.assertEqual(MockPaginatorView.limit, content['per_page'])
+
+# self.assertEqual(range(0, MockPaginatorView.limit), content['results'])
+
+# def test_overwriting_limit(self):
+# """ Tests if the limit can be overwritten """
+# limit = 10
+
+# request = self.req.get('/paginator')
+# response = MockPaginatorView.as_view(limit=limit)(request)
+# content = response.raw_content
+
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(content['per_page'], limit)
+
+# self.assertEqual(range(0, limit), content['results'])
+
+# def test_limit_param(self):
+# """ Tests if the client can set the limit """
+# from math import ceil
+
+# limit = 5
+# num_pages = int(ceil(MockPaginatorView.total / float(limit)))
+
+# request = self.req.get('/paginator/?limit=%d' % limit)
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(MockPaginatorView.total, content['total'])
+# self.assertEqual(limit, content['per_page'])
+# self.assertEqual(num_pages, content['pages'])
+
+# def test_exceeding_limit(self):
+# """ Makes sure the client cannot exceed the default limit """
+# from math import ceil
+
+# limit = MockPaginatorView.limit + 10
+# num_pages = int(ceil(MockPaginatorView.total / float(limit)))
+
+# request = self.req.get('/paginator/?limit=%d' % limit)
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(MockPaginatorView.total, content['total'])
+# self.assertNotEqual(limit, content['per_page'])
+# self.assertNotEqual(num_pages, content['pages'])
+# self.assertEqual(MockPaginatorView.limit, content['per_page'])
+
+# def test_only_works_for_get(self):
+# """ Pagination should only work for GET requests """
+# request = self.req.post('/paginator', data={'content': 'spam'})
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+
+# self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+# self.assertEqual(None, content.get('per_page'))
+# self.assertEqual('OK', content['status'])
+
+# def test_non_int_page(self):
+# """ Tests that it can handle invalid values """
+# request = self.req.get('/paginator/?page=spam')
+# response = MockPaginatorView.as_view()(request)
+
+# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+# def test_page_range(self):
+# """ Tests that the page range is handle correctly """
+# request = self.req.get('/paginator/?page=0')
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+# request = self.req.get('/paginator/')
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(range(0, MockPaginatorView.limit), content['results'])
+
+# num_pages = content['pages']
+
+# request = self.req.get('/paginator/?page=%d' % num_pages)
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(range(MockPaginatorView.limit*(num_pages-1), MockPaginatorView.total), content['results'])
+
+# request = self.req.get('/paginator/?page=%d' % (num_pages + 1,))
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+# def test_existing_query_parameters_are_preserved(self):
+# """ Tests that existing query parameters are preserved when
+# generating next/previous page links """
+# request = self.req.get('/paginator/?foo=bar&another=something')
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertTrue('foo=bar' in content['next'])
+# self.assertTrue('another=something' in content['next'])
+# self.assertTrue('page=2' in content['next'])
+
+# def test_duplicate_parameters_are_not_created(self):
+# """ Regression: ensure duplicate "page" parameters are not added to
+# paginated URLs. So page 1 should contain ?page=2, not ?page=1&page=2 """
+# request = self.req.get('/paginator/?page=1')
+# response = MockPaginatorView.as_view()(request)
+# content = response.raw_content
+# self.assertTrue('page=2' in content['next'])
+# self.assertFalse('page=1' in content['next'])
diff --git a/djangorestframework/tests/modelviews.py b/djangorestframework/tests/modelviews.py
index ccd8513f..73cb0b2b 100644
--- a/djangorestframework/tests/modelviews.py
+++ b/djangorestframework/tests/modelviews.py
@@ -1,90 +1,90 @@
-from django.conf.urls.defaults import patterns, url
-from django.forms import ModelForm
-from django.contrib.auth.models import Group, User
-from djangorestframework.resources import ModelResource
-from djangorestframework.views import ListOrCreateModelView, InstanceModelView
-from djangorestframework.tests.models import CustomUser
-from djangorestframework.tests.testcases import TestModelsTestCase
+# from django.conf.urls.defaults import patterns, url
+# from django.forms import ModelForm
+# from django.contrib.auth.models import Group, User
+# from djangorestframework.resources import ModelResource
+# from djangorestframework.views import ListOrCreateModelView, InstanceModelView
+# from djangorestframework.tests.models import CustomUser
+# from djangorestframework.tests.testcases import TestModelsTestCase
-class GroupResource(ModelResource):
- model = Group
+# class GroupResource(ModelResource):
+# model = Group
-class UserForm(ModelForm):
- class Meta:
- model = User
- exclude = ('last_login', 'date_joined')
+# class UserForm(ModelForm):
+# class Meta:
+# model = User
+# exclude = ('last_login', 'date_joined')
-class UserResource(ModelResource):
- model = User
- form = UserForm
+# class UserResource(ModelResource):
+# model = User
+# form = UserForm
-class CustomUserResource(ModelResource):
- model = CustomUser
+# class CustomUserResource(ModelResource):
+# model = CustomUser
-urlpatterns = patterns('',
- url(r'^users/$', ListOrCreateModelView.as_view(resource=UserResource), name='users'),
- url(r'^users/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=UserResource)),
- url(r'^customusers/$', ListOrCreateModelView.as_view(resource=CustomUserResource), name='customusers'),
- url(r'^customusers/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=CustomUserResource)),
- url(r'^groups/$', ListOrCreateModelView.as_view(resource=GroupResource), name='groups'),
- url(r'^groups/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=GroupResource)),
-)
+# urlpatterns = patterns('',
+# url(r'^users/$', ListOrCreateModelView.as_view(resource=UserResource), name='users'),
+# url(r'^users/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=UserResource)),
+# url(r'^customusers/$', ListOrCreateModelView.as_view(resource=CustomUserResource), name='customusers'),
+# url(r'^customusers/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=CustomUserResource)),
+# url(r'^groups/$', ListOrCreateModelView.as_view(resource=GroupResource), name='groups'),
+# url(r'^groups/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=GroupResource)),
+# )
-class ModelViewTests(TestModelsTestCase):
- """Test the model views djangorestframework provides"""
- urls = 'djangorestframework.tests.modelviews'
+# class ModelViewTests(TestModelsTestCase):
+# """Test the model views djangorestframework provides"""
+# urls = 'djangorestframework.tests.modelviews'
- def test_creation(self):
- """Ensure that a model object can be created"""
- self.assertEqual(0, Group.objects.count())
+# def test_creation(self):
+# """Ensure that a model object can be created"""
+# self.assertEqual(0, Group.objects.count())
- response = self.client.post('/groups/', {'name': 'foo'})
+# response = self.client.post('/groups/', {'name': 'foo'})
- self.assertEqual(response.status_code, 201)
- self.assertEqual(1, Group.objects.count())
- self.assertEqual('foo', Group.objects.all()[0].name)
+# self.assertEqual(response.status_code, 201)
+# self.assertEqual(1, Group.objects.count())
+# self.assertEqual('foo', Group.objects.all()[0].name)
- def test_creation_with_m2m_relation(self):
- """Ensure that a model object with a m2m relation can be created"""
- group = Group(name='foo')
- group.save()
- self.assertEqual(0, User.objects.count())
+# def test_creation_with_m2m_relation(self):
+# """Ensure that a model object with a m2m relation can be created"""
+# group = Group(name='foo')
+# group.save()
+# self.assertEqual(0, User.objects.count())
- response = self.client.post('/users/', {'username': 'bar', 'password': 'baz', 'groups': [group.id]})
+# response = self.client.post('/users/', {'username': 'bar', 'password': 'baz', 'groups': [group.id]})
- self.assertEqual(response.status_code, 201)
- self.assertEqual(1, User.objects.count())
+# self.assertEqual(response.status_code, 201)
+# self.assertEqual(1, User.objects.count())
- user = User.objects.all()[0]
- self.assertEqual('bar', user.username)
- self.assertEqual('baz', user.password)
- self.assertEqual(1, user.groups.count())
+# user = User.objects.all()[0]
+# self.assertEqual('bar', user.username)
+# self.assertEqual('baz', user.password)
+# self.assertEqual(1, user.groups.count())
- group = user.groups.all()[0]
- self.assertEqual('foo', group.name)
+# group = user.groups.all()[0]
+# self.assertEqual('foo', group.name)
- def test_creation_with_m2m_relation_through(self):
- """
- Ensure that a model object with a m2m relation can be created where that
- relation uses a through table
- """
- group = Group(name='foo')
- group.save()
- self.assertEqual(0, User.objects.count())
+# def test_creation_with_m2m_relation_through(self):
+# """
+# Ensure that a model object with a m2m relation can be created where that
+# relation uses a through table
+# """
+# group = Group(name='foo')
+# group.save()
+# self.assertEqual(0, User.objects.count())
- response = self.client.post('/customusers/', {'username': 'bar', 'groups': [group.id]})
+# response = self.client.post('/customusers/', {'username': 'bar', 'groups': [group.id]})
- self.assertEqual(response.status_code, 201)
- self.assertEqual(1, CustomUser.objects.count())
+# self.assertEqual(response.status_code, 201)
+# self.assertEqual(1, CustomUser.objects.count())
- user = CustomUser.objects.all()[0]
- self.assertEqual('bar', user.username)
- self.assertEqual(1, user.groups.count())
+# user = CustomUser.objects.all()[0]
+# self.assertEqual('bar', user.username)
+# self.assertEqual(1, user.groups.count())
- group = user.groups.all()[0]
- self.assertEqual('foo', group.name)
+# group = user.groups.all()[0]
+# self.assertEqual('foo', group.name)
diff --git a/djangorestframework/tests/serializer.py b/djangorestframework/tests/serializer.py
index 834a60d0..7e9f4149 100644
--- a/djangorestframework/tests/serializer.py
+++ b/djangorestframework/tests/serializer.py
@@ -1,160 +1,161 @@
-"""Tests for the resource module"""
-from django.db import models
-from django.test import TestCase
-from django.utils.translation import ugettext_lazy
-from djangorestframework.serializer import Serializer
+# """Tests for the resource module"""
+# from django.db import models
+# from django.test import TestCase
+# from django.utils.translation import ugettext_lazy
+# from djangorestframework.serializer import Serializer
-import datetime
-import decimal
-
-class TestObjectToData(TestCase):
- """
- Tests for the Serializer class.
- """
-
- def setUp(self):
- self.serializer = Serializer()
- self.serialize = self.serializer.serialize
-
- def test_decimal(self):
- """Decimals need to be converted to a string representation."""
- self.assertEquals(self.serialize(decimal.Decimal('1.5')), decimal.Decimal('1.5'))
-
- def test_function(self):
- """Functions with no arguments should be called."""
- def foo():
- return 1
- self.assertEquals(self.serialize(foo), 1)
-
- def test_method(self):
- """Methods with only a ``self`` argument should be called."""
- class Foo(object):
- def foo(self):
- return 1
- self.assertEquals(self.serialize(Foo().foo), 1)
-
- def test_datetime(self):
- """datetime objects are left as-is."""
- now = datetime.datetime.now()
- self.assertEquals(self.serialize(now), now)
-
- def test_dict_method_name_collision(self):
- """dict with key that collides with dict method name"""
- self.assertEquals(self.serialize({'items': 'foo'}), {'items': u'foo'})
- self.assertEquals(self.serialize({'keys': 'foo'}), {'keys': u'foo'})
- self.assertEquals(self.serialize({'values': 'foo'}), {'values': u'foo'})
+# import datetime
+# import decimal
- def test_ugettext_lazy(self):
- self.assertEquals(self.serialize(ugettext_lazy('foobar')), u'foobar')
-
-
-class TestFieldNesting(TestCase):
- """
- Test nesting the fields in the Serializer class
- """
- def setUp(self):
- self.serializer = Serializer()
- self.serialize = self.serializer.serialize
-
- class M1(models.Model):
- field1 = models.CharField(max_length=256)
- field2 = models.CharField(max_length=256)
-
- class M2(models.Model):
- field = models.OneToOneField(M1)
-
- class M3(models.Model):
- field = models.ForeignKey(M1)
-
- self.m1 = M1(field1='foo', field2='bar')
- self.m2 = M2(field=self.m1)
- self.m3 = M3(field=self.m1)
-
-
- def test_tuple_nesting(self):
- """
- Test tuple nesting on `fields` attr
- """
- class SerializerM2(Serializer):
- fields = (('field', ('field1',)),)
-
- class SerializerM3(Serializer):
- fields = (('field', ('field2',)),)
-
- self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
- self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
-
-
- def test_serializer_class_nesting(self):
- """
- Test related model serialization
- """
- class NestedM2(Serializer):
- fields = ('field1', )
-
- class NestedM3(Serializer):
- fields = ('field2', )
-
- class SerializerM2(Serializer):
- fields = [('field', NestedM2)]
-
- class SerializerM3(Serializer):
- fields = [('field', NestedM3)]
-
- self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
- self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
- def test_serializer_no_fields(self):
- """
- Test related serializer works when the fields attr isn't present. Fix for
- #178.
- """
- class NestedM2(Serializer):
- fields = ('field1', )
+# class TestObjectToData(TestCase):
+# """
+# Tests for the Serializer class.
+# """
- class NestedM3(Serializer):
- fields = ('field2', )
-
- class SerializerM2(Serializer):
- include = [('field', NestedM2)]
- exclude = ('id', )
-
- class SerializerM3(Serializer):
- fields = [('field', NestedM3)]
-
- self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
- self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
+# def setUp(self):
+# self.serializer = Serializer()
+# self.serialize = self.serializer.serialize
- def test_serializer_classname_nesting(self):
- """
- Test related model serialization
- """
- class SerializerM2(Serializer):
- fields = [('field', 'NestedM2')]
+# def test_decimal(self):
+# """Decimals need to be converted to a string representation."""
+# self.assertEquals(self.serialize(decimal.Decimal('1.5')), decimal.Decimal('1.5'))
- class SerializerM3(Serializer):
- fields = [('field', 'NestedM3')]
+# def test_function(self):
+# """Functions with no arguments should be called."""
+# def foo():
+# return 1
+# self.assertEquals(self.serialize(foo), 1)
- class NestedM2(Serializer):
- fields = ('field1', )
+# def test_method(self):
+# """Methods with only a ``self`` argument should be called."""
+# class Foo(object):
+# def foo(self):
+# return 1
+# self.assertEquals(self.serialize(Foo().foo), 1)
+
+# def test_datetime(self):
+# """datetime objects are left as-is."""
+# now = datetime.datetime.now()
+# self.assertEquals(self.serialize(now), now)
- class NestedM3(Serializer):
- fields = ('field2', )
+# def test_dict_method_name_collision(self):
+# """dict with key that collides with dict method name"""
+# self.assertEquals(self.serialize({'items': 'foo'}), {'items': u'foo'})
+# self.assertEquals(self.serialize({'keys': 'foo'}), {'keys': u'foo'})
+# self.assertEquals(self.serialize({'values': 'foo'}), {'values': u'foo'})
- self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
- self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
+# def test_ugettext_lazy(self):
+# self.assertEquals(self.serialize(ugettext_lazy('foobar')), u'foobar')
- def test_serializer_overridden_hook_method(self):
- """
- Test serializing a model instance which overrides a class method on the
- serializer. Checks for correct behaviour in odd edge case.
- """
- class SerializerM2(Serializer):
- fields = ('overridden', )
- def overridden(self):
- return False
+# class TestFieldNesting(TestCase):
+# """
+# Test nesting the fields in the Serializer class
+# """
+# def setUp(self):
+# self.serializer = Serializer()
+# self.serialize = self.serializer.serialize
- self.m2.overridden = True
- self.assertEqual(SerializerM2().serialize_model(self.m2),
- {'overridden': True})
+# class M1(models.Model):
+# field1 = models.CharField(max_length=256)
+# field2 = models.CharField(max_length=256)
+
+# class M2(models.Model):
+# field = models.OneToOneField(M1)
+
+# class M3(models.Model):
+# field = models.ForeignKey(M1)
+
+# self.m1 = M1(field1='foo', field2='bar')
+# self.m2 = M2(field=self.m1)
+# self.m3 = M3(field=self.m1)
+
+
+# def test_tuple_nesting(self):
+# """
+# Test tuple nesting on `fields` attr
+# """
+# class SerializerM2(Serializer):
+# fields = (('field', ('field1',)),)
+
+# class SerializerM3(Serializer):
+# fields = (('field', ('field2',)),)
+
+# self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
+# self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
+
+
+# def test_serializer_class_nesting(self):
+# """
+# Test related model serialization
+# """
+# class NestedM2(Serializer):
+# fields = ('field1', )
+
+# class NestedM3(Serializer):
+# fields = ('field2', )
+
+# class SerializerM2(Serializer):
+# fields = [('field', NestedM2)]
+
+# class SerializerM3(Serializer):
+# fields = [('field', NestedM3)]
+
+# self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
+# self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
+
+# def test_serializer_no_fields(self):
+# """
+# Test related serializer works when the fields attr isn't present. Fix for
+# #178.
+# """
+# class NestedM2(Serializer):
+# fields = ('field1', )
+
+# class NestedM3(Serializer):
+# fields = ('field2', )
+
+# class SerializerM2(Serializer):
+# include = [('field', NestedM2)]
+# exclude = ('id', )
+
+# class SerializerM3(Serializer):
+# fields = [('field', NestedM3)]
+
+# self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
+# self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
+
+# def test_serializer_classname_nesting(self):
+# """
+# Test related model serialization
+# """
+# class SerializerM2(Serializer):
+# fields = [('field', 'NestedM2')]
+
+# class SerializerM3(Serializer):
+# fields = [('field', 'NestedM3')]
+
+# class NestedM2(Serializer):
+# fields = ('field1', )
+
+# class NestedM3(Serializer):
+# fields = ('field2', )
+
+# self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
+# self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
+
+# def test_serializer_overridden_hook_method(self):
+# """
+# Test serializing a model instance which overrides a class method on the
+# serializer. Checks for correct behaviour in odd edge case.
+# """
+# class SerializerM2(Serializer):
+# fields = ('overridden', )
+
+# def overridden(self):
+# return False
+
+# self.m2.overridden = True
+# self.assertEqual(SerializerM2().serialize_model(self.m2),
+# {'overridden': True})
diff --git a/djangorestframework/tests/throttling.py b/djangorestframework/tests/throttling.py
index 8c5457d3..d307cd32 100644
--- a/djangorestframework/tests/throttling.py
+++ b/djangorestframework/tests/throttling.py
@@ -8,8 +8,7 @@ from django.core.cache import cache
from djangorestframework.compat import RequestFactory
from djangorestframework.views import View
-from djangorestframework.permissions import PerUserThrottling, PerViewThrottling, PerResourceThrottling
-from djangorestframework.resources import FormResource
+from djangorestframework.permissions import PerUserThrottling, PerViewThrottling
from djangorestframework.response import Response
@@ -25,11 +24,6 @@ class MockView_PerViewThrottling(MockView):
permission_classes = (PerViewThrottling,)
-class MockView_PerResourceThrottling(MockView):
- permission_classes = (PerResourceThrottling,)
- resource = FormResource
-
-
class MockView_MinuteThrottling(MockView):
throttle = '3/min'
@@ -98,12 +92,6 @@ class ThrottlingTests(TestCase):
"""
self.ensure_is_throttled(MockView_PerViewThrottling, 503)
- def test_request_throttling_is_per_resource(self):
- """
- Ensure request rate is limited globally per Resource for PerResourceThrottles
- """
- self.ensure_is_throttled(MockView_PerResourceThrottling, 503)
-
def ensure_response_header_contains_proper_throttle_field(self, view, expected_headers):
"""
Ensure the response returns an X-Throttle field with status and next attributes
diff --git a/djangorestframework/tests/validators.py b/djangorestframework/tests/validators.py
index bf2bf8b7..80ad2b17 100644
--- a/djangorestframework/tests/validators.py
+++ b/djangorestframework/tests/validators.py
@@ -1,330 +1,329 @@
-from django import forms
-from django.db import models
-from django.test import TestCase
-from djangorestframework.resources import FormResource, ModelResource
-from djangorestframework.response import ImmediateResponse
-from djangorestframework.views import View
+# from django import forms
+# from django.db import models
+# from django.test import TestCase
+# from djangorestframework.response import ImmediateResponse
+# from djangorestframework.views import View
-class TestDisabledValidations(TestCase):
- """Tests on FormValidator with validation disabled by setting form to None"""
-
- def test_disabled_form_validator_returns_content_unchanged(self):
- """If the view's form attribute is None then FormValidator(view).validate_request(content, None)
- should just return the content unmodified."""
- class DisabledFormResource(FormResource):
- form = None
-
- class MockView(View):
- resource = DisabledFormResource
-
- view = MockView()
- content = {'qwerty': 'uiop'}
- self.assertEqual(FormResource(view).validate_request(content, None), content)
-
- def test_disabled_form_validator_get_bound_form_returns_none(self):
- """If the view's form attribute is None on then
- FormValidator(view).get_bound_form(content) should just return None."""
- class DisabledFormResource(FormResource):
- form = None
-
- class MockView(View):
- resource = DisabledFormResource
-
- view = MockView()
- content = {'qwerty': 'uiop'}
- self.assertEqual(FormResource(view).get_bound_form(content), None)
-
- def test_disabled_model_form_validator_returns_content_unchanged(self):
- """If the view's form is None and does not have a Resource with a model set then
- ModelFormValidator(view).validate_request(content, None) should just return the content unmodified."""
-
- class DisabledModelFormView(View):
- resource = ModelResource
-
- view = DisabledModelFormView()
- content = {'qwerty': 'uiop'}
- self.assertEqual(ModelResource(view).get_bound_form(content), None)
-
- def test_disabled_model_form_validator_get_bound_form_returns_none(self):
- """If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None."""
- class DisabledModelFormView(View):
- resource = ModelResource
-
- view = DisabledModelFormView()
- content = {'qwerty': 'uiop'}
- self.assertEqual(ModelResource(view).get_bound_form(content), None)
-
-
-class TestNonFieldErrors(TestCase):
- """Tests against form validation errors caused by non-field errors. (eg as might be caused by some custom form validation)"""
-
- def test_validate_failed_due_to_non_field_error_returns_appropriate_message(self):
- """If validation fails with a non-field error, ensure the response a non-field error"""
- class MockForm(forms.Form):
- field1 = forms.CharField(required=False)
- field2 = forms.CharField(required=False)
- ERROR_TEXT = 'You may not supply both field1 and field2'
-
- def clean(self):
- if 'field1' in self.cleaned_data and 'field2' in self.cleaned_data:
- raise forms.ValidationError(self.ERROR_TEXT)
- return self.cleaned_data
-
- class MockResource(FormResource):
- form = MockForm
-
- class MockView(View):
- pass
-
- view = MockView()
- content = {'field1': 'example1', 'field2': 'example2'}
- try:
- MockResource(view).validate_request(content, None)
- except ImmediateResponse, exc:
- response = exc.response
- self.assertEqual(response.raw_content, {'errors': [MockForm.ERROR_TEXT]})
- else:
- self.fail('ImmediateResponse was not raised')
-
-
-class TestFormValidation(TestCase):
- """Tests which check basic form validation.
- Also includes the same set of tests with a ModelFormValidator for which the form has been explicitly set.
- (ModelFormValidator should behave as FormValidator if a form is set rather than relying on the default ModelForm)"""
- def setUp(self):
- class MockForm(forms.Form):
- qwerty = forms.CharField(required=True)
-
- class MockFormResource(FormResource):
- form = MockForm
-
- class MockModelResource(ModelResource):
- form = MockForm
-
- class MockFormView(View):
- resource = MockFormResource
-
- class MockModelFormView(View):
- resource = MockModelResource
-
- self.MockFormResource = MockFormResource
- self.MockModelResource = MockModelResource
- self.MockFormView = MockFormView
- self.MockModelFormView = MockModelFormView
-
- def validation_returns_content_unchanged_if_already_valid_and_clean(self, validator):
- """If the content is already valid and clean then validate(content) should just return the content unmodified."""
- content = {'qwerty': 'uiop'}
- self.assertEqual(validator.validate_request(content, None), content)
-
- def validation_failure_raises_response_exception(self, validator):
- """If form validation fails a ResourceException 400 (Bad Request) should be raised."""
- content = {}
- self.assertRaises(ImmediateResponse, validator.validate_request, content, None)
-
- 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(ImmediateResponse, validator.validate_request, content, None)
-
- 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."""
- content = {'qwerty': 'uiop', 'extra': 'extra'}
- validator._validate(content, None, allowed_extra_fields=('extra',))
-
- def validation_allows_unknown_fields_if_explicitly_allowed(self, validator):
- """If we set ``unknown_form_fields`` on the form resource, then don't
- raise errors on unexpected request data"""
- content = {'qwerty': 'uiop', 'extra': 'extra'}
- validator.allow_unknown_form_fields = True
- self.assertEqual({'qwerty': u'uiop'},
- validator.validate_request(content, None),
- "Resource didn't accept unknown fields.")
- validator.allow_unknown_form_fields = False
-
- def validation_does_not_require_extra_fields_if_explicitly_set(self, validator):
- """If we include an allowed_extra_fields paramater on _validate, then do not fail if we do not have fields with those names."""
- content = {'qwerty': 'uiop'}
- self.assertEqual(validator._validate(content, None, allowed_extra_fields=('extra',)), content)
-
- def validation_failed_due_to_no_content_returns_appropriate_message(self, validator):
- """If validation fails due to no content, ensure the response contains a single non-field error"""
- content = {}
- try:
- validator.validate_request(content, None)
- except ImmediateResponse, exc:
- response = exc.response
- self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.']}})
- else:
- self.fail('ResourceException was not raised')
-
- def validation_failed_due_to_field_error_returns_appropriate_message(self, validator):
- """If validation fails due to a field error, ensure the response contains a single field error"""
- content = {'qwerty': ''}
- try:
- validator.validate_request(content, None)
- except ImmediateResponse, exc:
- response = exc.response
- self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.']}})
- else:
- self.fail('ResourceException was not raised')
-
- def validation_failed_due_to_invalid_field_returns_appropriate_message(self, validator):
- """If validation fails due to an invalid field, ensure the response contains a single field error"""
- content = {'qwerty': 'uiop', 'extra': 'extra'}
- try:
- validator.validate_request(content, None)
- except ImmediateResponse, exc:
- response = exc.response
- self.assertEqual(response.raw_content, {'field_errors': {'extra': ['This field does not exist.']}})
- else:
- self.fail('ResourceException was not raised')
-
- def validation_failed_due_to_multiple_errors_returns_appropriate_message(self, validator):
- """If validation for multiple reasons, ensure the response contains each error"""
- content = {'qwerty': '', 'extra': 'extra'}
- try:
- validator.validate_request(content, None)
- except ImmediateResponse, exc:
- response = exc.response
- self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.'],
- 'extra': ['This field does not exist.']}})
- else:
- self.fail('ResourceException was not raised')
-
- # Tests on FormResource
-
- def test_form_validation_returns_content_unchanged_if_already_valid_and_clean(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
-
- def test_form_validation_failure_raises_response_exception(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_failure_raises_response_exception(validator)
-
- def test_validation_does_not_allow_extra_fields_by_default(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_does_not_allow_extra_fields_by_default(validator)
-
- def test_validation_allows_extra_fields_if_explicitly_set(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_allows_extra_fields_if_explicitly_set(validator)
-
- def test_validation_allows_unknown_fields_if_explicitly_allowed(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_allows_unknown_fields_if_explicitly_allowed(validator)
-
- def test_validation_does_not_require_extra_fields_if_explicitly_set(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
-
- def test_validation_failed_due_to_no_content_returns_appropriate_message(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
-
- def test_validation_failed_due_to_field_error_returns_appropriate_message(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
-
- def test_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
-
- def test_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
- validator = self.MockFormResource(self.MockFormView())
- self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
-
- # Same tests on ModelResource
-
- def test_modelform_validation_returns_content_unchanged_if_already_valid_and_clean(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
-
- def test_modelform_validation_failure_raises_response_exception(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_failure_raises_response_exception(validator)
-
- def test_modelform_validation_does_not_allow_extra_fields_by_default(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_does_not_allow_extra_fields_by_default(validator)
-
- def test_modelform_validation_allows_extra_fields_if_explicitly_set(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_allows_extra_fields_if_explicitly_set(validator)
-
- def test_modelform_validation_does_not_require_extra_fields_if_explicitly_set(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
-
- def test_modelform_validation_failed_due_to_no_content_returns_appropriate_message(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
-
- def test_modelform_validation_failed_due_to_field_error_returns_appropriate_message(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
-
- def test_modelform_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
-
- def test_modelform_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
- validator = self.MockModelResource(self.MockModelFormView())
- self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
-
-
-class TestModelFormValidator(TestCase):
- """Tests specific to ModelFormValidatorMixin"""
-
- def setUp(self):
- """Create a validator for a model with two fields and a property."""
- class MockModel(models.Model):
- qwerty = models.CharField(max_length=256)
- uiop = models.CharField(max_length=256, blank=True)
-
- @property
- def readonly(self):
- return 'read only'
-
- class MockResource(ModelResource):
- model = MockModel
+# class TestDisabledValidations(TestCase):
+# """Tests on FormValidator with validation disabled by setting form to None"""
- class MockView(View):
- resource = MockResource
+# def test_disabled_form_validator_returns_content_unchanged(self):
+# """If the view's form attribute is None then FormValidator(view).validate_request(content, None)
+# should just return the content unmodified."""
+# class DisabledFormResource(FormResource):
+# form = None
+
+# class MockView(View):
+# resource = DisabledFormResource
- self.validator = MockResource(MockView)
+# view = MockView()
+# content = {'qwerty': 'uiop'}
+# self.assertEqual(FormResource(view).validate_request(content, None), content)
+
+# def test_disabled_form_validator_get_bound_form_returns_none(self):
+# """If the view's form attribute is None on then
+# FormValidator(view).get_bound_form(content) should just return None."""
+# class DisabledFormResource(FormResource):
+# form = None
+
+# class MockView(View):
+# resource = DisabledFormResource
+
+# view = MockView()
+# content = {'qwerty': 'uiop'}
+# self.assertEqual(FormResource(view).get_bound_form(content), None)
+
+# def test_disabled_model_form_validator_returns_content_unchanged(self):
+# """If the view's form is None and does not have a Resource with a model set then
+# ModelFormValidator(view).validate_request(content, None) should just return the content unmodified."""
+
+# class DisabledModelFormView(View):
+# resource = ModelResource
+
+# view = DisabledModelFormView()
+# content = {'qwerty': 'uiop'}
+# self.assertEqual(ModelResource(view).get_bound_form(content), None)
+
+# def test_disabled_model_form_validator_get_bound_form_returns_none(self):
+# """If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None."""
+# class DisabledModelFormView(View):
+# resource = ModelResource
+
+# view = DisabledModelFormView()
+# content = {'qwerty': 'uiop'}
+# self.assertEqual(ModelResource(view).get_bound_form(content), None)
+
+
+# class TestNonFieldErrors(TestCase):
+# """Tests against form validation errors caused by non-field errors. (eg as might be caused by some custom form validation)"""
+
+# def test_validate_failed_due_to_non_field_error_returns_appropriate_message(self):
+# """If validation fails with a non-field error, ensure the response a non-field error"""
+# class MockForm(forms.Form):
+# field1 = forms.CharField(required=False)
+# field2 = forms.CharField(required=False)
+# ERROR_TEXT = 'You may not supply both field1 and field2'
+
+# def clean(self):
+# if 'field1' in self.cleaned_data and 'field2' in self.cleaned_data:
+# raise forms.ValidationError(self.ERROR_TEXT)
+# return self.cleaned_data
+
+# class MockResource(FormResource):
+# form = MockForm
+
+# class MockView(View):
+# pass
+
+# view = MockView()
+# content = {'field1': 'example1', 'field2': 'example2'}
+# try:
+# MockResource(view).validate_request(content, None)
+# except ImmediateResponse, exc:
+# response = exc.response
+# self.assertEqual(response.raw_content, {'errors': [MockForm.ERROR_TEXT]})
+# else:
+# self.fail('ImmediateResponse was not raised')
+
+
+# class TestFormValidation(TestCase):
+# """Tests which check basic form validation.
+# Also includes the same set of tests with a ModelFormValidator for which the form has been explicitly set.
+# (ModelFormValidator should behave as FormValidator if a form is set rather than relying on the default ModelForm)"""
+# def setUp(self):
+# class MockForm(forms.Form):
+# qwerty = forms.CharField(required=True)
+
+# class MockFormResource(FormResource):
+# form = MockForm
+
+# class MockModelResource(ModelResource):
+# form = MockForm
+
+# class MockFormView(View):
+# resource = MockFormResource
+
+# class MockModelFormView(View):
+# resource = MockModelResource
+
+# self.MockFormResource = MockFormResource
+# self.MockModelResource = MockModelResource
+# self.MockFormView = MockFormView
+# self.MockModelFormView = MockModelFormView
+
+# def validation_returns_content_unchanged_if_already_valid_and_clean(self, validator):
+# """If the content is already valid and clean then validate(content) should just return the content unmodified."""
+# content = {'qwerty': 'uiop'}
+# self.assertEqual(validator.validate_request(content, None), content)
+
+# def validation_failure_raises_response_exception(self, validator):
+# """If form validation fails a ResourceException 400 (Bad Request) should be raised."""
+# content = {}
+# self.assertRaises(ImmediateResponse, validator.validate_request, content, None)
+
+# 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(ImmediateResponse, validator.validate_request, content, None)
+
+# 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."""
+# content = {'qwerty': 'uiop', 'extra': 'extra'}
+# validator._validate(content, None, allowed_extra_fields=('extra',))
+
+# def validation_allows_unknown_fields_if_explicitly_allowed(self, validator):
+# """If we set ``unknown_form_fields`` on the form resource, then don't
+# raise errors on unexpected request data"""
+# content = {'qwerty': 'uiop', 'extra': 'extra'}
+# validator.allow_unknown_form_fields = True
+# self.assertEqual({'qwerty': u'uiop'},
+# validator.validate_request(content, None),
+# "Resource didn't accept unknown fields.")
+# validator.allow_unknown_form_fields = False
+
+# def validation_does_not_require_extra_fields_if_explicitly_set(self, validator):
+# """If we include an allowed_extra_fields paramater on _validate, then do not fail if we do not have fields with those names."""
+# content = {'qwerty': 'uiop'}
+# self.assertEqual(validator._validate(content, None, allowed_extra_fields=('extra',)), content)
+
+# def validation_failed_due_to_no_content_returns_appropriate_message(self, validator):
+# """If validation fails due to no content, ensure the response contains a single non-field error"""
+# content = {}
+# try:
+# validator.validate_request(content, None)
+# except ImmediateResponse, exc:
+# response = exc.response
+# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.']}})
+# else:
+# self.fail('ResourceException was not raised')
+
+# def validation_failed_due_to_field_error_returns_appropriate_message(self, validator):
+# """If validation fails due to a field error, ensure the response contains a single field error"""
+# content = {'qwerty': ''}
+# try:
+# validator.validate_request(content, None)
+# except ImmediateResponse, exc:
+# response = exc.response
+# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.']}})
+# else:
+# self.fail('ResourceException was not raised')
+
+# def validation_failed_due_to_invalid_field_returns_appropriate_message(self, validator):
+# """If validation fails due to an invalid field, ensure the response contains a single field error"""
+# content = {'qwerty': 'uiop', 'extra': 'extra'}
+# try:
+# validator.validate_request(content, None)
+# except ImmediateResponse, exc:
+# response = exc.response
+# self.assertEqual(response.raw_content, {'field_errors': {'extra': ['This field does not exist.']}})
+# else:
+# self.fail('ResourceException was not raised')
+
+# def validation_failed_due_to_multiple_errors_returns_appropriate_message(self, validator):
+# """If validation for multiple reasons, ensure the response contains each error"""
+# content = {'qwerty': '', 'extra': 'extra'}
+# try:
+# validator.validate_request(content, None)
+# except ImmediateResponse, exc:
+# response = exc.response
+# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.'],
+# 'extra': ['This field does not exist.']}})
+# else:
+# self.fail('ResourceException was not raised')
+
+# # Tests on FormResource
+
+# def test_form_validation_returns_content_unchanged_if_already_valid_and_clean(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
+
+# def test_form_validation_failure_raises_response_exception(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_failure_raises_response_exception(validator)
+
+# def test_validation_does_not_allow_extra_fields_by_default(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_does_not_allow_extra_fields_by_default(validator)
+
+# def test_validation_allows_extra_fields_if_explicitly_set(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_allows_extra_fields_if_explicitly_set(validator)
+
+# def test_validation_allows_unknown_fields_if_explicitly_allowed(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_allows_unknown_fields_if_explicitly_allowed(validator)
+
+# def test_validation_does_not_require_extra_fields_if_explicitly_set(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
+
+# def test_validation_failed_due_to_no_content_returns_appropriate_message(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
+
+# def test_validation_failed_due_to_field_error_returns_appropriate_message(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
+
+# def test_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
+
+# def test_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
+# validator = self.MockFormResource(self.MockFormView())
+# self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
+
+# # Same tests on ModelResource
+
+# def test_modelform_validation_returns_content_unchanged_if_already_valid_and_clean(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
+
+# def test_modelform_validation_failure_raises_response_exception(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_failure_raises_response_exception(validator)
+
+# def test_modelform_validation_does_not_allow_extra_fields_by_default(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_does_not_allow_extra_fields_by_default(validator)
+
+# def test_modelform_validation_allows_extra_fields_if_explicitly_set(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_allows_extra_fields_if_explicitly_set(validator)
+
+# def test_modelform_validation_does_not_require_extra_fields_if_explicitly_set(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
+
+# def test_modelform_validation_failed_due_to_no_content_returns_appropriate_message(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
+
+# def test_modelform_validation_failed_due_to_field_error_returns_appropriate_message(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
+
+# def test_modelform_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
+
+# def test_modelform_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
+# validator = self.MockModelResource(self.MockModelFormView())
+# self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
+
+
+# class TestModelFormValidator(TestCase):
+# """Tests specific to ModelFormValidatorMixin"""
+
+# def setUp(self):
+# """Create a validator for a model with two fields and a property."""
+# class MockModel(models.Model):
+# qwerty = models.CharField(max_length=256)
+# uiop = models.CharField(max_length=256, blank=True)
+
+# @property
+# def readonly(self):
+# return 'read only'
+
+# class MockResource(ModelResource):
+# model = MockModel
- def test_property_fields_are_allowed_on_model_forms(self):
- """Validation on ModelForms may include property fields that exist on the Model to be included in the input."""
- content = {'qwerty': 'example', 'uiop': 'example', 'readonly': 'read only'}
- self.assertEqual(self.validator.validate_request(content, None), content)
+# class MockView(View):
+# resource = MockResource
- def test_property_fields_are_not_required_on_model_forms(self):
- """Validation on ModelForms does not require property fields that exist on the Model to be included in the input."""
- content = {'qwerty': 'example', 'uiop': 'example'}
- self.assertEqual(self.validator.validate_request(content, None), content)
+# self.validator = MockResource(MockView)
- def test_extra_fields_not_allowed_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 = {'qwerty': 'example', 'uiop': 'example', 'readonly': 'read only', 'extra': 'extra'}
- self.assertRaises(ImmediateResponse, self.validator.validate_request, content, None)
+# def test_property_fields_are_allowed_on_model_forms(self):
+# """Validation on ModelForms may include property fields that exist on the Model to be included in the input."""
+# content = {'qwerty': 'example', 'uiop': 'example', 'readonly': 'read only'}
+# self.assertEqual(self.validator.validate_request(content, None), 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(ImmediateResponse, self.validator.validate_request, content, None)
+# def test_property_fields_are_not_required_on_model_forms(self):
+# """Validation on ModelForms does not require property fields that exist on the Model to be included in the input."""
+# content = {'qwerty': 'example', 'uiop': 'example'}
+# self.assertEqual(self.validator.validate_request(content, None), 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."""
- content = {'qwerty': 'example', 'readonly': 'read only'}
- self.validator.validate_request(content, None)
+# def test_extra_fields_not_allowed_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 = {'qwerty': 'example', 'uiop': 'example', 'readonly': 'read only', 'extra': 'extra'}
+# self.assertRaises(ImmediateResponse, self.validator.validate_request, content, None)
- def test_model_form_validator_uses_model_forms(self):
- self.assertTrue(isinstance(self.validator.get_bound_form(), forms.ModelForm))
+# 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(ImmediateResponse, self.validator.validate_request, content, None)
+
+# def test_validate_does_not_require_blankable_fields_on_model_forms(self):
+# """Test standard ModelForm validation behaviour - fields with blank=True are not required."""
+# content = {'qwerty': 'example', 'readonly': 'read only'}
+# self.validator.validate_request(content, None)
+
+# def test_model_form_validator_uses_model_forms(self):
+# self.assertTrue(isinstance(self.validator.get_bound_form(), forms.ModelForm))
diff --git a/djangorestframework/tests/views.py b/djangorestframework/tests/views.py
index 00bce002..d4e4098a 100644
--- a/djangorestframework/tests/views.py
+++ b/djangorestframework/tests/views.py
@@ -1,135 +1,128 @@
-from django.core.urlresolvers import reverse
-from django.conf.urls.defaults import patterns, url, include
-from django.http import HttpResponse
-from django.test import TestCase
-from django import forms
-from django.db import models
-from django.utils import simplejson as json
-
-from djangorestframework.resources import ModelResource
-from djangorestframework.views import (
- View,
- ListOrCreateModelView,
- InstanceModelView
-)
-
-
-class MockView(View):
- """This is a basic mock view"""
- pass
-
-
-class MockViewFinal(View):
- """View with final() override"""
-
- def final(self, request, response, *args, **kwargs):
- return HttpResponse('{"test": "passed"}', content_type="application/json")
-
-
-class ResourceMockView(View):
- """This is a resource-based mock view"""
-
- class MockForm(forms.Form):
- foo = forms.BooleanField(required=False)
- bar = forms.IntegerField(help_text='Must be an integer.')
- baz = forms.CharField(max_length=32)
-
- form = MockForm
-
-
-class MockResource(ModelResource):
- """This is a mock model-based resource"""
-
- class MockResourceModel(models.Model):
- foo = models.BooleanField()
- bar = models.IntegerField(help_text='Must be an integer.')
- baz = models.CharField(max_length=32, help_text='Free text. Max length 32 chars.')
-
- model = MockResourceModel
- fields = ('foo', 'bar', 'baz')
-
-urlpatterns = patterns('',
- url(r'^mock/$', MockView.as_view()),
- url(r'^mock/final/$', MockViewFinal.as_view()),
- url(r'^resourcemock/$', ResourceMockView.as_view()),
- url(r'^model/$', ListOrCreateModelView.as_view(resource=MockResource)),
- url(r'^model/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=MockResource)),
- url(r'^restframework/', include('djangorestframework.urls', namespace='djangorestframework')),
-)
-
-
-class BaseViewTests(TestCase):
- """Test the base view class of djangorestframework"""
- urls = 'djangorestframework.tests.views'
-
- def test_view_call_final(self):
- response = self.client.options('/mock/final/')
- self.assertEqual(response['Content-Type'].split(';')[0], "application/json")
- data = json.loads(response.content)
- self.assertEqual(data['test'], 'passed')
-
- def test_options_method_simple_view(self):
- response = self.client.options('/mock/')
- self._verify_options_response(response,
- name='Mock',
- description='This is a basic mock view')
-
- def test_options_method_resource_view(self):
- response = self.client.options('/resourcemock/')
- self._verify_options_response(response,
- name='Resource Mock',
- description='This is a resource-based mock view',
- fields={'foo': 'BooleanField',
- 'bar': 'IntegerField',
- 'baz': 'CharField',
- })
-
- def test_options_method_model_resource_list_view(self):
- response = self.client.options('/model/')
- self._verify_options_response(response,
- name='Mock List',
- description='This is a mock model-based resource',
- fields={'foo': 'BooleanField',
- 'bar': 'IntegerField',
- 'baz': 'CharField',
- })
-
- def test_options_method_model_resource_detail_view(self):
- response = self.client.options('/model/0/')
- self._verify_options_response(response,
- name='Mock Instance',
- description='This is a mock model-based resource',
- fields={'foo': 'BooleanField',
- 'bar': 'IntegerField',
- 'baz': 'CharField',
- })
-
- def _verify_options_response(self, response, name, description, fields=None, status=200,
- mime_type='application/json'):
- self.assertEqual(response.status_code, status)
- self.assertEqual(response['Content-Type'].split(';')[0], mime_type)
- data = json.loads(response.content)
- self.assertTrue('application/json' in data['renders'])
- self.assertEqual(name, data['name'])
- self.assertEqual(description, data['description'])
- if fields is None:
- self.assertFalse(hasattr(data, 'fields'))
- else:
- self.assertEqual(data['fields'], fields)
-
-
-class ExtraViewsTests(TestCase):
- """Test the extra views djangorestframework provides"""
- urls = 'djangorestframework.tests.views'
-
- def test_login_view(self):
- """Ensure the login view exists"""
- response = self.client.get(reverse('djangorestframework:login'))
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
-
- def test_logout_view(self):
- """Ensure the logout view exists"""
- response = self.client.get(reverse('djangorestframework:logout'))
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
+# from django.core.urlresolvers import reverse
+# from django.conf.urls.defaults import patterns, url, include
+# from django.http import HttpResponse
+# from django.test import TestCase
+# from django.utils import simplejson as json
+
+# from djangorestframework.views import View
+
+
+# class MockView(View):
+# """This is a basic mock view"""
+# pass
+
+
+# class MockViewFinal(View):
+# """View with final() override"""
+
+# def final(self, request, response, *args, **kwargs):
+# return HttpResponse('{"test": "passed"}', content_type="application/json")
+
+
+# # class ResourceMockView(View):
+# # """This is a resource-based mock view"""
+
+# # class MockForm(forms.Form):
+# # foo = forms.BooleanField(required=False)
+# # bar = forms.IntegerField(help_text='Must be an integer.')
+# # baz = forms.CharField(max_length=32)
+
+# # form = MockForm
+
+
+# # class MockResource(ModelResource):
+# # """This is a mock model-based resource"""
+
+# # class MockResourceModel(models.Model):
+# # foo = models.BooleanField()
+# # bar = models.IntegerField(help_text='Must be an integer.')
+# # baz = models.CharField(max_length=32, help_text='Free text. Max length 32 chars.')
+
+# # model = MockResourceModel
+# # fields = ('foo', 'bar', 'baz')
+
+# urlpatterns = patterns('',
+# url(r'^mock/$', MockView.as_view()),
+# url(r'^mock/final/$', MockViewFinal.as_view()),
+# # url(r'^resourcemock/$', ResourceMockView.as_view()),
+# # url(r'^model/$', ListOrCreateModelView.as_view(resource=MockResource)),
+# # url(r'^model/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=MockResource)),
+# url(r'^restframework/', include('djangorestframework.urls', namespace='djangorestframework')),
+# )
+
+
+# class BaseViewTests(TestCase):
+# """Test the base view class of djangorestframework"""
+# urls = 'djangorestframework.tests.views'
+
+# def test_view_call_final(self):
+# response = self.client.options('/mock/final/')
+# self.assertEqual(response['Content-Type'].split(';')[0], "application/json")
+# data = json.loads(response.content)
+# self.assertEqual(data['test'], 'passed')
+
+# def test_options_method_simple_view(self):
+# response = self.client.options('/mock/')
+# self._verify_options_response(response,
+# name='Mock',
+# description='This is a basic mock view')
+
+# def test_options_method_resource_view(self):
+# response = self.client.options('/resourcemock/')
+# self._verify_options_response(response,
+# name='Resource Mock',
+# description='This is a resource-based mock view',
+# fields={'foo': 'BooleanField',
+# 'bar': 'IntegerField',
+# 'baz': 'CharField',
+# })
+
+# def test_options_method_model_resource_list_view(self):
+# response = self.client.options('/model/')
+# self._verify_options_response(response,
+# name='Mock List',
+# description='This is a mock model-based resource',
+# fields={'foo': 'BooleanField',
+# 'bar': 'IntegerField',
+# 'baz': 'CharField',
+# })
+
+# def test_options_method_model_resource_detail_view(self):
+# response = self.client.options('/model/0/')
+# self._verify_options_response(response,
+# name='Mock Instance',
+# description='This is a mock model-based resource',
+# fields={'foo': 'BooleanField',
+# 'bar': 'IntegerField',
+# 'baz': 'CharField',
+# })
+
+# def _verify_options_response(self, response, name, description, fields=None, status=200,
+# mime_type='application/json'):
+# self.assertEqual(response.status_code, status)
+# self.assertEqual(response['Content-Type'].split(';')[0], mime_type)
+# data = json.loads(response.content)
+# self.assertTrue('application/json' in data['renders'])
+# self.assertEqual(name, data['name'])
+# self.assertEqual(description, data['description'])
+# if fields is None:
+# self.assertFalse(hasattr(data, 'fields'))
+# else:
+# self.assertEqual(data['fields'], fields)
+
+
+# class ExtraViewsTests(TestCase):
+# """Test the extra views djangorestframework provides"""
+# urls = 'djangorestframework.tests.views'
+
+# def test_login_view(self):
+# """Ensure the login view exists"""
+# response = self.client.get(reverse('djangorestframework:login'))
+# self.assertEqual(response.status_code, 200)
+# self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
+
+# def test_logout_view(self):
+# """Ensure the logout view exists"""
+# response = self.client.get(reverse('djangorestframework:logout'))
+# self.assertEqual(response.status_code, 200)
+# self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
diff --git a/djangorestframework/views.py b/djangorestframework/views.py
index 2ce36a9a..be8f08ae 100644
--- a/djangorestframework/views.py
+++ b/djangorestframework/views.py
@@ -13,8 +13,7 @@ from django.views.decorators.csrf import csrf_exempt
from djangorestframework.compat import View as DjangoView, apply_markdown
from djangorestframework.response import Response, ImmediateResponse
from djangorestframework.request import Request
-from djangorestframework.mixins import *
-from djangorestframework import resources, renderers, parsers, authentication, permissions, status
+from djangorestframework import renderers, parsers, authentication, permissions, status
__all__ = (
@@ -29,7 +28,7 @@ __all__ = (
def _remove_trailing_string(content, trailing):
"""
Strip trailing component `trailing` from `content` if it exists.
- Used when generating names from view/resource classes.
+ Used when generating names from view classes.
"""
if content.endswith(trailing) and content != trailing:
return content[:-len(trailing)]
@@ -54,40 +53,26 @@ def _remove_leading_indent(content):
def _camelcase_to_spaces(content):
"""
Translate 'CamelCaseNames' to 'Camel Case Names'.
- Used when generating names from view/resource classes.
+ Used when generating names from view classes.
"""
camelcase_boundry = '(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))'
return re.sub(camelcase_boundry, ' \\1', content).strip()
-_resource_classes = (
- None,
- resources.Resource,
- resources.FormResource,
- resources.ModelResource
-)
-
-
-class View(ResourceMixin, DjangoView):
+class View(DjangoView):
"""
Handles incoming requests and maps them to REST operations.
Performs request deserialization, response serialization, authentication and input validation.
"""
- resource = None
- """
- The resource to use when validating requests and filtering responses,
- or `None` to use default behaviour.
- """
-
renderers = renderers.DEFAULT_RENDERERS
"""
- List of renderer classes the resource can serialize the response with, ordered by preference.
+ List of renderer classes the view can serialize the response with, ordered by preference.
"""
parsers = parsers.DEFAULT_PARSERS
"""
- List of parser classes the resource can parse the request with.
+ List of parser classes the view can parse the request with.
"""
authentication = (authentication.UserLoggedInAuthentication,
@@ -132,17 +117,8 @@ class View(ResourceMixin, DjangoView):
Return the resource or view class name for use as this view's name.
Override to customize.
"""
- # If this view has a resource that's been overridden, then use that resource for the name
- if getattr(self, 'resource', None) not in _resource_classes:
- name = self.resource.__name__
- name = _remove_trailing_string(name, 'Resource')
- name += getattr(self, '_suffix', '')
-
- # If it's a view class with no resource then grok the name from the class name
- else:
- name = self.__class__.__name__
- name = _remove_trailing_string(name, 'View')
-
+ name = self.__class__.__name__
+ name = _remove_trailing_string(name, 'View')
return _camelcase_to_spaces(name)
def get_description(self, html=False):
@@ -150,20 +126,8 @@ class View(ResourceMixin, DjangoView):
Return the resource or view docstring for use as this view's description.
Override to customize.
"""
-
- description = None
-
- # If this view has a resource that's been overridden,
- # then try to use the resource's docstring
- if getattr(self, 'resource', None) not in _resource_classes:
- description = self.resource.__doc__
-
- # Otherwise use the view docstring
- if not description:
- description = self.__doc__ or ''
-
+ description = self.__doc__ or ''
description = _remove_leading_indent(description)
-
if html:
return self.markup_description(description)
return description
@@ -184,7 +148,7 @@ class View(ResourceMixin, DjangoView):
a handler method.
"""
content = {
- 'detail': "Method '%s' not allowed on this resource." % request.method
+ 'detail': "Method '%s' not allowed." % request.method
}
raise ImmediateResponse(content, status.HTTP_405_METHOD_NOT_ALLOWED)
@@ -283,10 +247,6 @@ class View(ResourceMixin, DjangoView):
response = handler(request, *args, **kwargs)
- if isinstance(response, Response):
- # Pre-serialize filtering (eg filter complex objects into natively serializable types)
- response.raw_content = self.filter_response(response.raw_content)
-
except ImmediateResponse, exc:
response = exc.response
@@ -307,31 +267,3 @@ class View(ResourceMixin, DjangoView):
field_name_types[name] = field.__class__.__name__
content['fields'] = field_name_types
raise ImmediateResponse(content, status=status.HTTP_200_OK)
-
-
-class ModelView(View):
- """
- A RESTful view that maps to a model in the database.
- """
- resource = resources.ModelResource
-
-
-class InstanceModelView(ReadModelMixin, UpdateModelMixin, DeleteModelMixin, ModelView):
- """
- A view which provides default operations for read/update/delete against a model instance.
- """
- _suffix = 'Instance'
-
-
-class ListModelView(ListModelMixin, ModelView):
- """
- A view which provides default operations for list, against a model in the database.
- """
- _suffix = 'List'
-
-
-class ListOrCreateModelView(ListModelMixin, CreateModelMixin, ModelView):
- """
- A view which provides default operations for list and create, against a model in the database.
- """
- _suffix = 'List'