diff options
| -rw-r--r-- | djangorestframework/mixins.py | 388 | ||||
| -rw-r--r-- | djangorestframework/permissions.py | 14 | ||||
| -rw-r--r-- | djangorestframework/resources.py | 343 | ||||
| -rw-r--r-- | djangorestframework/serializer.py | 283 | ||||
| -rw-r--r-- | djangorestframework/tests/files.py | 51 | ||||
| -rw-r--r-- | djangorestframework/tests/mixins.py | 565 | ||||
| -rw-r--r-- | djangorestframework/tests/modelviews.py | 132 | ||||
| -rw-r--r-- | djangorestframework/tests/serializer.py | 297 | ||||
| -rw-r--r-- | djangorestframework/tests/throttling.py | 14 | ||||
| -rw-r--r-- | djangorestframework/tests/validators.py | 639 | ||||
| -rw-r--r-- | djangorestframework/tests/views.py | 263 | ||||
| -rw-r--r-- | djangorestframework/views.py | 88 | 
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'  | 
