mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	Code linting and added runtests.py
This commit is contained in:
		
							parent
							
								
									e385a7b8eb
								
							
						
					
					
						commit
						bf09c32de8
					
				| 
						 | 
				
			
			@ -28,7 +28,7 @@ install:
 | 
			
		|||
  - export PYTHONPATH=.
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  - py.test
 | 
			
		||||
  - ./runtests.py
 | 
			
		||||
 | 
			
		||||
matrix:
 | 
			
		||||
  exclude:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,2 +0,0 @@
 | 
			
		|||
[pytest]
 | 
			
		||||
addopts = --tb=short
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,9 @@
 | 
			
		|||
# Test requirements
 | 
			
		||||
pytest-django==2.6
 | 
			
		||||
pytest==2.5.2
 | 
			
		||||
pytest-cov==1.6
 | 
			
		||||
 | 
			
		||||
# Optional packages
 | 
			
		||||
markdown>=2.1.0
 | 
			
		||||
PyYAML>=3.10
 | 
			
		||||
defusedxml>=0.3
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1 @@
 | 
			
		|||
-e .
 | 
			
		||||
Django>=1.3
 | 
			
		||||
pytest-django==2.6
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
"""
 | 
			
		||||
______ _____ _____ _____    __                                             _    
 | 
			
		||||
| ___ \  ___/  ___|_   _|  / _|                                           | |   
 | 
			
		||||
| |_/ / |__ \ `--.  | |   | |_ _ __ __ _ _ __ ___   _____      _____  _ __| | __
 | 
			
		||||
______ _____ _____ _____    __
 | 
			
		||||
| ___ \  ___/  ___|_   _|  / _|                                           | |
 | 
			
		||||
| |_/ / |__ \ `--.  | |   | |_ _ __ __ _ _ __ ___   _____      _____  _ __| |__
 | 
			
		||||
|    /|  __| `--. \ | |   |  _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ /
 | 
			
		||||
| |\ \| |___/\__/ / | |   | | | | | (_| | | | | | |  __/\ V  V / (_) | |  |   < 
 | 
			
		||||
| |\ \| |___/\__/ / | |   | | | | | (_| | | | | | |  __/\ V  V / (_) | |  |   <
 | 
			
		||||
\_| \_\____/\____/  \_/   |_| |_|  \__,_|_| |_| |_|\___| \_/\_/ \___/|_|  |_|\_|
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ def get_authorization_header(request):
 | 
			
		|||
    Hide some test client ickyness where the header can be unicode.
 | 
			
		||||
    """
 | 
			
		||||
    auth = request.META.get('HTTP_AUTHORIZATION', b'')
 | 
			
		||||
    if type(auth) == type(''):
 | 
			
		||||
    if isinstance(auth, type('')):
 | 
			
		||||
        # Work around django test client oddness
 | 
			
		||||
        auth = auth.encode(HTTP_HEADER_ENCODING)
 | 
			
		||||
    return auth
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
import binascii
 | 
			
		||||
import os
 | 
			
		||||
from hashlib import sha1
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,10 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
import datetime
 | 
			
		||||
from south.db import db
 | 
			
		||||
from south.v2 import SchemaMigration
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
from rest_framework.settings import api_settings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from django.contrib.auth import get_user_model
 | 
			
		||||
except ImportError: # django < 1.5
 | 
			
		||||
except ImportError:  # django < 1.5
 | 
			
		||||
    from django.contrib.auth.models import User
 | 
			
		||||
else:
 | 
			
		||||
    User = get_user_model()
 | 
			
		||||
| 
						 | 
				
			
			@ -26,12 +21,10 @@ class Migration(SchemaMigration):
 | 
			
		|||
        ))
 | 
			
		||||
        db.send_create_signal('authtoken', ['Token'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def backwards(self, orm):
 | 
			
		||||
        # Deleting model 'Token'
 | 
			
		||||
        db.delete_table('authtoken_token')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    models = {
 | 
			
		||||
        'auth.group': {
 | 
			
		||||
            'Meta': {'object_name': 'Group'},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -131,6 +131,7 @@ def list_route(methods=['get'], **kwargs):
 | 
			
		|||
        return func
 | 
			
		||||
    return decorator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# These are now pending deprecation, in favor of `detail_route` and `list_route`.
 | 
			
		||||
 | 
			
		||||
def link(**kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -139,11 +140,13 @@ def link(**kwargs):
 | 
			
		|||
    """
 | 
			
		||||
    msg = 'link is pending deprecation. Use detail_route instead.'
 | 
			
		||||
    warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
 | 
			
		||||
 | 
			
		||||
    def decorator(func):
 | 
			
		||||
        func.bind_to_methods = ['get']
 | 
			
		||||
        func.detail = True
 | 
			
		||||
        func.kwargs = kwargs
 | 
			
		||||
        return func
 | 
			
		||||
 | 
			
		||||
    return decorator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,9 +156,11 @@ def action(methods=['post'], **kwargs):
 | 
			
		|||
    """
 | 
			
		||||
    msg = 'action is pending deprecation. Use detail_route instead.'
 | 
			
		||||
    warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
 | 
			
		||||
 | 
			
		||||
    def decorator(func):
 | 
			
		||||
        func.bind_to_methods = methods
 | 
			
		||||
        func.detail = True
 | 
			
		||||
        func.kwargs = kwargs
 | 
			
		||||
        return func
 | 
			
		||||
    return decorator
 | 
			
		||||
 | 
			
		||||
    return decorator
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ class APIException(Exception):
 | 
			
		|||
    def __str__(self):
 | 
			
		||||
        return self.detail
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ParseError(APIException):
 | 
			
		||||
    status_code = status.HTTP_400_BAD_REQUEST
 | 
			
		||||
    default_detail = 'Malformed request.'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -116,7 +116,7 @@ class OrderingFilter(BaseFilterBackend):
 | 
			
		|||
    def get_ordering(self, request):
 | 
			
		||||
        """
 | 
			
		||||
        Ordering is set by a comma delimited ?ordering=... query parameter.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        The `ordering` query parameter can be overridden by setting
 | 
			
		||||
        the `ordering_param` value on the OrderingFilter or by
 | 
			
		||||
        specifying an `ORDERING_PARAM` value in the API settings.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ def strict_positive_int(integer_string, cutoff=None):
 | 
			
		|||
        ret = min(ret, cutoff)
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_object_or_404(queryset, *filter_args, **filter_kwargs):
 | 
			
		||||
    """
 | 
			
		||||
    Same as Django's standard shortcut, but make sure to raise 404
 | 
			
		||||
| 
						 | 
				
			
			@ -162,10 +163,11 @@ class GenericAPIView(views.APIView):
 | 
			
		|||
                raise Http404(_("Page is not 'last', nor can it be converted to an int."))
 | 
			
		||||
        try:
 | 
			
		||||
            page = paginator.page(page_number)
 | 
			
		||||
        except InvalidPage as e:
 | 
			
		||||
            raise Http404(_('Invalid page (%(page_number)s): %(message)s') % {
 | 
			
		||||
                                'page_number': page_number,
 | 
			
		||||
                                'message': str(e)
 | 
			
		||||
        except InvalidPage as exc:
 | 
			
		||||
            error_format = _('Invalid page (%(page_number)s): %(message)s')
 | 
			
		||||
            raise Http404(error_format % {
 | 
			
		||||
                'page_number': page_number,
 | 
			
		||||
                'message': str(exc)
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        if deprecated_style:
 | 
			
		||||
| 
						 | 
				
			
			@ -208,7 +210,6 @@ class GenericAPIView(views.APIView):
 | 
			
		|||
 | 
			
		||||
        return filter_backends
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    ########################
 | 
			
		||||
    ### The following methods provide default implementations
 | 
			
		||||
    ### that you may want to override for more complex cases.
 | 
			
		||||
| 
						 | 
				
			
			@ -284,8 +285,8 @@ class GenericAPIView(views.APIView):
 | 
			
		|||
        if self.model is not None:
 | 
			
		||||
            return self.model._default_manager.all()
 | 
			
		||||
 | 
			
		||||
        raise ImproperlyConfigured("'%s' must define 'queryset' or 'model'"
 | 
			
		||||
                                    % self.__class__.__name__)
 | 
			
		||||
        error_format = "'%s' must define 'queryset' or 'model'"
 | 
			
		||||
        raise ImproperlyConfigured(error_format % self.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
    def get_object(self, queryset=None):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,8 +54,10 @@ class DefaultContentNegotiation(BaseContentNegotiation):
 | 
			
		|||
                for media_type in media_type_set:
 | 
			
		||||
                    if media_type_matches(renderer.media_type, media_type):
 | 
			
		||||
                        # Return the most specific media type as accepted.
 | 
			
		||||
                        if (_MediaType(renderer.media_type).precedence >
 | 
			
		||||
                            _MediaType(media_type).precedence):
 | 
			
		||||
                        if (
 | 
			
		||||
                            _MediaType(renderer.media_type).precedence >
 | 
			
		||||
                            _MediaType(media_type).precedence
 | 
			
		||||
                        ):
 | 
			
		||||
                            # Eg client requests '*/*'
 | 
			
		||||
                            # Accepted media type is 'application/json'
 | 
			
		||||
                            return renderer, renderer.media_type
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,9 +62,11 @@ class IsAuthenticatedOrReadOnly(BasePermission):
 | 
			
		|||
    """
 | 
			
		||||
 | 
			
		||||
    def has_permission(self, request, view):
 | 
			
		||||
        return (request.method in SAFE_METHODS or 
 | 
			
		||||
            request.user and 
 | 
			
		||||
            request.user.is_authenticated())
 | 
			
		||||
        return (
 | 
			
		||||
            request.method in SAFE_METHODS or
 | 
			
		||||
            request.user and
 | 
			
		||||
            request.user.is_authenticated()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoModelPermissions(BasePermission):
 | 
			
		||||
| 
						 | 
				
			
			@ -122,9 +124,11 @@ class DjangoModelPermissions(BasePermission):
 | 
			
		|||
 | 
			
		||||
        perms = self.get_required_permissions(request.method, model_cls)
 | 
			
		||||
 | 
			
		||||
        return (request.user and
 | 
			
		||||
        return (
 | 
			
		||||
            request.user and
 | 
			
		||||
            (request.user.is_authenticated() or not self.authenticated_users_only) and
 | 
			
		||||
            request.user.has_perms(perms))
 | 
			
		||||
            request.user.has_perms(perms)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
 | 
			
		||||
| 
						 | 
				
			
			@ -212,6 +216,8 @@ class TokenHasReadWriteScope(BasePermission):
 | 
			
		|||
            required = oauth2_constants.READ if read_only else oauth2_constants.WRITE
 | 
			
		||||
            return oauth2_provider_scope.check(required, request.auth.scope)
 | 
			
		||||
 | 
			
		||||
        assert False, ('TokenHasReadWriteScope requires either the'
 | 
			
		||||
        '`OAuthAuthentication` or `OAuth2Authentication` authentication '
 | 
			
		||||
        'class to be used.')
 | 
			
		||||
        assert False, (
 | 
			
		||||
            'TokenHasReadWriteScope requires either the'
 | 
			
		||||
            '`OAuthAuthentication` or `OAuth2Authentication` authentication '
 | 
			
		||||
            'class to be used.'
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,6 @@ REST framework also provides an HTML renderer the renders the browsable API.
 | 
			
		|||
"""
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
import json
 | 
			
		||||
import django
 | 
			
		||||
from django import forms
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +74,6 @@ class JSONRenderer(BaseRenderer):
 | 
			
		|||
        # E.g. If we're being called by the BrowsableAPIRenderer.
 | 
			
		||||
        return renderer_context.get('indent', None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def render(self, data, accepted_media_type=None, renderer_context=None):
 | 
			
		||||
        """
 | 
			
		||||
        Render `data` into JSON, returning a bytestring.
 | 
			
		||||
| 
						 | 
				
			
			@ -86,8 +84,10 @@ class JSONRenderer(BaseRenderer):
 | 
			
		|||
        renderer_context = renderer_context or {}
 | 
			
		||||
        indent = self.get_indent(accepted_media_type, renderer_context)
 | 
			
		||||
 | 
			
		||||
        ret = json.dumps(data, cls=self.encoder_class,
 | 
			
		||||
            indent=indent, ensure_ascii=self.ensure_ascii)
 | 
			
		||||
        ret = json.dumps(
 | 
			
		||||
            data, cls=self.encoder_class,
 | 
			
		||||
            indent=indent, ensure_ascii=self.ensure_ascii
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # On python 2.x json.dumps() returns bytestrings if ensure_ascii=True,
 | 
			
		||||
        # but if ensure_ascii=False, the return type is underspecified,
 | 
			
		||||
| 
						 | 
				
			
			@ -454,8 +454,10 @@ class BrowsableAPIRenderer(BaseRenderer):
 | 
			
		|||
            if method in ('DELETE', 'OPTIONS'):
 | 
			
		||||
                return True  # Don't actually need to return a form
 | 
			
		||||
 | 
			
		||||
            if (not getattr(view, 'get_serializer', None)
 | 
			
		||||
                or not any(is_form_media_type(parser.media_type) for parser in view.parser_classes)):
 | 
			
		||||
            if (
 | 
			
		||||
                not getattr(view, 'get_serializer', None)
 | 
			
		||||
                or not any(is_form_media_type(parser.media_type) for parser in view.parser_classes)
 | 
			
		||||
            ):
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            serializer = view.get_serializer(instance=obj, data=data, files=files)
 | 
			
		||||
| 
						 | 
				
			
			@ -576,7 +578,7 @@ class BrowsableAPIRenderer(BaseRenderer):
 | 
			
		|||
            'version': VERSION,
 | 
			
		||||
            'breadcrumblist': self.get_breadcrumbs(request),
 | 
			
		||||
            'allowed_methods': view.allowed_methods,
 | 
			
		||||
            'available_formats': [renderer.format for renderer in view.renderer_classes],
 | 
			
		||||
            'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes],
 | 
			
		||||
            'response_headers': response_headers,
 | 
			
		||||
 | 
			
		||||
            'put_form': self.get_rendered_html_form(view, 'PUT', request),
 | 
			
		||||
| 
						 | 
				
			
			@ -625,4 +627,3 @@ class MultiPartRenderer(BaseRenderer):
 | 
			
		|||
 | 
			
		||||
    def render(self, data, accepted_media_type=None, renderer_context=None):
 | 
			
		||||
        return encode_multipart(self.BOUNDARY, data)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -295,8 +295,11 @@ class Request(object):
 | 
			
		|||
        Return the content body of the request, as a stream.
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            content_length = int(self.META.get('CONTENT_LENGTH',
 | 
			
		||||
                                    self.META.get('HTTP_CONTENT_LENGTH')))
 | 
			
		||||
            content_length = int(
 | 
			
		||||
                self.META.get(
 | 
			
		||||
                    'CONTENT_LENGTH', self.META.get('HTTP_CONTENT_LENGTH')
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        except (ValueError, TypeError):
 | 
			
		||||
            content_length = 0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -320,9 +323,11 @@ class Request(object):
 | 
			
		|||
        )
 | 
			
		||||
 | 
			
		||||
        # We only need to use form overloading on form POST requests.
 | 
			
		||||
        if (not USE_FORM_OVERLOADING
 | 
			
		||||
        if (
 | 
			
		||||
            not USE_FORM_OVERLOADING
 | 
			
		||||
            or self._request.method != 'POST'
 | 
			
		||||
            or not is_form_media_type(self._content_type)):
 | 
			
		||||
            or not is_form_media_type(self._content_type)
 | 
			
		||||
        ):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # At this point we're committed to parsing the request as form data.
 | 
			
		||||
| 
						 | 
				
			
			@ -330,15 +335,19 @@ class Request(object):
 | 
			
		|||
        self._files = self._request.FILES
 | 
			
		||||
 | 
			
		||||
        # Method overloading - change the method and remove the param from the content.
 | 
			
		||||
        if (self._METHOD_PARAM and
 | 
			
		||||
            self._METHOD_PARAM in self._data):
 | 
			
		||||
        if (
 | 
			
		||||
            self._METHOD_PARAM and
 | 
			
		||||
            self._METHOD_PARAM in self._data
 | 
			
		||||
        ):
 | 
			
		||||
            self._method = self._data[self._METHOD_PARAM].upper()
 | 
			
		||||
 | 
			
		||||
        # Content overloading - modify the content type, and force re-parse.
 | 
			
		||||
        if (self._CONTENT_PARAM and
 | 
			
		||||
        if (
 | 
			
		||||
            self._CONTENT_PARAM and
 | 
			
		||||
            self._CONTENTTYPE_PARAM and
 | 
			
		||||
            self._CONTENT_PARAM in self._data and
 | 
			
		||||
            self._CONTENTTYPE_PARAM in self._data):
 | 
			
		||||
            self._CONTENTTYPE_PARAM in self._data
 | 
			
		||||
        ):
 | 
			
		||||
            self._content_type = self._data[self._CONTENTTYPE_PARAM]
 | 
			
		||||
            self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context['encoding']))
 | 
			
		||||
            self._data, self._files = (Empty, Empty)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,8 +62,10 @@ class Response(SimpleTemplateResponse):
 | 
			
		|||
 | 
			
		||||
        ret = renderer.render(self.data, media_type, context)
 | 
			
		||||
        if isinstance(ret, six.text_type):
 | 
			
		||||
            assert charset, 'renderer returned unicode, and did not specify ' \
 | 
			
		||||
            'a charset value.'
 | 
			
		||||
            assert charset, (
 | 
			
		||||
                'renderer returned unicode, and did not specify '
 | 
			
		||||
                'a charset value.'
 | 
			
		||||
            )
 | 
			
		||||
            return bytes(ret.encode(charset))
 | 
			
		||||
 | 
			
		||||
        if not ret:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -449,9 +449,11 @@ class BaseSerializer(WritableField):
 | 
			
		|||
 | 
			
		||||
                # If we have a model manager or similar object then we need
 | 
			
		||||
                # to iterate through each instance.
 | 
			
		||||
                if (self.many and
 | 
			
		||||
                if (
 | 
			
		||||
                    self.many and
 | 
			
		||||
                    not hasattr(obj, '__iter__') and
 | 
			
		||||
                    is_simple_callable(getattr(obj, 'all', None))):
 | 
			
		||||
                    is_simple_callable(getattr(obj, 'all', None))
 | 
			
		||||
                ):
 | 
			
		||||
                    obj = obj.all()
 | 
			
		||||
 | 
			
		||||
                kwargs = {
 | 
			
		||||
| 
						 | 
				
			
			@ -601,8 +603,10 @@ class BaseSerializer(WritableField):
 | 
			
		|||
        API schemas for auto-documentation.
 | 
			
		||||
        """
 | 
			
		||||
        return SortedDict(
 | 
			
		||||
            [(field_name, field.metadata())
 | 
			
		||||
            for field_name, field in six.iteritems(self.fields)]
 | 
			
		||||
            [
 | 
			
		||||
                (field_name, field.metadata())
 | 
			
		||||
                for field_name, field in six.iteritems(self.fields)
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -656,8 +660,10 @@ class ModelSerializer(Serializer):
 | 
			
		|||
        """
 | 
			
		||||
 | 
			
		||||
        cls = self.opts.model
 | 
			
		||||
        assert cls is not None, \
 | 
			
		||||
                "Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__
 | 
			
		||||
        assert cls is not None, (
 | 
			
		||||
            "Serializer class '%s' is missing 'model' Meta option" %
 | 
			
		||||
            self.__class__.__name__
 | 
			
		||||
        )
 | 
			
		||||
        opts = cls._meta.concrete_model._meta
 | 
			
		||||
        ret = SortedDict()
 | 
			
		||||
        nested = bool(self.opts.depth)
 | 
			
		||||
| 
						 | 
				
			
			@ -668,9 +674,9 @@ class ModelSerializer(Serializer):
 | 
			
		|||
            # If model is a child via multitable inheritance, use parent's pk
 | 
			
		||||
            pk_field = pk_field.rel.to._meta.pk
 | 
			
		||||
 | 
			
		||||
        field = self.get_pk_field(pk_field)
 | 
			
		||||
        if field:
 | 
			
		||||
            ret[pk_field.name] = field
 | 
			
		||||
        serializer_pk_field = self.get_pk_field(pk_field)
 | 
			
		||||
        if serializer_pk_field:
 | 
			
		||||
            ret[pk_field.name] = serializer_pk_field
 | 
			
		||||
 | 
			
		||||
        # Deal with forward relationships
 | 
			
		||||
        forward_rels = [field for field in opts.fields if field.serialize]
 | 
			
		||||
| 
						 | 
				
			
			@ -739,9 +745,11 @@ class ModelSerializer(Serializer):
 | 
			
		|||
            is_m2m = isinstance(relation.field,
 | 
			
		||||
                                models.fields.related.ManyToManyField)
 | 
			
		||||
 | 
			
		||||
            if (is_m2m and
 | 
			
		||||
            if (
 | 
			
		||||
                is_m2m and
 | 
			
		||||
                hasattr(relation.field.rel, 'through') and
 | 
			
		||||
                not relation.field.rel.through._meta.auto_created):
 | 
			
		||||
                not relation.field.rel.through._meta.auto_created
 | 
			
		||||
            ):
 | 
			
		||||
                has_through_model = True
 | 
			
		||||
 | 
			
		||||
            if nested:
 | 
			
		||||
| 
						 | 
				
			
			@ -911,10 +919,12 @@ class ModelSerializer(Serializer):
 | 
			
		|||
 | 
			
		||||
        for field_name, field in self.fields.items():
 | 
			
		||||
            field_name = field.source or field_name
 | 
			
		||||
            if field_name in exclusions \
 | 
			
		||||
                and not field.read_only \
 | 
			
		||||
                and (field.required or hasattr(instance, field_name)) \
 | 
			
		||||
                and not isinstance(field, Serializer):
 | 
			
		||||
            if (
 | 
			
		||||
                field_name in exclusions
 | 
			
		||||
                and not field.read_only
 | 
			
		||||
                and (field.required or hasattr(instance, field_name))
 | 
			
		||||
                and not isinstance(field, Serializer)
 | 
			
		||||
            ):
 | 
			
		||||
                exclusions.remove(field_name)
 | 
			
		||||
        return exclusions
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,16 +46,12 @@ DEFAULTS = {
 | 
			
		|||
    'DEFAULT_PERMISSION_CLASSES': (
 | 
			
		||||
        'rest_framework.permissions.AllowAny',
 | 
			
		||||
    ),
 | 
			
		||||
    'DEFAULT_THROTTLE_CLASSES': (
 | 
			
		||||
    ),
 | 
			
		||||
    'DEFAULT_CONTENT_NEGOTIATION_CLASS':
 | 
			
		||||
        'rest_framework.negotiation.DefaultContentNegotiation',
 | 
			
		||||
    'DEFAULT_THROTTLE_CLASSES': (),
 | 
			
		||||
    'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
 | 
			
		||||
 | 
			
		||||
    # Genric view behavior
 | 
			
		||||
    'DEFAULT_MODEL_SERIALIZER_CLASS':
 | 
			
		||||
        'rest_framework.serializers.ModelSerializer',
 | 
			
		||||
    'DEFAULT_PAGINATION_SERIALIZER_CLASS':
 | 
			
		||||
        'rest_framework.pagination.PaginationSerializer',
 | 
			
		||||
    'DEFAULT_MODEL_SERIALIZER_CLASS': 'rest_framework.serializers.ModelSerializer',
 | 
			
		||||
    'DEFAULT_PAGINATION_SERIALIZER_CLASS': 'rest_framework.pagination.PaginationSerializer',
 | 
			
		||||
    'DEFAULT_FILTER_BACKENDS': (),
 | 
			
		||||
 | 
			
		||||
    # Throttling
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,15 +10,19 @@ from __future__ import unicode_literals
 | 
			
		|||
def is_informational(code):
 | 
			
		||||
    return code >= 100 and code <= 199
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_success(code):
 | 
			
		||||
    return code >= 200 and code <= 299
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_redirect(code):
 | 
			
		||||
    return code >= 300 and code <= 399
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_client_error(code):
 | 
			
		||||
    return code >= 400 and code <= 499
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_server_error(code):
 | 
			
		||||
    return code >= 500 and code <= 599
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -152,8 +152,10 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru
 | 
			
		|||
                    middle = middle[len(opening):]
 | 
			
		||||
                    lead = lead + opening
 | 
			
		||||
                # Keep parentheses at the end only if they're balanced.
 | 
			
		||||
                if (middle.endswith(closing)
 | 
			
		||||
                    and middle.count(closing) == middle.count(opening) + 1):
 | 
			
		||||
                if (
 | 
			
		||||
                    middle.endswith(closing)
 | 
			
		||||
                    and middle.count(closing) == middle.count(opening) + 1
 | 
			
		||||
                ):
 | 
			
		||||
                    middle = middle[:-len(closing)]
 | 
			
		||||
                    trail = closing + trail
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,9 +49,10 @@ class APIRequestFactory(DjangoRequestFactory):
 | 
			
		|||
        else:
 | 
			
		||||
            format = format or self.default_format
 | 
			
		||||
 | 
			
		||||
            assert format in self.renderer_classes, ("Invalid format '{0}'. "
 | 
			
		||||
                "Available formats are {1}.  Set TEST_REQUEST_RENDERER_CLASSES "
 | 
			
		||||
                "to enable extra request formats.".format(
 | 
			
		||||
            assert format in self.renderer_classes, (
 | 
			
		||||
                "Invalid format '{0}'. Available formats are {1}. "
 | 
			
		||||
                "Set TEST_REQUEST_RENDERER_CLASSES to enable "
 | 
			
		||||
                "extra request formats.".format(
 | 
			
		||||
                    format,
 | 
			
		||||
                    ', '.join(["'" + fmt + "'" for fmt in self.renderer_classes.keys()])
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,17 +8,19 @@ your API requires authentication:
 | 
			
		|||
        ...
 | 
			
		||||
        url(r'^auth', include('rest_framework.urls', namespace='rest_framework'))
 | 
			
		||||
    )
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
The urls must be namespaced as 'rest_framework', and you should make sure
 | 
			
		||||
your authentication settings include `SessionAuthentication`.
 | 
			
		||||
"""
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
from django.conf.urls import patterns, url
 | 
			
		||||
from django.contrib.auth import views
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
template_name = {'template_name': 'rest_framework/login.html'}
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('django.contrib.auth.views',
 | 
			
		||||
    url(r'^login/$', 'login', template_name, name='login'),
 | 
			
		||||
    url(r'^logout/$', 'logout', template_name, name='logout'),
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^login/$', views.login, template_name, name='login'),
 | 
			
		||||
    url(r'^logout/$', views.logout, template_name, name='logout')
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,14 +98,23 @@ else:
 | 
			
		|||
                    node.flow_style = best_style
 | 
			
		||||
            return node
 | 
			
		||||
 | 
			
		||||
    SafeDumper.add_representer(decimal.Decimal,
 | 
			
		||||
            SafeDumper.represent_decimal)
 | 
			
		||||
 | 
			
		||||
    SafeDumper.add_representer(SortedDict,
 | 
			
		||||
            yaml.representer.SafeRepresenter.represent_dict)
 | 
			
		||||
    SafeDumper.add_representer(DictWithMetadata,
 | 
			
		||||
            yaml.representer.SafeRepresenter.represent_dict)
 | 
			
		||||
    SafeDumper.add_representer(SortedDictWithMetadata,
 | 
			
		||||
            yaml.representer.SafeRepresenter.represent_dict)
 | 
			
		||||
    SafeDumper.add_representer(types.GeneratorType,
 | 
			
		||||
            yaml.representer.SafeRepresenter.represent_list)
 | 
			
		||||
    SafeDumper.add_representer(
 | 
			
		||||
        decimal.Decimal,
 | 
			
		||||
        SafeDumper.represent_decimal
 | 
			
		||||
    )
 | 
			
		||||
    SafeDumper.add_representer(
 | 
			
		||||
        SortedDict,
 | 
			
		||||
        yaml.representer.SafeRepresenter.represent_dict
 | 
			
		||||
    )
 | 
			
		||||
    SafeDumper.add_representer(
 | 
			
		||||
        DictWithMetadata,
 | 
			
		||||
        yaml.representer.SafeRepresenter.represent_dict
 | 
			
		||||
    )
 | 
			
		||||
    SafeDumper.add_representer(
 | 
			
		||||
        SortedDictWithMetadata,
 | 
			
		||||
        yaml.representer.SafeRepresenter.represent_dict
 | 
			
		||||
    )
 | 
			
		||||
    SafeDumper.add_representer(
 | 
			
		||||
        types.GeneratorType,
 | 
			
		||||
        yaml.representer.SafeRepresenter.represent_list
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,8 +6,6 @@ from __future__ import unicode_literals
 | 
			
		|||
from django.utils.html import escape
 | 
			
		||||
from django.utils.safestring import mark_safe
 | 
			
		||||
from rest_framework.compat import apply_markdown
 | 
			
		||||
from rest_framework.settings import api_settings
 | 
			
		||||
from textwrap import dedent
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +38,7 @@ def dedent(content):
 | 
			
		|||
 | 
			
		||||
    return content.strip()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def camelcase_to_spaces(content):
 | 
			
		||||
    """
 | 
			
		||||
    Translate 'CamelCaseNames' to 'Camel Case Names'.
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +48,7 @@ def camelcase_to_spaces(content):
 | 
			
		|||
    content = re.sub(camelcase_boundry, ' \\1', content).strip()
 | 
			
		||||
    return ' '.join(content.split('_')).title()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def markup_description(description):
 | 
			
		||||
    """
 | 
			
		||||
    Apply HTML markup to the given description.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ class _MediaType(object):
 | 
			
		|||
            if key != 'q' and other.params.get(key, None) != self.params.get(key, None):
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
        if self.sub_type != '*' and other.sub_type != '*'  and other.sub_type != self.sub_type:
 | 
			
		||||
        if self.sub_type != '*' and other.sub_type != '*' and other.sub_type != self.sub_type:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        if self.main_type != '*' and other.main_type != '*' and other.main_type != self.main_type:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@ def get_view_name(view_cls, suffix=None):
 | 
			
		|||
 | 
			
		||||
    return name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_view_description(view_cls, html=False):
 | 
			
		||||
    """
 | 
			
		||||
    Given a view class, return a textual description to represent the view.
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +120,6 @@ class APIView(View):
 | 
			
		|||
            headers['Vary'] = 'Accept'
 | 
			
		||||
        return headers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def http_method_not_allowed(self, request, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        If `request.method` does not correspond to a handler method,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -127,11 +127,11 @@ class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class ModelViewSet(mixins.CreateModelMixin,
 | 
			
		||||
                    mixins.RetrieveModelMixin,
 | 
			
		||||
                    mixins.UpdateModelMixin,
 | 
			
		||||
                    mixins.DestroyModelMixin,
 | 
			
		||||
                    mixins.ListModelMixin,
 | 
			
		||||
                    GenericViewSet):
 | 
			
		||||
                   mixins.RetrieveModelMixin,
 | 
			
		||||
                   mixins.UpdateModelMixin,
 | 
			
		||||
                   mixins.DestroyModelMixin,
 | 
			
		||||
                   mixins.ListModelMixin,
 | 
			
		||||
                   GenericViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    A viewset that provides default `create()`, `retrieve()`, `update()`,
 | 
			
		||||
    `partial_update()`, `destroy()` and `list()` actions.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										86
									
								
								runtests.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										86
									
								
								runtests.py
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,86 @@
 | 
			
		|||
#! /usr/bin/env python
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PYTEST_ARGS = {
 | 
			
		||||
    'default': ['tests'],
 | 
			
		||||
    'fast': ['tests', '-q'],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FLAKE8_ARGS = ['rest_framework', 'tests', '--ignore=E501']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sys.path.append(os.path.dirname(__file__))
 | 
			
		||||
 | 
			
		||||
def exit_on_failure(ret, message=None):
 | 
			
		||||
    if ret:
 | 
			
		||||
        sys.exit(ret)
 | 
			
		||||
 | 
			
		||||
def flake8_main(args):
 | 
			
		||||
    print('Running flake8 code linting')
 | 
			
		||||
    ret = subprocess.call(['flake8'] + args)
 | 
			
		||||
    print('flake8 failed' if ret else 'flake8 passed')
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
def split_class_and_function(string):
 | 
			
		||||
    class_string, function_string = string.split('.', 1)
 | 
			
		||||
    return "%s and %s" % (class_string, function_string)
 | 
			
		||||
 | 
			
		||||
def is_function(string):
 | 
			
		||||
    # `True` if it looks like a test function is included in the string.
 | 
			
		||||
    return string.startswith('test_') or '.test_' in string
 | 
			
		||||
 | 
			
		||||
def is_class(string):
 | 
			
		||||
    # `True` if first character is uppercase - assume it's a class name.
 | 
			
		||||
    return string[0] == string[0].upper()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    try:
 | 
			
		||||
        sys.argv.remove('--nolint')
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        run_flake8 = True
 | 
			
		||||
    else:
 | 
			
		||||
        run_flake8 = False
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        sys.argv.remove('--lintonly')
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        run_tests = True
 | 
			
		||||
    else:
 | 
			
		||||
        run_tests = False
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        sys.argv.remove('--fast')
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        style = 'default'
 | 
			
		||||
    else:
 | 
			
		||||
        style = 'fast'
 | 
			
		||||
        run_flake8 = False
 | 
			
		||||
 | 
			
		||||
    if len(sys.argv) > 1:
 | 
			
		||||
        pytest_args = sys.argv[1:]
 | 
			
		||||
        first_arg = pytest_args[0]
 | 
			
		||||
        if first_arg.startswith('-'):
 | 
			
		||||
            # `runtests.py [flags]`
 | 
			
		||||
            pytest_args = ['tests'] + pytest_args
 | 
			
		||||
        elif is_class(first_arg) and is_function(first_arg):
 | 
			
		||||
            # `runtests.py TestCase.test_function [flags]`
 | 
			
		||||
            expression = split_class_and_function(first_arg)
 | 
			
		||||
            pytest_args = ['tests', '-k', expression] + pytest_args[1:]
 | 
			
		||||
        elif is_class(first_arg) or is_function(first_arg):
 | 
			
		||||
            # `runtests.py TestCase [flags]` 
 | 
			
		||||
            # `runtests.py test_function [flags]`
 | 
			
		||||
            pytest_args = ['tests', '-k', pytest_args[0]] + pytest_args[1:]
 | 
			
		||||
    else:
 | 
			
		||||
        pytest_args = PYTEST_ARGS[style]
 | 
			
		||||
 | 
			
		||||
    if run_tests:
 | 
			
		||||
        exit_on_failure(pytest.main(pytest_args))
 | 
			
		||||
    if run_flake8:
 | 
			
		||||
        exit_on_failure(flake8_main(FLAKE8_ARGS))
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,4 @@
 | 
			
		|||
from rest_framework import serializers
 | 
			
		||||
 | 
			
		||||
from tests.models import NullableForeignKeySource
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,7 +68,6 @@ SECRET_KEY = 'u@x-aj9(hoh#rb-^ymf#g2jx_hp0vj7u5#b@ag1n^seu9e!%cy'
 | 
			
		|||
TEMPLATE_LOADERS = (
 | 
			
		||||
    'django.template.loaders.filesystem.Loader',
 | 
			
		||||
    'django.template.loaders.app_directories.Loader',
 | 
			
		||||
#     'django.template.loaders.eggs.Loader',
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
MIDDLEWARE_CLASSES = (
 | 
			
		||||
| 
						 | 
				
			
			@ -104,8 +103,8 @@ INSTALLED_APPS = (
 | 
			
		|||
 | 
			
		||||
# OAuth is optional and won't work if there is no oauth_provider & oauth2
 | 
			
		||||
try:
 | 
			
		||||
    import oauth_provider
 | 
			
		||||
    import oauth2
 | 
			
		||||
    import oauth_provider  # NOQA
 | 
			
		||||
    import oauth2  # NOQA
 | 
			
		||||
except ImportError:
 | 
			
		||||
    pass
 | 
			
		||||
else:
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +113,7 @@ else:
 | 
			
		|||
    )
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import provider
 | 
			
		||||
    import provider  # NOQA
 | 
			
		||||
except ImportError:
 | 
			
		||||
    pass
 | 
			
		||||
else:
 | 
			
		||||
| 
						 | 
				
			
			@ -125,13 +124,13 @@ else:
 | 
			
		|||
 | 
			
		||||
# guardian is optional
 | 
			
		||||
try:
 | 
			
		||||
    import guardian
 | 
			
		||||
    import guardian  # NOQA
 | 
			
		||||
except ImportError:
 | 
			
		||||
    pass
 | 
			
		||||
else:
 | 
			
		||||
    ANONYMOUS_USER_ID = -1
 | 
			
		||||
    AUTHENTICATION_BACKENDS = (
 | 
			
		||||
        'django.contrib.auth.backends.ModelBackend', # default
 | 
			
		||||
        'django.contrib.auth.backends.ModelBackend',  # default
 | 
			
		||||
        'guardian.backends.ObjectPermissionBackend',
 | 
			
		||||
    )
 | 
			
		||||
    INSTALLED_APPS += (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,26 +45,39 @@ class MockView(APIView):
 | 
			
		|||
        return HttpResponse({'a': 1, 'b': 2, 'c': 3})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    (r'^session/$', MockView.as_view(authentication_classes=[SessionAuthentication])),
 | 
			
		||||
    (r'^basic/$', MockView.as_view(authentication_classes=[BasicAuthentication])),
 | 
			
		||||
    (r'^token/$', MockView.as_view(authentication_classes=[TokenAuthentication])),
 | 
			
		||||
    (r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'),
 | 
			
		||||
    (r'^oauth/$', MockView.as_view(authentication_classes=[OAuthAuthentication])),
 | 
			
		||||
    (r'^oauth-with-scope/$', MockView.as_view(authentication_classes=[OAuthAuthentication],
 | 
			
		||||
        permission_classes=[permissions.TokenHasReadWriteScope]))
 | 
			
		||||
    (
 | 
			
		||||
        r'^oauth-with-scope/$',
 | 
			
		||||
        MockView.as_view(
 | 
			
		||||
            authentication_classes=[OAuthAuthentication],
 | 
			
		||||
            permission_classes=[permissions.TokenHasReadWriteScope]
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuth2AuthenticationDebug(OAuth2Authentication):
 | 
			
		||||
    allow_query_params_token = True
 | 
			
		||||
 | 
			
		||||
if oauth2_provider is not None:
 | 
			
		||||
    urlpatterns += patterns('',
 | 
			
		||||
    urlpatterns += patterns(
 | 
			
		||||
        '',
 | 
			
		||||
        url(r'^oauth2/', include('provider.oauth2.urls', namespace='oauth2')),
 | 
			
		||||
        url(r'^oauth2-test/$', MockView.as_view(authentication_classes=[OAuth2Authentication])),
 | 
			
		||||
        url(r'^oauth2-test-debug/$', MockView.as_view(authentication_classes=[OAuth2AuthenticationDebug])),
 | 
			
		||||
        url(r'^oauth2-with-scope-test/$', MockView.as_view(authentication_classes=[OAuth2Authentication],
 | 
			
		||||
            permission_classes=[permissions.TokenHasReadWriteScope])),
 | 
			
		||||
        url(
 | 
			
		||||
            r'^oauth2-with-scope-test/$',
 | 
			
		||||
            MockView.as_view(
 | 
			
		||||
                authentication_classes=[OAuth2Authentication],
 | 
			
		||||
                permission_classes=[permissions.TokenHasReadWriteScope]
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -278,12 +291,16 @@ class OAuthTests(TestCase):
 | 
			
		|||
        self.TOKEN_KEY = "token_key"
 | 
			
		||||
        self.TOKEN_SECRET = "token_secret"
 | 
			
		||||
 | 
			
		||||
        self.consumer = Consumer.objects.create(key=self.CONSUMER_KEY, secret=self.CONSUMER_SECRET,
 | 
			
		||||
            name='example', user=self.user, status=self.consts.ACCEPTED)
 | 
			
		||||
        self.consumer = Consumer.objects.create(
 | 
			
		||||
            key=self.CONSUMER_KEY, secret=self.CONSUMER_SECRET,
 | 
			
		||||
            name='example', user=self.user, status=self.consts.ACCEPTED
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.scope = Scope.objects.create(name="resource name", url="api/")
 | 
			
		||||
        self.token = OAuthToken.objects.create(user=self.user, consumer=self.consumer, scope=self.scope,
 | 
			
		||||
            token_type=OAuthToken.ACCESS, key=self.TOKEN_KEY, secret=self.TOKEN_SECRET, is_approved=True
 | 
			
		||||
        self.token = OAuthToken.objects.create(
 | 
			
		||||
            user=self.user, consumer=self.consumer, scope=self.scope,
 | 
			
		||||
            token_type=OAuthToken.ACCESS, key=self.TOKEN_KEY, secret=self.TOKEN_SECRET,
 | 
			
		||||
            is_approved=True
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _create_authorization_header(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -501,24 +518,24 @@ class OAuth2Tests(TestCase):
 | 
			
		|||
        self.REFRESH_TOKEN = "refresh_token"
 | 
			
		||||
 | 
			
		||||
        self.oauth2_client = oauth2_provider.oauth2.models.Client.objects.create(
 | 
			
		||||
                client_id=self.CLIENT_ID,
 | 
			
		||||
                client_secret=self.CLIENT_SECRET,
 | 
			
		||||
                redirect_uri='',
 | 
			
		||||
                client_type=0,
 | 
			
		||||
                name='example',
 | 
			
		||||
                user=None,
 | 
			
		||||
            )
 | 
			
		||||
            client_id=self.CLIENT_ID,
 | 
			
		||||
            client_secret=self.CLIENT_SECRET,
 | 
			
		||||
            redirect_uri='',
 | 
			
		||||
            client_type=0,
 | 
			
		||||
            name='example',
 | 
			
		||||
            user=None,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.access_token = oauth2_provider.oauth2.models.AccessToken.objects.create(
 | 
			
		||||
                token=self.ACCESS_TOKEN,
 | 
			
		||||
                client=self.oauth2_client,
 | 
			
		||||
                user=self.user,
 | 
			
		||||
            )
 | 
			
		||||
            token=self.ACCESS_TOKEN,
 | 
			
		||||
            client=self.oauth2_client,
 | 
			
		||||
            user=self.user,
 | 
			
		||||
        )
 | 
			
		||||
        self.refresh_token = oauth2_provider.oauth2.models.RefreshToken.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                access_token=self.access_token,
 | 
			
		||||
                client=self.oauth2_client
 | 
			
		||||
            )
 | 
			
		||||
            user=self.user,
 | 
			
		||||
            access_token=self.access_token,
 | 
			
		||||
            client=self.oauth2_client
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _create_authorization_header(self, token=None):
 | 
			
		||||
        return "Bearer {0}".format(token or self.access_token.token)
 | 
			
		||||
| 
						 | 
				
			
			@ -569,8 +586,10 @@ class OAuth2Tests(TestCase):
 | 
			
		|||
    @unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
 | 
			
		||||
    def test_post_form_passing_auth_url_transport(self):
 | 
			
		||||
        """Ensure GETing form over OAuth with correct client credentials in form data succeed"""
 | 
			
		||||
        response = self.csrf_client.post('/oauth2-test/',
 | 
			
		||||
                data={'access_token': self.access_token.token})
 | 
			
		||||
        response = self.csrf_client.post(
 | 
			
		||||
            '/oauth2-test/',
 | 
			
		||||
            data={'access_token': self.access_token.token}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,8 @@ class NestedResourceRoot(APIView):
 | 
			
		|||
class NestedResourceInstance(APIView):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^$', Root.as_view()),
 | 
			
		||||
    url(r'^resource/$', ResourceRoot.as_view()),
 | 
			
		||||
    url(r'^resource/(?P<key>[0-9]+)$', ResourceInstance.as_view()),
 | 
			
		||||
| 
						 | 
				
			
			@ -40,34 +41,60 @@ class BreadcrumbTests(TestCase):
 | 
			
		|||
 | 
			
		||||
    def test_root_breadcrumbs(self):
 | 
			
		||||
        url = '/'
 | 
			
		||||
        self.assertEqual(get_breadcrumbs(url), [('Root', '/')])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_breadcrumbs(url),
 | 
			
		||||
            [('Root', '/')]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_resource_root_breadcrumbs(self):
 | 
			
		||||
        url = '/resource/'
 | 
			
		||||
        self.assertEqual(get_breadcrumbs(url), [('Root', '/'),
 | 
			
		||||
                                            ('Resource Root', '/resource/')])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_breadcrumbs(url),
 | 
			
		||||
            [
 | 
			
		||||
                ('Root', '/'),
 | 
			
		||||
                ('Resource Root', '/resource/')
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_resource_instance_breadcrumbs(self):
 | 
			
		||||
        url = '/resource/123'
 | 
			
		||||
        self.assertEqual(get_breadcrumbs(url), [('Root', '/'),
 | 
			
		||||
                                            ('Resource Root', '/resource/'),
 | 
			
		||||
                                            ('Resource Instance', '/resource/123')])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_breadcrumbs(url),
 | 
			
		||||
            [
 | 
			
		||||
                ('Root', '/'),
 | 
			
		||||
                ('Resource Root', '/resource/'),
 | 
			
		||||
                ('Resource Instance', '/resource/123')
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_nested_resource_breadcrumbs(self):
 | 
			
		||||
        url = '/resource/123/'
 | 
			
		||||
        self.assertEqual(get_breadcrumbs(url), [('Root', '/'),
 | 
			
		||||
                                            ('Resource Root', '/resource/'),
 | 
			
		||||
                                            ('Resource Instance', '/resource/123'),
 | 
			
		||||
                                            ('Nested Resource Root', '/resource/123/')])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_breadcrumbs(url),
 | 
			
		||||
            [
 | 
			
		||||
                ('Root', '/'),
 | 
			
		||||
                ('Resource Root', '/resource/'),
 | 
			
		||||
                ('Resource Instance', '/resource/123'),
 | 
			
		||||
                ('Nested Resource Root', '/resource/123/')
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_nested_resource_instance_breadcrumbs(self):
 | 
			
		||||
        url = '/resource/123/abc'
 | 
			
		||||
        self.assertEqual(get_breadcrumbs(url), [('Root', '/'),
 | 
			
		||||
                                            ('Resource Root', '/resource/'),
 | 
			
		||||
                                            ('Resource Instance', '/resource/123'),
 | 
			
		||||
                                            ('Nested Resource Root', '/resource/123/'),
 | 
			
		||||
                                            ('Nested Resource Instance', '/resource/123/abc')])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_breadcrumbs(url),
 | 
			
		||||
            [
 | 
			
		||||
                ('Root', '/'),
 | 
			
		||||
                ('Resource Root', '/resource/'),
 | 
			
		||||
                ('Resource Instance', '/resource/123'),
 | 
			
		||||
                ('Nested Resource Root', '/resource/123/'),
 | 
			
		||||
                ('Nested Resource Instance', '/resource/123/abc')
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_broken_url_breadcrumbs_handled_gracefully(self):
 | 
			
		||||
        url = '/foobar'
 | 
			
		||||
        self.assertEqual(get_breadcrumbs(url), [('Root', '/')])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_breadcrumbs(url),
 | 
			
		||||
            [('Root', '/')]
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -648,7 +648,7 @@ class DecimalFieldTest(TestCase):
 | 
			
		|||
        s = DecimalSerializer(data={'decimal_field': '123'})
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(s.is_valid())
 | 
			
		||||
        self.assertEqual(s.errors,  {'decimal_field': ['Ensure this value is less than or equal to 100.']})
 | 
			
		||||
        self.assertEqual(s.errors, {'decimal_field': ['Ensure this value is less than or equal to 100.']})
 | 
			
		||||
 | 
			
		||||
    def test_raise_min_value(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -660,7 +660,7 @@ class DecimalFieldTest(TestCase):
 | 
			
		|||
        s = DecimalSerializer(data={'decimal_field': '99'})
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(s.is_valid())
 | 
			
		||||
        self.assertEqual(s.errors,  {'decimal_field': ['Ensure this value is greater than or equal to 100.']})
 | 
			
		||||
        self.assertEqual(s.errors, {'decimal_field': ['Ensure this value is greater than or equal to 100.']})
 | 
			
		||||
 | 
			
		||||
    def test_raise_max_digits(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -672,7 +672,7 @@ class DecimalFieldTest(TestCase):
 | 
			
		|||
        s = DecimalSerializer(data={'decimal_field': '123.456'})
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(s.is_valid())
 | 
			
		||||
        self.assertEqual(s.errors,  {'decimal_field': ['Ensure that there are no more than 5 digits in total.']})
 | 
			
		||||
        self.assertEqual(s.errors, {'decimal_field': ['Ensure that there are no more than 5 digits in total.']})
 | 
			
		||||
 | 
			
		||||
    def test_raise_max_decimal_places(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -684,7 +684,7 @@ class DecimalFieldTest(TestCase):
 | 
			
		|||
        s = DecimalSerializer(data={'decimal_field': '123.4567'})
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(s.is_valid())
 | 
			
		||||
        self.assertEqual(s.errors,  {'decimal_field': ['Ensure that there are no more than 3 decimal places.']})
 | 
			
		||||
        self.assertEqual(s.errors, {'decimal_field': ['Ensure that there are no more than 3 decimal places.']})
 | 
			
		||||
 | 
			
		||||
    def test_raise_max_whole_digits(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -696,7 +696,7 @@ class DecimalFieldTest(TestCase):
 | 
			
		|||
        s = DecimalSerializer(data={'decimal_field': '12345.6'})
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(s.is_valid())
 | 
			
		||||
        self.assertEqual(s.errors,  {'decimal_field': ['Ensure that there are no more than 4 digits in total.']})
 | 
			
		||||
        self.assertEqual(s.errors, {'decimal_field': ['Ensure that there are no more than 4 digits in total.']})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ChoiceFieldTests(TestCase):
 | 
			
		||||
| 
						 | 
				
			
			@ -729,7 +729,7 @@ class ChoiceFieldTests(TestCase):
 | 
			
		|||
    def test_invalid_choice_model(self):
 | 
			
		||||
        s = ChoiceFieldModelSerializer(data={'choice': 'wrong_value'})
 | 
			
		||||
        self.assertFalse(s.is_valid())
 | 
			
		||||
        self.assertEqual(s.errors,  {'choice': ['Select a valid choice. wrong_value is not one of the available choices.']})
 | 
			
		||||
        self.assertEqual(s.errors, {'choice': ['Select a valid choice. wrong_value is not one of the available choices.']})
 | 
			
		||||
        self.assertEqual(s.data['choice'], '')
 | 
			
		||||
 | 
			
		||||
    def test_empty_choice_model(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -875,7 +875,7 @@ class SlugFieldTests(TestCase):
 | 
			
		|||
        s = SlugFieldSerializer(data={'slug_field': 'a b'})
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(s.is_valid(), False)
 | 
			
		||||
        self.assertEqual(s.errors,  {'slug_field': ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."]})
 | 
			
		||||
        self.assertEqual(s.errors, {'slug_field': ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."]})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class URLFieldTests(TestCase):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,11 +85,8 @@ class FileSerializerTests(TestCase):
 | 
			
		|||
        """
 | 
			
		||||
        Validation should still function when no data dictionary is provided.
 | 
			
		||||
        """
 | 
			
		||||
        now = datetime.datetime.now()
 | 
			
		||||
        file = BytesIO(six.b('stuff'))
 | 
			
		||||
        file.name = 'stuff.txt'
 | 
			
		||||
        file.size = len(file.getvalue())
 | 
			
		||||
        uploaded_file = UploadedFile(file=file, created=now)
 | 
			
		||||
 | 
			
		||||
        serializer = UploadedFileSerializer(files={'file': file})
 | 
			
		||||
        uploaded_file = BytesIO(six.b('stuff'))
 | 
			
		||||
        uploaded_file.name = 'stuff.txt'
 | 
			
		||||
        uploaded_file.size = len(uploaded_file.getvalue())
 | 
			
		||||
        serializer = UploadedFileSerializer(files={'file': uploaded_file})
 | 
			
		||||
        self.assertFalse(serializer.is_valid())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,7 +74,8 @@ if django_filters:
 | 
			
		|||
        def get_queryset(self):
 | 
			
		||||
            return FilterableItem.objects.all()
 | 
			
		||||
 | 
			
		||||
    urlpatterns = patterns('',
 | 
			
		||||
    urlpatterns = patterns(
 | 
			
		||||
        '',
 | 
			
		||||
        url(r'^(?P<pk>\d+)/$', FilterClassDetailView.as_view(), name='detail-view'),
 | 
			
		||||
        url(r'^$', FilterClassRootView.as_view(), name='root-view'),
 | 
			
		||||
        url(r'^get-queryset/$', GetQuerysetView.as_view(),
 | 
			
		||||
| 
						 | 
				
			
			@ -653,8 +654,8 @@ class SensitiveOrderingFilterTests(TestCase):
 | 
			
		|||
            self.assertEqual(
 | 
			
		||||
                response.data,
 | 
			
		||||
                [
 | 
			
		||||
                    {'id': 1, username_field: 'userA'}, # PassB
 | 
			
		||||
                    {'id': 2, username_field: 'userB'}, # PassC
 | 
			
		||||
                    {'id': 3, username_field: 'userC'}, # PassA
 | 
			
		||||
                    {'id': 1, username_field: 'userA'},  # PassB
 | 
			
		||||
                    {'id': 2, username_field: 'userB'},  # PassC
 | 
			
		||||
                    {'id': 3, username_field: 'userC'},  # PassA
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,18 +117,18 @@ class TestGenericRelations(TestCase):
 | 
			
		|||
 | 
			
		||||
        serializer = TagSerializer(Tag.objects.all(), many=True)
 | 
			
		||||
        expected = [
 | 
			
		||||
        {
 | 
			
		||||
            'tag': 'django',
 | 
			
		||||
            'tagged_item': 'Bookmark: https://www.djangoproject.com/'
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            'tag': 'python',
 | 
			
		||||
            'tagged_item': 'Bookmark: https://www.djangoproject.com/'
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            'tag': 'reminder',
 | 
			
		||||
            'tagged_item': 'Note: Remember the milk'
 | 
			
		||||
        }
 | 
			
		||||
            {
 | 
			
		||||
                'tag': 'django',
 | 
			
		||||
                'tagged_item': 'Bookmark: https://www.djangoproject.com/'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                'tag': 'python',
 | 
			
		||||
                'tagged_item': 'Bookmark: https://www.djangoproject.com/'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                'tag': 'reminder',
 | 
			
		||||
                'tagged_item': 'Note: Remember the milk'
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(serializer.data, expected)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,8 @@ def not_found(request):
 | 
			
		|||
    raise Http404()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^$', example),
 | 
			
		||||
    url(r'^permission_denied$', permission_denied),
 | 
			
		||||
    url(r'^not_found$', not_found),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,7 +94,8 @@ class OptionalRelationDetail(generics.RetrieveUpdateDestroyAPIView):
 | 
			
		|||
    model_serializer_class = serializers.HyperlinkedModelSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^basic/$', BasicList.as_view(), name='basicmodel-list'),
 | 
			
		||||
    url(r'^basic/(?P<pk>\d+)/$', BasicDetail.as_view(), name='basicmodel-detail'),
 | 
			
		||||
    url(r'^anchor/(?P<pk>\d+)/$', AnchorDetail.as_view(), name='anchor-detail'),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
from __future__ import unicode_literals
 | 
			
		||||
import datetime
 | 
			
		||||
from decimal import Decimal
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.core.paginator import Paginator
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
from django.utils import unittest
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +11,7 @@ from .models import BasicModel, FilterableItem
 | 
			
		|||
 | 
			
		||||
factory = APIRequestFactory()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Helper function to split arguments out of an url
 | 
			
		||||
def split_arguments_from_url(url):
 | 
			
		||||
    if '?' not in url:
 | 
			
		||||
| 
						 | 
				
			
			@ -274,8 +274,8 @@ class TestUnpaginated(TestCase):
 | 
			
		|||
            BasicModel(text=i).save()
 | 
			
		||||
        self.objects = BasicModel.objects
 | 
			
		||||
        self.data = [
 | 
			
		||||
        {'id': obj.id, 'text': obj.text}
 | 
			
		||||
        for obj in self.objects.all()
 | 
			
		||||
            {'id': obj.id, 'text': obj.text}
 | 
			
		||||
            for obj in self.objects.all()
 | 
			
		||||
        ]
 | 
			
		||||
        self.view = DefaultPageSizeKwargView.as_view()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -302,8 +302,8 @@ class TestCustomPaginateByParam(TestCase):
 | 
			
		|||
            BasicModel(text=i).save()
 | 
			
		||||
        self.objects = BasicModel.objects
 | 
			
		||||
        self.data = [
 | 
			
		||||
        {'id': obj.id, 'text': obj.text}
 | 
			
		||||
        for obj in self.objects.all()
 | 
			
		||||
            {'id': obj.id, 'text': obj.text}
 | 
			
		||||
            for obj in self.objects.all()
 | 
			
		||||
        ]
 | 
			
		||||
        self.view = PaginateByParamView.as_view()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -483,8 +483,6 @@ class NonIntegerPaginator(object):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class TestNonIntegerPagination(TestCase):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def test_custom_pagination_serializer(self):
 | 
			
		||||
        objects = ['john', 'paul', 'george', 'ringo']
 | 
			
		||||
        paginator = NonIntegerPaginator(objects, 2)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ import base64
 | 
			
		|||
 | 
			
		||||
factory = APIRequestFactory()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RootView(generics.ListCreateAPIView):
 | 
			
		||||
    model = BasicModel
 | 
			
		||||
    authentication_classes = [authentication.BasicAuthentication]
 | 
			
		||||
| 
						 | 
				
			
			@ -101,42 +102,54 @@ class ModelPermissionsIntegrationTests(TestCase):
 | 
			
		|||
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 | 
			
		||||
 | 
			
		||||
    def test_options_permitted(self):
 | 
			
		||||
        request = factory.options('/',
 | 
			
		||||
                               HTTP_AUTHORIZATION=self.permitted_credentials)
 | 
			
		||||
        request = factory.options(
 | 
			
		||||
            '/',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.permitted_credentials
 | 
			
		||||
        )
 | 
			
		||||
        response = root_view(request, pk='1')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertIn('actions', response.data)
 | 
			
		||||
        self.assertEqual(list(response.data['actions'].keys()), ['POST'])
 | 
			
		||||
 | 
			
		||||
        request = factory.options('/1',
 | 
			
		||||
                               HTTP_AUTHORIZATION=self.permitted_credentials)
 | 
			
		||||
        request = factory.options(
 | 
			
		||||
            '/1',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.permitted_credentials
 | 
			
		||||
        )
 | 
			
		||||
        response = instance_view(request, pk='1')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertIn('actions', response.data)
 | 
			
		||||
        self.assertEqual(list(response.data['actions'].keys()), ['PUT'])
 | 
			
		||||
 | 
			
		||||
    def test_options_disallowed(self):
 | 
			
		||||
        request = factory.options('/',
 | 
			
		||||
                               HTTP_AUTHORIZATION=self.disallowed_credentials)
 | 
			
		||||
        request = factory.options(
 | 
			
		||||
            '/',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.disallowed_credentials
 | 
			
		||||
        )
 | 
			
		||||
        response = root_view(request, pk='1')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertNotIn('actions', response.data)
 | 
			
		||||
 | 
			
		||||
        request = factory.options('/1',
 | 
			
		||||
                               HTTP_AUTHORIZATION=self.disallowed_credentials)
 | 
			
		||||
        request = factory.options(
 | 
			
		||||
            '/1',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.disallowed_credentials
 | 
			
		||||
        )
 | 
			
		||||
        response = instance_view(request, pk='1')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertNotIn('actions', response.data)
 | 
			
		||||
 | 
			
		||||
    def test_options_updateonly(self):
 | 
			
		||||
        request = factory.options('/',
 | 
			
		||||
                               HTTP_AUTHORIZATION=self.updateonly_credentials)
 | 
			
		||||
        request = factory.options(
 | 
			
		||||
            '/',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.updateonly_credentials
 | 
			
		||||
        )
 | 
			
		||||
        response = root_view(request, pk='1')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertNotIn('actions', response.data)
 | 
			
		||||
 | 
			
		||||
        request = factory.options('/1',
 | 
			
		||||
                               HTTP_AUTHORIZATION=self.updateonly_credentials)
 | 
			
		||||
        request = factory.options(
 | 
			
		||||
            '/1',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.updateonly_credentials
 | 
			
		||||
        )
 | 
			
		||||
        response = instance_view(request, pk='1')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertIn('actions', response.data)
 | 
			
		||||
| 
						 | 
				
			
			@ -153,6 +166,7 @@ class BasicPermModel(models.Model):
 | 
			
		|||
            # add, change, delete built in to django
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Custom object-level permission, that includes 'view' permissions
 | 
			
		||||
class ViewObjectPermissions(permissions.DjangoObjectPermissions):
 | 
			
		||||
    perms_map = {
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +219,7 @@ class ObjectPermissionsIntegrationTests(TestCase):
 | 
			
		|||
        app_label = BasicPermModel._meta.app_label
 | 
			
		||||
        f = '{0}_{1}'.format
 | 
			
		||||
        perms = {
 | 
			
		||||
            'view':   f('view', model_name),
 | 
			
		||||
            'view': f('view', model_name),
 | 
			
		||||
            'change': f('change', model_name),
 | 
			
		||||
            'delete': f('delete', model_name)
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -246,21 +260,27 @@ class ObjectPermissionsIntegrationTests(TestCase):
 | 
			
		|||
 | 
			
		||||
    # Update
 | 
			
		||||
    def test_can_update_permissions(self):
 | 
			
		||||
        request = factory.patch('/1', {'text': 'foobar'}, format='json',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.credentials['writeonly'])
 | 
			
		||||
        request = factory.patch(
 | 
			
		||||
            '/1', {'text': 'foobar'}, format='json',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.credentials['writeonly']
 | 
			
		||||
        )
 | 
			
		||||
        response = object_permissions_view(request, pk='1')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertEqual(response.data.get('text'), 'foobar')
 | 
			
		||||
 | 
			
		||||
    def test_cannot_update_permissions(self):
 | 
			
		||||
        request = factory.patch('/1', {'text': 'foobar'}, format='json',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.credentials['deleteonly'])
 | 
			
		||||
        request = factory.patch(
 | 
			
		||||
            '/1', {'text': 'foobar'}, format='json',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.credentials['deleteonly']
 | 
			
		||||
        )
 | 
			
		||||
        response = object_permissions_view(request, pk='1')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
 | 
			
		||||
 | 
			
		||||
    def test_cannot_update_permissions_non_existing(self):
 | 
			
		||||
        request = factory.patch('/999', {'text': 'foobar'}, format='json',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.credentials['deleteonly'])
 | 
			
		||||
        request = factory.patch(
 | 
			
		||||
            '/999', {'text': 'foobar'}, format='json',
 | 
			
		||||
            HTTP_AUTHORIZATION=self.credentials['deleteonly']
 | 
			
		||||
        )
 | 
			
		||||
        response = object_permissions_view(request, pk='999')
 | 
			
		||||
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -108,19 +108,25 @@ class RelatedFieldSourceTests(TestCase):
 | 
			
		|||
        doesn't exist.
 | 
			
		||||
        """
 | 
			
		||||
        from tests.models import ManyToManySource
 | 
			
		||||
 | 
			
		||||
        class Meta:
 | 
			
		||||
            model = ManyToManySource
 | 
			
		||||
 | 
			
		||||
        attrs = {
 | 
			
		||||
            'name': serializers.SlugRelatedField(
 | 
			
		||||
                slug_field='name', source='banzai'),
 | 
			
		||||
            'Meta': Meta,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        TestSerializer = type(str('TestSerializer'),
 | 
			
		||||
            (serializers.ModelSerializer,), attrs)
 | 
			
		||||
        TestSerializer = type(
 | 
			
		||||
            str('TestSerializer'),
 | 
			
		||||
            (serializers.ModelSerializer,),
 | 
			
		||||
            attrs
 | 
			
		||||
        )
 | 
			
		||||
        with self.assertRaises(AttributeError):
 | 
			
		||||
            TestSerializer(data={'name': 'foo'})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@unittest.skipIf(get_version() < '1.6.0', 'Upstream behaviour changed in v1.6')
 | 
			
		||||
class RelatedFieldChoicesTests(TestCase):
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -141,4 +147,3 @@ class RelatedFieldChoicesTests(TestCase):
 | 
			
		|||
        widget_count = len(field.widget.choices)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(widget_count, choice_count + 1, 'BLANK_CHOICE_DASH option should have been added')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,8 @@ request = factory.get('/')  # Just to ensure we have a request in the serializer
 | 
			
		|||
def dummy_view(request, pk):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^dummyurl/(?P<pk>[0-9]+)/$', dummy_view, name='dummy-url'),
 | 
			
		||||
    url(r'^manytomanysource/(?P<pk>[0-9]+)/$', dummy_view, name='manytomanysource-detail'),
 | 
			
		||||
    url(r'^manytomanytarget/(?P<pk>[0-9]+)/$', dummy_view, name='manytomanytarget-detail'),
 | 
			
		||||
| 
						 | 
				
			
			@ -86,9 +87,9 @@ class HyperlinkedManyToManyTests(TestCase):
 | 
			
		|||
        queryset = ManyToManySource.objects.all()
 | 
			
		||||
        serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request})
 | 
			
		||||
        expected = [
 | 
			
		||||
                {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/']},
 | 
			
		||||
                {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']},
 | 
			
		||||
                {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}
 | 
			
		||||
            {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/']},
 | 
			
		||||
            {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']},
 | 
			
		||||
            {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(serializer.data, expected)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,9 +115,9 @@ class HyperlinkedManyToManyTests(TestCase):
 | 
			
		|||
        queryset = ManyToManySource.objects.all()
 | 
			
		||||
        serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request})
 | 
			
		||||
        expected = [
 | 
			
		||||
                {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']},
 | 
			
		||||
                {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']},
 | 
			
		||||
                {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}
 | 
			
		||||
            {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']},
 | 
			
		||||
            {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']},
 | 
			
		||||
            {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(serializer.data, expected)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,9 +65,9 @@ class PKManyToManyTests(TestCase):
 | 
			
		|||
        queryset = ManyToManySource.objects.all()
 | 
			
		||||
        serializer = ManyToManySourceSerializer(queryset, many=True)
 | 
			
		||||
        expected = [
 | 
			
		||||
                {'id': 1, 'name': 'source-1', 'targets': [1]},
 | 
			
		||||
                {'id': 2, 'name': 'source-2', 'targets': [1, 2]},
 | 
			
		||||
                {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}
 | 
			
		||||
            {'id': 1, 'name': 'source-1', 'targets': [1]},
 | 
			
		||||
            {'id': 2, 'name': 'source-2', 'targets': [1, 2]},
 | 
			
		||||
            {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(serializer.data, expected)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -93,9 +93,9 @@ class PKManyToManyTests(TestCase):
 | 
			
		|||
        queryset = ManyToManySource.objects.all()
 | 
			
		||||
        serializer = ManyToManySourceSerializer(queryset, many=True)
 | 
			
		||||
        expected = [
 | 
			
		||||
                {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]},
 | 
			
		||||
                {'id': 2, 'name': 'source-2', 'targets': [1, 2]},
 | 
			
		||||
                {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}
 | 
			
		||||
            {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]},
 | 
			
		||||
            {'id': 2, 'name': 'source-2', 'targets': [1, 2]},
 | 
			
		||||
            {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(serializer.data, expected)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,7 +76,6 @@ class MockGETView(APIView):
 | 
			
		|||
        return Response({'foo': ['bar', 'baz']})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MockPOSTView(APIView):
 | 
			
		||||
    def post(self, request, **kwargs):
 | 
			
		||||
        return Response({'foo': request.DATA})
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +101,8 @@ class HTMLView1(APIView):
 | 
			
		|||
    def get(self, request, **kwargs):
 | 
			
		||||
        return Response('text')
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
 | 
			
		||||
    url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
 | 
			
		||||
    url(r'^cache$', MockGETView.as_view()),
 | 
			
		||||
| 
						 | 
				
			
			@ -312,16 +312,22 @@ class JSONRendererTests(TestCase):
 | 
			
		|||
        class Dict(MutableMapping):
 | 
			
		||||
            def __init__(self):
 | 
			
		||||
                self._dict = dict()
 | 
			
		||||
 | 
			
		||||
            def __getitem__(self, key):
 | 
			
		||||
                return self._dict.__getitem__(key)
 | 
			
		||||
 | 
			
		||||
            def __setitem__(self, key, value):
 | 
			
		||||
                return self._dict.__setitem__(key, value)
 | 
			
		||||
 | 
			
		||||
            def __delitem__(self, key):
 | 
			
		||||
                return self._dict.__delitem__(key)
 | 
			
		||||
 | 
			
		||||
            def __iter__(self):
 | 
			
		||||
                return self._dict.__iter__()
 | 
			
		||||
 | 
			
		||||
            def __len__(self):
 | 
			
		||||
                return self._dict.__len__()
 | 
			
		||||
 | 
			
		||||
            def keys(self):
 | 
			
		||||
                return self._dict.keys()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -330,22 +336,24 @@ class JSONRendererTests(TestCase):
 | 
			
		|||
        x[2] = 3
 | 
			
		||||
        ret = JSONRenderer().render(x)
 | 
			
		||||
        data = json.loads(ret.decode('utf-8'))
 | 
			
		||||
        self.assertEquals(data, {'key': 'string value', '2': 3})    
 | 
			
		||||
        self.assertEquals(data, {'key': 'string value', '2': 3})
 | 
			
		||||
 | 
			
		||||
    def test_render_obj_with_getitem(self):
 | 
			
		||||
        class DictLike(object):
 | 
			
		||||
            def __init__(self):
 | 
			
		||||
                self._dict = {}
 | 
			
		||||
 | 
			
		||||
            def set(self, value):
 | 
			
		||||
                self._dict = dict(value)
 | 
			
		||||
 | 
			
		||||
            def __getitem__(self, key):
 | 
			
		||||
                return self._dict[key]
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
        x = DictLike()
 | 
			
		||||
        x.set({'a': 1, 'b': 'string'})
 | 
			
		||||
        with self.assertRaises(TypeError):
 | 
			
		||||
            JSONRenderer().render(x)
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    def test_without_content_type_args(self):
 | 
			
		||||
        """
 | 
			
		||||
        Test basic JSON rendering.
 | 
			
		||||
| 
						 | 
				
			
			@ -394,35 +402,47 @@ class JSONPRendererTests(TestCase):
 | 
			
		|||
        """
 | 
			
		||||
        Test JSONP rendering with View JSON Renderer.
 | 
			
		||||
        """
 | 
			
		||||
        resp = self.client.get('/jsonp/jsonrenderer',
 | 
			
		||||
                               HTTP_ACCEPT='application/javascript')
 | 
			
		||||
        resp = self.client.get(
 | 
			
		||||
            '/jsonp/jsonrenderer',
 | 
			
		||||
            HTTP_ACCEPT='application/javascript'
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertEqual(resp['Content-Type'], 'application/javascript; charset=utf-8')
 | 
			
		||||
        self.assertEqual(resp.content,
 | 
			
		||||
            ('callback(%s);' % _flat_repr).encode('ascii'))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            resp.content,
 | 
			
		||||
            ('callback(%s);' % _flat_repr).encode('ascii')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_without_callback_without_json_renderer(self):
 | 
			
		||||
        """
 | 
			
		||||
        Test JSONP rendering without View JSON Renderer.
 | 
			
		||||
        """
 | 
			
		||||
        resp = self.client.get('/jsonp/nojsonrenderer',
 | 
			
		||||
                               HTTP_ACCEPT='application/javascript')
 | 
			
		||||
        resp = self.client.get(
 | 
			
		||||
            '/jsonp/nojsonrenderer',
 | 
			
		||||
            HTTP_ACCEPT='application/javascript'
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertEqual(resp['Content-Type'], 'application/javascript; charset=utf-8')
 | 
			
		||||
        self.assertEqual(resp.content,
 | 
			
		||||
            ('callback(%s);' % _flat_repr).encode('ascii'))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            resp.content,
 | 
			
		||||
            ('callback(%s);' % _flat_repr).encode('ascii')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_with_callback(self):
 | 
			
		||||
        """
 | 
			
		||||
        Test JSONP rendering with callback function name.
 | 
			
		||||
        """
 | 
			
		||||
        callback_func = 'myjsonpcallback'
 | 
			
		||||
        resp = self.client.get('/jsonp/nojsonrenderer?callback=' + callback_func,
 | 
			
		||||
                               HTTP_ACCEPT='application/javascript')
 | 
			
		||||
        resp = self.client.get(
 | 
			
		||||
            '/jsonp/nojsonrenderer?callback=' + callback_func,
 | 
			
		||||
            HTTP_ACCEPT='application/javascript'
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
 | 
			
		||||
        self.assertEqual(resp['Content-Type'], 'application/javascript; charset=utf-8')
 | 
			
		||||
        self.assertEqual(resp.content,
 | 
			
		||||
            ('%s(%s);' % (callback_func, _flat_repr)).encode('ascii'))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            resp.content,
 | 
			
		||||
            ('%s(%s);' % (callback_func, _flat_repr)).encode('ascii')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if yaml:
 | 
			
		||||
| 
						 | 
				
			
			@ -467,7 +487,6 @@ if yaml:
 | 
			
		|||
        def assertYAMLContains(self, content, string):
 | 
			
		||||
            self.assertTrue(string in content, '%r not in %r' % (string, content))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class UnicodeYAMLRendererTests(TestCase):
 | 
			
		||||
        """
 | 
			
		||||
        Tests specific for the Unicode YAML Renderer
 | 
			
		||||
| 
						 | 
				
			
			@ -592,13 +611,13 @@ class CacheRenderTest(TestCase):
 | 
			
		|||
        """ Return any errors that would be raised if `obj' is pickled
 | 
			
		||||
        Courtesy of koffie @ http://stackoverflow.com/a/7218986/109897
 | 
			
		||||
        """
 | 
			
		||||
        if seen == None:
 | 
			
		||||
        if seen is None:
 | 
			
		||||
            seen = []
 | 
			
		||||
        try:
 | 
			
		||||
            state = obj.__getstate__()
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            return
 | 
			
		||||
        if state == None:
 | 
			
		||||
        if state is None:
 | 
			
		||||
            return
 | 
			
		||||
        if isinstance(state, tuple):
 | 
			
		||||
            if not isinstance(state[0], dict):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -272,7 +272,8 @@ class MockView(APIView):
 | 
			
		|||
 | 
			
		||||
        return Response(status=status.INTERNAL_SERVER_ERROR)
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    (r'^$', MockView.as_view()),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,7 +100,8 @@ new_model_viewset_router = routers.DefaultRouter()
 | 
			
		|||
new_model_viewset_router.register(r'', HTMLNewModelViewSet)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^setbyview$', MockViewSettingContentType.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
 | 
			
		||||
    url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
 | 
			
		||||
    url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,8 @@ factory = APIRequestFactory()
 | 
			
		|||
def null_view(request):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^view$', null_view, name='view'),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,7 +93,8 @@ class TestCustomLookupFields(TestCase):
 | 
			
		|||
 | 
			
		||||
        from tests import test_routers
 | 
			
		||||
        urls = getattr(test_routers, 'urlpatterns')
 | 
			
		||||
        urls += patterns('',
 | 
			
		||||
        urls += patterns(
 | 
			
		||||
            '',
 | 
			
		||||
            url(r'^', include(self.router.urls)),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +105,8 @@ class TestCustomLookupFields(TestCase):
 | 
			
		|||
 | 
			
		||||
    def test_retrieve_lookup_field_list_view(self):
 | 
			
		||||
        response = self.client.get('/notes/')
 | 
			
		||||
        self.assertEqual(response.data,
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            response.data,
 | 
			
		||||
            [{
 | 
			
		||||
                "url": "http://testserver/notes/123/",
 | 
			
		||||
                "uuid": "123", "text": "foo bar"
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +115,8 @@ class TestCustomLookupFields(TestCase):
 | 
			
		|||
 | 
			
		||||
    def test_retrieve_lookup_field_detail_view(self):
 | 
			
		||||
        response = self.client.get('/notes/123/')
 | 
			
		||||
        self.assertEqual(response.data,
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            response.data,
 | 
			
		||||
            {
 | 
			
		||||
                "url": "http://testserver/notes/123/",
 | 
			
		||||
                "uuid": "123", "text": "foo bar"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,10 +7,12 @@ from django.utils import unittest
 | 
			
		|||
from django.utils.datastructures import MultiValueDict
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from rest_framework import serializers, fields, relations
 | 
			
		||||
from tests.models import (HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel,
 | 
			
		||||
    BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, DefaultValueModel,
 | 
			
		||||
    ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel,
 | 
			
		||||
    ForeignKeySource, ManyToManySource)
 | 
			
		||||
from tests.models import (
 | 
			
		||||
    HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel,
 | 
			
		||||
    BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel,
 | 
			
		||||
    DefaultValueModel, ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo,
 | 
			
		||||
    RESTFrameworkModel, ForeignKeySource
 | 
			
		||||
)
 | 
			
		||||
from tests.models import BasicModelSerializer
 | 
			
		||||
import datetime
 | 
			
		||||
import pickle
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +101,7 @@ class ActionItemSerializer(serializers.ModelSerializer):
 | 
			
		|||
    class Meta:
 | 
			
		||||
        model = ActionItem
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ActionItemSerializerOptionalFields(serializers.ModelSerializer):
 | 
			
		||||
    """
 | 
			
		||||
    Intended to test that fields with `required=False` are excluded from validation.
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +112,7 @@ class ActionItemSerializerOptionalFields(serializers.ModelSerializer):
 | 
			
		|||
        model = ActionItem
 | 
			
		||||
        fields = ('title',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ActionItemSerializerCustomRestore(serializers.ModelSerializer):
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
| 
						 | 
				
			
			@ -295,8 +299,10 @@ class BasicTests(TestCase):
 | 
			
		|||
        in the Meta data
 | 
			
		||||
        """
 | 
			
		||||
        serializer = PersonSerializer(self.person)
 | 
			
		||||
        self.assertEqual(set(serializer.data.keys()),
 | 
			
		||||
                          set(['name', 'age', 'info']))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            set(serializer.data.keys()),
 | 
			
		||||
            set(['name', 'age', 'info'])
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_field_with_dictionary(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -331,9 +337,9 @@ class BasicTests(TestCase):
 | 
			
		|||
            — id field is not populated if `data` is accessed prior to `save()`
 | 
			
		||||
        """
 | 
			
		||||
        serializer = ActionItemSerializer(self.actionitem)
 | 
			
		||||
        self.assertIsNone(serializer.data.get('id',None), 'New instance. `id` should not be set.')
 | 
			
		||||
        self.assertIsNone(serializer.data.get('id', None), 'New instance. `id` should not be set.')
 | 
			
		||||
        serializer.save()
 | 
			
		||||
        self.assertIsNotNone(serializer.data.get('id',None), 'Model is saved. `id` should be set.')
 | 
			
		||||
        self.assertIsNotNone(serializer.data.get('id', None), 'Model is saved. `id` should be set.')
 | 
			
		||||
 | 
			
		||||
    def test_fields_marked_as_not_required_are_excluded_from_validation(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -660,10 +666,10 @@ class ModelValidationTests(TestCase):
 | 
			
		|||
        serializer.save()
 | 
			
		||||
        second_serializer = AlbumsSerializer(data={'title': 'a'})
 | 
			
		||||
        self.assertFalse(second_serializer.is_valid())
 | 
			
		||||
        self.assertEqual(second_serializer.errors,  {'title': ['Album with this Title already exists.'],})
 | 
			
		||||
        self.assertEqual(second_serializer.errors, {'title': ['Album with this Title already exists.']})
 | 
			
		||||
        third_serializer = AlbumsSerializer(data=[{'title': 'b', 'ref': '1'}, {'title': 'c'}], many=True)
 | 
			
		||||
        self.assertFalse(third_serializer.is_valid())
 | 
			
		||||
        self.assertEqual(third_serializer.errors,  [{'ref': ['Album with this Ref already exists.']}, {}])
 | 
			
		||||
        self.assertEqual(third_serializer.errors, [{'ref': ['Album with this Ref already exists.']}, {}])
 | 
			
		||||
 | 
			
		||||
    def test_foreign_key_is_null_with_partial(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -959,7 +965,7 @@ class WritableFieldDefaultValueTests(TestCase):
 | 
			
		|||
        self.assertEqual(got, self.expected)
 | 
			
		||||
 | 
			
		||||
    def test_get_default_value_with_callable(self):
 | 
			
		||||
        field = self.create_field(default=lambda : self.expected)
 | 
			
		||||
        field = self.create_field(default=lambda: self.expected)
 | 
			
		||||
        got = field.get_default_value()
 | 
			
		||||
        self.assertEqual(got, self.expected)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -974,7 +980,7 @@ class WritableFieldDefaultValueTests(TestCase):
 | 
			
		|||
        self.assertIsNone(got)
 | 
			
		||||
 | 
			
		||||
    def test_get_default_value_returns_non_True_values(self):
 | 
			
		||||
        values = [None, '', False, 0, [], (), {}] # values that assumed as 'False' in the 'if' clause
 | 
			
		||||
        values = [None, '', False, 0, [], (), {}]  # values that assumed as 'False' in the 'if' clause
 | 
			
		||||
        for expected in values:
 | 
			
		||||
            field = self.create_field(default=expected)
 | 
			
		||||
            got = field.get_default_value()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,9 +83,9 @@ class BulkCreateSerializerTests(TestCase):
 | 
			
		|||
        self.assertEqual(serializer.is_valid(), False)
 | 
			
		||||
 | 
			
		||||
        expected_errors = [
 | 
			
		||||
                {'non_field_errors': ['Invalid data']},
 | 
			
		||||
                {'non_field_errors': ['Invalid data']},
 | 
			
		||||
                {'non_field_errors': ['Invalid data']}
 | 
			
		||||
            {'non_field_errors': ['Invalid data']},
 | 
			
		||||
            {'non_field_errors': ['Invalid data']},
 | 
			
		||||
            {'non_field_errors': ['Invalid data']}
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(serializer.errors, expected_errors)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -328,12 +328,14 @@ class NestedModelSerializerUpdateTests(TestCase):
 | 
			
		|||
 | 
			
		||||
        class BlogPostSerializer(serializers.ModelSerializer):
 | 
			
		||||
            comments = BlogPostCommentSerializer(many=True, source='blogpostcomment_set')
 | 
			
		||||
 | 
			
		||||
            class Meta:
 | 
			
		||||
                model = models.BlogPost
 | 
			
		||||
                fields = ('id', 'title', 'comments')
 | 
			
		||||
 | 
			
		||||
        class PersonSerializer(serializers.ModelSerializer):
 | 
			
		||||
            posts = BlogPostSerializer(many=True, source='blogpost_set')
 | 
			
		||||
 | 
			
		||||
            class Meta:
 | 
			
		||||
                model = models.Person
 | 
			
		||||
                fields = ('id', 'name', 'age', 'posts')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,7 @@
 | 
			
		|||
from django.db import models
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
from rest_framework.compat import six
 | 
			
		||||
from rest_framework.serializers import _resolve_model
 | 
			
		||||
from tests.models import BasicModel
 | 
			
		||||
from rest_framework.compat import six
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResolveModelTests(TestCase):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,4 +30,4 @@ class TestStatus(TestCase):
 | 
			
		|||
        self.assertFalse(is_server_error(499))
 | 
			
		||||
        self.assertTrue(is_server_error(500))
 | 
			
		||||
        self.assertTrue(is_server_error(599))
 | 
			
		||||
        self.assertFalse(is_server_error(600))
 | 
			
		||||
        self.assertFalse(is_server_error(600))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,4 +48,4 @@ class Issue1386Tests(TestCase):
 | 
			
		|||
            self.assertEqual(i, res)
 | 
			
		||||
 | 
			
		||||
        # example from issue #1386, this shouldn't raise an exception
 | 
			
		||||
        _ = urlize_quoted_links("asdf:[/p]zxcv.com")
 | 
			
		||||
        urlize_quoted_links("asdf:[/p]zxcv.com")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,8 @@ def session_view(request):
 | 
			
		|||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
urlpatterns = patterns('',
 | 
			
		||||
urlpatterns = patterns(
 | 
			
		||||
    '',
 | 
			
		||||
    url(r'^view/$', view),
 | 
			
		||||
    url(r'^session-view/$', session_view),
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +143,8 @@ class TestAPIRequestFactory(TestCase):
 | 
			
		|||
        assertion error.
 | 
			
		||||
        """
 | 
			
		||||
        factory = APIRequestFactory()
 | 
			
		||||
        self.assertRaises(AssertionError, factory.post,
 | 
			
		||||
        self.assertRaises(
 | 
			
		||||
            AssertionError, factory.post,
 | 
			
		||||
            path='/view/', data={'example': 1}, format='xml'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ class NonTimeThrottle(BaseThrottle):
 | 
			
		|||
        if not hasattr(self.__class__, 'called'):
 | 
			
		||||
            self.__class__.called = True
 | 
			
		||||
            return True
 | 
			
		||||
        return False 
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MockView(APIView):
 | 
			
		||||
| 
						 | 
				
			
			@ -125,36 +125,42 @@ class ThrottlingTests(TestCase):
 | 
			
		|||
        """
 | 
			
		||||
        Ensure for second based throttles.
 | 
			
		||||
        """
 | 
			
		||||
        self.ensure_response_header_contains_proper_throttle_field(MockView,
 | 
			
		||||
         ((0, None),
 | 
			
		||||
          (0, None),
 | 
			
		||||
          (0, None),
 | 
			
		||||
          (0, '1')
 | 
			
		||||
         ))
 | 
			
		||||
        self.ensure_response_header_contains_proper_throttle_field(
 | 
			
		||||
            MockView, (
 | 
			
		||||
                (0, None),
 | 
			
		||||
                (0, None),
 | 
			
		||||
                (0, None),
 | 
			
		||||
                (0, '1')
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_minutes_fields(self):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure for minute based throttles.
 | 
			
		||||
        """
 | 
			
		||||
        self.ensure_response_header_contains_proper_throttle_field(MockView_MinuteThrottling,
 | 
			
		||||
         ((0, None),
 | 
			
		||||
          (0, None),
 | 
			
		||||
          (0, None),
 | 
			
		||||
          (0, '60')
 | 
			
		||||
         ))
 | 
			
		||||
        self.ensure_response_header_contains_proper_throttle_field(
 | 
			
		||||
            MockView_MinuteThrottling, (
 | 
			
		||||
                (0, None),
 | 
			
		||||
                (0, None),
 | 
			
		||||
                (0, None),
 | 
			
		||||
                (0, '60')
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_next_rate_remains_constant_if_followed(self):
 | 
			
		||||
        """
 | 
			
		||||
        If a client follows the recommended next request rate,
 | 
			
		||||
        the throttling rate should stay constant.
 | 
			
		||||
        """
 | 
			
		||||
        self.ensure_response_header_contains_proper_throttle_field(MockView_MinuteThrottling,
 | 
			
		||||
         ((0, None),
 | 
			
		||||
          (20, None),
 | 
			
		||||
          (40, None),
 | 
			
		||||
          (60, None),
 | 
			
		||||
          (80, None)
 | 
			
		||||
         ))
 | 
			
		||||
        self.ensure_response_header_contains_proper_throttle_field(
 | 
			
		||||
            MockView_MinuteThrottling, (
 | 
			
		||||
                (0, None),
 | 
			
		||||
                (20, None),
 | 
			
		||||
                (40, None),
 | 
			
		||||
                (60, None),
 | 
			
		||||
                (80, None)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_non_time_throttle(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +176,7 @@ class ThrottlingTests(TestCase):
 | 
			
		|||
        self.assertTrue(MockView_NonTimeThrottling.throttle_classes[0].called)
 | 
			
		||||
 | 
			
		||||
        response = MockView_NonTimeThrottling.as_view()(request)
 | 
			
		||||
        self.assertFalse('X-Throttle-Wait-Seconds' in response) 
 | 
			
		||||
        self.assertFalse('X-Throttle-Wait-Seconds' in response)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScopedRateThrottleTests(TestCase):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
from __future__ import unicode_literals
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
from rest_framework.templatetags.rest_framework import urlize_quoted_links
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class URLizerTests(TestCase):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								tox.ini
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,13 +1,19 @@
 | 
			
		|||
[tox]
 | 
			
		||||
downloadcache = {toxworkdir}/cache/
 | 
			
		||||
envlist =
 | 
			
		||||
       flake8,
 | 
			
		||||
       py3.4-django1.7,py3.3-django1.7,py3.2-django1.7,py2.7-django1.7,
 | 
			
		||||
       py3.4-django1.6,py3.3-django1.6,py3.2-django1.6,py2.7-django1.6,py2.6-django1.6,
 | 
			
		||||
       py3.4-django1.5,py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.6-django1.5,
 | 
			
		||||
       py2.7-django1.4,py2.6-django1.4,
 | 
			
		||||
       py2.7-django1.4,py2.6-django1.4
 | 
			
		||||
 | 
			
		||||
[testenv]
 | 
			
		||||
commands = py.test -q
 | 
			
		||||
commands = ./runtests.py --fast
 | 
			
		||||
 | 
			
		||||
[testenv:flake8]
 | 
			
		||||
basepython = python2.7
 | 
			
		||||
deps = pytest==2.5.2
 | 
			
		||||
commands = ./runtests.py --lintonly
 | 
			
		||||
 | 
			
		||||
[testenv:py3.4-django1.7]
 | 
			
		||||
basepython = python3.4
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user