mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-27 20:14:01 +03:00
Dropped Python 2 compatibility. (#6615)
Thanks to Jon Dufresne (@jdufresne) for review. Co-authored-by: Asif Saif Uddin <auvipy@gmail.com> Co-authored-by: Rizwan Mansuri <Rizwan@webbyfox.com>
This commit is contained in:
parent
5c992baf32
commit
0407a0df8a
|
@ -4,7 +4,6 @@ dist: xenial
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- { python: "2.7", env: DJANGO=1.11 }
|
|
||||||
|
|
||||||
- { python: "3.4", env: DJANGO=1.11 }
|
- { python: "3.4", env: DJANGO=1.11 }
|
||||||
- { python: "3.4", env: DJANGO=2.0 }
|
- { python: "3.4", env: DJANGO=2.0 }
|
||||||
|
@ -26,8 +25,8 @@ matrix:
|
||||||
- { python: "3.7", env: DJANGO=master }
|
- { python: "3.7", env: DJANGO=master }
|
||||||
|
|
||||||
- { python: "3.7", env: TOXENV=base }
|
- { python: "3.7", env: TOXENV=base }
|
||||||
- { python: "2.7", env: TOXENV=lint }
|
- { python: "3.7", env: TOXENV=lint }
|
||||||
- { python: "2.7", env: TOXENV=docs }
|
- { python: "3.7", env: TOXENV=docs }
|
||||||
|
|
||||||
- python: "3.7"
|
- python: "3.7"
|
||||||
env: TOXENV=dist
|
env: TOXENV=dist
|
||||||
|
|
|
@ -53,7 +53,7 @@ There is a live example API for testing purposes, [available here][sandbox].
|
||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
|
|
||||||
* Python (2.7, 3.4, 3.5, 3.6, 3.7)
|
* Python (3.4, 3.5, 3.6, 3.7)
|
||||||
* Django (1.11, 2.0, 2.1, 2.2)
|
* Django (1.11, 2.0, 2.1, 2.2)
|
||||||
|
|
||||||
We **highly recommend** and only officially support the latest patch release of
|
We **highly recommend** and only officially support the latest patch release of
|
||||||
|
|
|
@ -629,7 +629,7 @@ Our `ColorField` class above currently does not perform any data validation.
|
||||||
To indicate invalid data, we should raise a `serializers.ValidationError`, like so:
|
To indicate invalid data, we should raise a `serializers.ValidationError`, like so:
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if not isinstance(data, six.text_type):
|
if not isinstance(data, str):
|
||||||
msg = 'Incorrect type. Expected a string, but got %s'
|
msg = 'Incorrect type. Expected a string, but got %s'
|
||||||
raise ValidationError(msg % type(data).__name__)
|
raise ValidationError(msg % type(data).__name__)
|
||||||
|
|
||||||
|
@ -653,7 +653,7 @@ The `.fail()` method is a shortcut for raising `ValidationError` that takes a me
|
||||||
}
|
}
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if not isinstance(data, six.text_type):
|
if not isinstance(data, str):
|
||||||
self.fail('incorrect_type', input_type=type(data).__name__)
|
self.fail('incorrect_type', input_type=type(data).__name__)
|
||||||
|
|
||||||
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
|
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
"""
|
"""
|
||||||
Provides various authentication policies.
|
Provides various authentication policies.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
|
|
||||||
from django.contrib.auth import authenticate, get_user_model
|
from django.contrib.auth import authenticate, get_user_model
|
||||||
from django.middleware.csrf import CsrfViewMiddleware
|
from django.middleware.csrf import CsrfViewMiddleware
|
||||||
from django.utils.six import text_type
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
||||||
|
@ -21,7 +18,7 @@ def get_authorization_header(request):
|
||||||
Hide some test client ickyness where the header can be unicode.
|
Hide some test client ickyness where the header can be unicode.
|
||||||
"""
|
"""
|
||||||
auth = request.META.get('HTTP_AUTHORIZATION', b'')
|
auth = request.META.get('HTTP_AUTHORIZATION', b'')
|
||||||
if isinstance(auth, text_type):
|
if isinstance(auth, str):
|
||||||
# Work around django test client oddness
|
# Work around django test client oddness
|
||||||
auth = auth.encode(HTTP_HEADER_ENCODING)
|
auth = auth.encode(HTTP_HEADER_ENCODING)
|
||||||
return auth
|
return auth
|
||||||
|
@ -33,7 +30,7 @@ class CSRFCheck(CsrfViewMiddleware):
|
||||||
return reason
|
return reason
|
||||||
|
|
||||||
|
|
||||||
class BaseAuthentication(object):
|
class BaseAuthentication:
|
||||||
"""
|
"""
|
||||||
All authentication classes should extend BaseAuthentication.
|
All authentication classes should extend BaseAuthentication.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -38,8 +38,8 @@ class Command(BaseCommand):
|
||||||
token = self.create_user_token(username, reset_token)
|
token = self.create_user_token(username, reset_token)
|
||||||
except UserModel.DoesNotExist:
|
except UserModel.DoesNotExist:
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
'Cannot create the Token: user {0} does not exist'.format(
|
'Cannot create the Token: user {} does not exist'.format(
|
||||||
username)
|
username)
|
||||||
)
|
)
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
'Generated token {0} for user {1}'.format(token.key, username))
|
'Generated token {} for user {}'.format(token.key, username))
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,9 @@ import os
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Token(models.Model):
|
class Token(models.Model):
|
||||||
"""
|
"""
|
||||||
The default authorization token model.
|
The default authorization token model.
|
||||||
|
@ -32,7 +30,7 @@ class Token(models.Model):
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.key:
|
if not self.key:
|
||||||
self.key = self.generate_key()
|
self.key = self.generate_key()
|
||||||
return super(Token, self).save(*args, **kwargs)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
def generate_key(self):
|
def generate_key(self):
|
||||||
return binascii.hexlify(os.urandom(20)).decode()
|
return binascii.hexlify(os.urandom(20)).decode()
|
||||||
|
|
|
@ -2,23 +2,13 @@
|
||||||
The `compat` module provides support for backwards compatibility with older
|
The `compat` module provides support for backwards compatibility with older
|
||||||
versions of Django/Python, and compatibility wrappers around optional packages.
|
versions of Django/Python, and compatibility wrappers around optional packages.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
from collections.abc import Mapping, MutableMapping # noqa
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.utils import six
|
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
try:
|
|
||||||
# Python 3
|
|
||||||
from collections.abc import Mapping, MutableMapping # noqa
|
|
||||||
except ImportError:
|
|
||||||
# Python 2.7
|
|
||||||
from collections import Mapping, MutableMapping # noqa
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from django.urls import ( # noqa
|
from django.urls import ( # noqa
|
||||||
URLPattern,
|
URLPattern,
|
||||||
|
@ -36,11 +26,6 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ProhibitNullCharactersValidator = None
|
ProhibitNullCharactersValidator = None
|
||||||
|
|
||||||
try:
|
|
||||||
from unittest import mock
|
|
||||||
except ImportError:
|
|
||||||
mock = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_original_route(urlpattern):
|
def get_original_route(urlpattern):
|
||||||
"""
|
"""
|
||||||
|
@ -89,23 +74,6 @@ def make_url_resolver(regex, urlpatterns):
|
||||||
return URLResolver(regex, urlpatterns)
|
return URLResolver(regex, urlpatterns)
|
||||||
|
|
||||||
|
|
||||||
def unicode_repr(instance):
|
|
||||||
# Get the repr of an instance, but ensure it is a unicode string
|
|
||||||
# on both python 3 (already the case) and 2 (not the case).
|
|
||||||
if six.PY2:
|
|
||||||
return repr(instance).decode('utf-8')
|
|
||||||
return repr(instance)
|
|
||||||
|
|
||||||
|
|
||||||
def unicode_to_repr(value):
|
|
||||||
# Coerce a unicode string to the correct repr return type, depending on
|
|
||||||
# the Python version. We wrap all our `__repr__` implementations with
|
|
||||||
# this and then use unicode throughout internally.
|
|
||||||
if six.PY2:
|
|
||||||
return value.encode('utf-8')
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def unicode_http_header(value):
|
def unicode_http_header(value):
|
||||||
# Coerce HTTP header value to unicode.
|
# Coerce HTTP header value to unicode.
|
||||||
if isinstance(value, bytes):
|
if isinstance(value, bytes):
|
||||||
|
@ -168,15 +136,6 @@ def is_guardian_installed():
|
||||||
"""
|
"""
|
||||||
django-guardian is optional and only imported if in INSTALLED_APPS.
|
django-guardian is optional and only imported if in INSTALLED_APPS.
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
import guardian
|
|
||||||
except ImportError:
|
|
||||||
guardian = None
|
|
||||||
|
|
||||||
if six.PY2 and (not guardian or guardian.VERSION >= (1, 5)):
|
|
||||||
# Guardian 1.5.0, for Django 2.2 is NOT compatible with Python 2.7.
|
|
||||||
# Remove when dropping PY2.
|
|
||||||
return False
|
|
||||||
return 'guardian' in settings.INSTALLED_APPS
|
return 'guardian' in settings.INSTALLED_APPS
|
||||||
|
|
||||||
|
|
||||||
|
@ -289,17 +248,12 @@ except ImportError:
|
||||||
|
|
||||||
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
|
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
|
||||||
# See: https://bugs.python.org/issue22767
|
# See: https://bugs.python.org/issue22767
|
||||||
if six.PY3:
|
SHORT_SEPARATORS = (',', ':')
|
||||||
SHORT_SEPARATORS = (',', ':')
|
LONG_SEPARATORS = (', ', ': ')
|
||||||
LONG_SEPARATORS = (', ', ': ')
|
INDENT_SEPARATORS = (',', ': ')
|
||||||
INDENT_SEPARATORS = (',', ': ')
|
|
||||||
else:
|
|
||||||
SHORT_SEPARATORS = (b',', b':')
|
|
||||||
LONG_SEPARATORS = (b', ', b': ')
|
|
||||||
INDENT_SEPARATORS = (b',', b': ')
|
|
||||||
|
|
||||||
|
|
||||||
class CustomValidatorMessage(object):
|
class CustomValidatorMessage:
|
||||||
"""
|
"""
|
||||||
We need to avoid evaluation of `lazy` translated `message` in `django.core.validators.BaseValidator.__init__`.
|
We need to avoid evaluation of `lazy` translated `message` in `django.core.validators.BaseValidator.__init__`.
|
||||||
https://github.com/django/django/blob/75ed5900321d170debef4ac452b8b3cf8a1c2384/django/core/validators.py#L297
|
https://github.com/django/django/blob/75ed5900321d170debef4ac452b8b3cf8a1c2384/django/core/validators.py#L297
|
||||||
|
@ -309,7 +263,7 @@ class CustomValidatorMessage(object):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.message = kwargs.pop('message', self.message)
|
self.message = kwargs.pop('message', self.message)
|
||||||
super(CustomValidatorMessage, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class MinValueValidator(CustomValidatorMessage, validators.MinValueValidator):
|
class MinValueValidator(CustomValidatorMessage, validators.MinValueValidator):
|
||||||
|
|
|
@ -6,13 +6,10 @@ There are also various decorators for setting the API policies on function
|
||||||
based views, as well as the `@detail_route` and `@list_route` decorators, which are
|
based views, as well as the `@detail_route` and `@list_route` decorators, which are
|
||||||
used to annotate methods on viewsets that should be included by routers.
|
used to annotate methods on viewsets that should be included by routers.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import types
|
import types
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django.forms.utils import pretty_name
|
from django.forms.utils import pretty_name
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import RemovedInDRF310Warning
|
from rest_framework import RemovedInDRF310Warning
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
@ -28,7 +25,7 @@ def api_view(http_method_names=None):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
|
|
||||||
WrappedAPIView = type(
|
WrappedAPIView = type(
|
||||||
six.PY3 and 'WrappedAPIView' or b'WrappedAPIView',
|
'WrappedAPIView',
|
||||||
(APIView,),
|
(APIView,),
|
||||||
{'__doc__': func.__doc__}
|
{'__doc__': func.__doc__}
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,18 +4,14 @@ Handled exceptions raised by REST framework.
|
||||||
In addition Django's built in 403 and 404 exceptions are handled.
|
In addition Django's built in 403 and 404 exceptions are handled.
|
||||||
(`django.http.Http404` and `django.core.exceptions.PermissionDenied`)
|
(`django.http.Http404` and `django.core.exceptions.PermissionDenied`)
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.utils import six
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ungettext
|
from django.utils.translation import ungettext
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.compat import unicode_to_repr
|
|
||||||
from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
|
from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,19 +60,19 @@ def _get_full_details(detail):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ErrorDetail(six.text_type):
|
class ErrorDetail(str):
|
||||||
"""
|
"""
|
||||||
A string-like object that can additionally have a code.
|
A string-like object that can additionally have a code.
|
||||||
"""
|
"""
|
||||||
code = None
|
code = None
|
||||||
|
|
||||||
def __new__(cls, string, code=None):
|
def __new__(cls, string, code=None):
|
||||||
self = super(ErrorDetail, cls).__new__(cls, string)
|
self = super().__new__(cls, string)
|
||||||
self.code = code
|
self.code = code
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
r = super(ErrorDetail, self).__eq__(other)
|
r = super().__eq__(other)
|
||||||
try:
|
try:
|
||||||
return r and self.code == other.code
|
return r and self.code == other.code
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -86,10 +82,10 @@ class ErrorDetail(six.text_type):
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr('ErrorDetail(string=%r, code=%r)' % (
|
return 'ErrorDetail(string=%r, code=%r)' % (
|
||||||
six.text_type(self),
|
str(self),
|
||||||
self.code,
|
self.code,
|
||||||
))
|
)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(str(self))
|
return hash(str(self))
|
||||||
|
@ -113,7 +109,7 @@ class APIException(Exception):
|
||||||
self.detail = _get_error_details(detail, code)
|
self.detail = _get_error_details(detail, code)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return six.text_type(self.detail)
|
return str(self.detail)
|
||||||
|
|
||||||
def get_codes(self):
|
def get_codes(self):
|
||||||
"""
|
"""
|
||||||
|
@ -196,7 +192,7 @@ class MethodNotAllowed(APIException):
|
||||||
def __init__(self, method, detail=None, code=None):
|
def __init__(self, method, detail=None, code=None):
|
||||||
if detail is None:
|
if detail is None:
|
||||||
detail = force_text(self.default_detail).format(method=method)
|
detail = force_text(self.default_detail).format(method=method)
|
||||||
super(MethodNotAllowed, self).__init__(detail, code)
|
super().__init__(detail, code)
|
||||||
|
|
||||||
|
|
||||||
class NotAcceptable(APIException):
|
class NotAcceptable(APIException):
|
||||||
|
@ -206,7 +202,7 @@ class NotAcceptable(APIException):
|
||||||
|
|
||||||
def __init__(self, detail=None, code=None, available_renderers=None):
|
def __init__(self, detail=None, code=None, available_renderers=None):
|
||||||
self.available_renderers = available_renderers
|
self.available_renderers = available_renderers
|
||||||
super(NotAcceptable, self).__init__(detail, code)
|
super().__init__(detail, code)
|
||||||
|
|
||||||
|
|
||||||
class UnsupportedMediaType(APIException):
|
class UnsupportedMediaType(APIException):
|
||||||
|
@ -217,7 +213,7 @@ class UnsupportedMediaType(APIException):
|
||||||
def __init__(self, media_type, detail=None, code=None):
|
def __init__(self, media_type, detail=None, code=None):
|
||||||
if detail is None:
|
if detail is None:
|
||||||
detail = force_text(self.default_detail).format(media_type=media_type)
|
detail = force_text(self.default_detail).format(media_type=media_type)
|
||||||
super(UnsupportedMediaType, self).__init__(detail, code)
|
super().__init__(detail, code)
|
||||||
|
|
||||||
|
|
||||||
class Throttled(APIException):
|
class Throttled(APIException):
|
||||||
|
@ -238,7 +234,7 @@ class Throttled(APIException):
|
||||||
self.extra_detail_plural.format(wait=wait),
|
self.extra_detail_plural.format(wait=wait),
|
||||||
wait))))
|
wait))))
|
||||||
self.wait = wait
|
self.wait = wait
|
||||||
super(Throttled, self).__init__(detail, code)
|
super().__init__(detail, code)
|
||||||
|
|
||||||
|
|
||||||
def server_error(request, *args, **kwargs):
|
def server_error(request, *args, **kwargs):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
@ -17,7 +15,7 @@ from django.core.validators import (
|
||||||
)
|
)
|
||||||
from django.forms import FilePathField as DjangoFilePathField
|
from django.forms import FilePathField as DjangoFilePathField
|
||||||
from django.forms import ImageField as DjangoImageField
|
from django.forms import ImageField as DjangoImageField
|
||||||
from django.utils import six, timezone
|
from django.utils import timezone
|
||||||
from django.utils.dateparse import (
|
from django.utils.dateparse import (
|
||||||
parse_date, parse_datetime, parse_duration, parse_time
|
parse_date, parse_datetime, parse_duration, parse_time
|
||||||
)
|
)
|
||||||
|
@ -33,8 +31,7 @@ from pytz.exceptions import InvalidTimeError
|
||||||
from rest_framework import ISO_8601
|
from rest_framework import ISO_8601
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
Mapping, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
|
Mapping, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
|
||||||
MinValueValidator, ProhibitNullCharactersValidator, unicode_repr,
|
MinValueValidator, ProhibitNullCharactersValidator
|
||||||
unicode_to_repr
|
|
||||||
)
|
)
|
||||||
from rest_framework.exceptions import ErrorDetail, ValidationError
|
from rest_framework.exceptions import ErrorDetail, ValidationError
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
@ -51,39 +48,21 @@ class empty:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if six.PY3:
|
def is_simple_callable(obj):
|
||||||
def is_simple_callable(obj):
|
"""
|
||||||
"""
|
True if the object is a callable that takes no arguments.
|
||||||
True if the object is a callable that takes no arguments.
|
"""
|
||||||
"""
|
if not (inspect.isfunction(obj) or inspect.ismethod(obj) or isinstance(obj, functools.partial)):
|
||||||
if not (inspect.isfunction(obj) or inspect.ismethod(obj) or isinstance(obj, functools.partial)):
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
sig = inspect.signature(obj)
|
sig = inspect.signature(obj)
|
||||||
params = sig.parameters.values()
|
params = sig.parameters.values()
|
||||||
return all(
|
return all(
|
||||||
param.kind == param.VAR_POSITIONAL or
|
param.kind == param.VAR_POSITIONAL or
|
||||||
param.kind == param.VAR_KEYWORD or
|
param.kind == param.VAR_KEYWORD or
|
||||||
param.default != param.empty
|
param.default != param.empty
|
||||||
for param in params
|
for param in params
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
|
||||||
def is_simple_callable(obj):
|
|
||||||
function = inspect.isfunction(obj)
|
|
||||||
method = inspect.ismethod(obj)
|
|
||||||
|
|
||||||
if not (function or method):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if method:
|
|
||||||
is_unbound = obj.im_self is None
|
|
||||||
|
|
||||||
args, _, _, defaults = inspect.getargspec(obj)
|
|
||||||
|
|
||||||
len_args = len(args) if function or is_unbound else len(args) - 1
|
|
||||||
len_defaults = len(defaults) if defaults else 0
|
|
||||||
return len_args <= len_defaults
|
|
||||||
|
|
||||||
|
|
||||||
def get_attribute(instance, attrs):
|
def get_attribute(instance, attrs):
|
||||||
|
@ -108,7 +87,7 @@ def get_attribute(instance, attrs):
|
||||||
# If we raised an Attribute or KeyError here it'd get treated
|
# If we raised an Attribute or KeyError here it'd get treated
|
||||||
# as an omitted field in `Field.get_attribute()`. Instead we
|
# as an omitted field in `Field.get_attribute()`. Instead we
|
||||||
# raise a ValueError to ensure the exception is not masked.
|
# raise a ValueError to ensure the exception is not masked.
|
||||||
raise ValueError('Exception raised in callable attribute "{0}"; original exception was: {1}'.format(attr, exc))
|
raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@ -185,18 +164,18 @@ def iter_options(grouped_choices, cutoff=None, cutoff_text=None):
|
||||||
"""
|
"""
|
||||||
Helper function for options and option groups in templates.
|
Helper function for options and option groups in templates.
|
||||||
"""
|
"""
|
||||||
class StartOptionGroup(object):
|
class StartOptionGroup:
|
||||||
start_option_group = True
|
start_option_group = True
|
||||||
end_option_group = False
|
end_option_group = False
|
||||||
|
|
||||||
def __init__(self, label):
|
def __init__(self, label):
|
||||||
self.label = label
|
self.label = label
|
||||||
|
|
||||||
class EndOptionGroup(object):
|
class EndOptionGroup:
|
||||||
start_option_group = False
|
start_option_group = False
|
||||||
end_option_group = True
|
end_option_group = True
|
||||||
|
|
||||||
class Option(object):
|
class Option:
|
||||||
start_option_group = False
|
start_option_group = False
|
||||||
end_option_group = False
|
end_option_group = False
|
||||||
|
|
||||||
|
@ -251,7 +230,7 @@ def get_error_detail(exc_info):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CreateOnlyDefault(object):
|
class CreateOnlyDefault:
|
||||||
"""
|
"""
|
||||||
This class may be used to provide default values that are only used
|
This class may be used to provide default values that are only used
|
||||||
for create operations, but that do not return any value for update
|
for create operations, but that do not return any value for update
|
||||||
|
@ -273,12 +252,10 @@ class CreateOnlyDefault(object):
|
||||||
return self.default
|
return self.default
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr(
|
return '%s(%s)' % (self.__class__.__name__, repr(self.default))
|
||||||
'%s(%s)' % (self.__class__.__name__, unicode_repr(self.default))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CurrentUserDefault(object):
|
class CurrentUserDefault:
|
||||||
def set_context(self, serializer_field):
|
def set_context(self, serializer_field):
|
||||||
self.user = serializer_field.context['request'].user
|
self.user = serializer_field.context['request'].user
|
||||||
|
|
||||||
|
@ -286,7 +263,7 @@ class CurrentUserDefault(object):
|
||||||
return self.user
|
return self.user
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr('%s()' % self.__class__.__name__)
|
return '%s()' % self.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
class SkipField(Exception):
|
class SkipField(Exception):
|
||||||
|
@ -305,7 +282,7 @@ MISSING_ERROR_MESSAGE = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Field(object):
|
class Field:
|
||||||
_creation_counter = 0
|
_creation_counter = 0
|
||||||
|
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
|
@ -618,7 +595,7 @@ class Field(object):
|
||||||
When a field is instantiated, we store the arguments that were used,
|
When a field is instantiated, we store the arguments that were used,
|
||||||
so that we can present a helpful representation of the object.
|
so that we can present a helpful representation of the object.
|
||||||
"""
|
"""
|
||||||
instance = super(Field, cls).__new__(cls)
|
instance = super().__new__(cls)
|
||||||
instance._args = args
|
instance._args = args
|
||||||
instance._kwargs = kwargs
|
instance._kwargs = kwargs
|
||||||
return instance
|
return instance
|
||||||
|
@ -647,7 +624,7 @@ class Field(object):
|
||||||
This allows us to create descriptive representations for serializer
|
This allows us to create descriptive representations for serializer
|
||||||
instances that show all the declared fields on the serializer.
|
instances that show all the declared fields on the serializer.
|
||||||
"""
|
"""
|
||||||
return unicode_to_repr(representation.field_repr(self))
|
return representation.field_repr(self)
|
||||||
|
|
||||||
|
|
||||||
# Boolean types...
|
# Boolean types...
|
||||||
|
@ -724,7 +701,7 @@ class NullBooleanField(Field):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
|
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
|
||||||
kwargs['allow_null'] = True
|
kwargs['allow_null'] = True
|
||||||
super(NullBooleanField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
try:
|
try:
|
||||||
|
@ -764,17 +741,14 @@ class CharField(Field):
|
||||||
self.trim_whitespace = kwargs.pop('trim_whitespace', True)
|
self.trim_whitespace = kwargs.pop('trim_whitespace', True)
|
||||||
self.max_length = kwargs.pop('max_length', None)
|
self.max_length = kwargs.pop('max_length', None)
|
||||||
self.min_length = kwargs.pop('min_length', None)
|
self.min_length = kwargs.pop('min_length', None)
|
||||||
super(CharField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
if self.max_length is not None:
|
if self.max_length is not None:
|
||||||
message = lazy(
|
message = lazy(self.error_messages['max_length'].format, str)(max_length=self.max_length)
|
||||||
self.error_messages['max_length'].format,
|
|
||||||
six.text_type)(max_length=self.max_length)
|
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MaxLengthValidator(self.max_length, message=message))
|
MaxLengthValidator(self.max_length, message=message))
|
||||||
if self.min_length is not None:
|
if self.min_length is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['min_length'].format,
|
self.error_messages['min_length'].format, str)(min_length=self.min_length)
|
||||||
six.text_type)(min_length=self.min_length)
|
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MinLengthValidator(self.min_length, message=message))
|
MinLengthValidator(self.min_length, message=message))
|
||||||
|
|
||||||
|
@ -786,23 +760,23 @@ class CharField(Field):
|
||||||
# Test for the empty string here so that it does not get validated,
|
# Test for the empty string here so that it does not get validated,
|
||||||
# and so that subclasses do not need to handle it explicitly
|
# and so that subclasses do not need to handle it explicitly
|
||||||
# inside the `to_internal_value()` method.
|
# inside the `to_internal_value()` method.
|
||||||
if data == '' or (self.trim_whitespace and six.text_type(data).strip() == ''):
|
if data == '' or (self.trim_whitespace and str(data).strip() == ''):
|
||||||
if not self.allow_blank:
|
if not self.allow_blank:
|
||||||
self.fail('blank')
|
self.fail('blank')
|
||||||
return ''
|
return ''
|
||||||
return super(CharField, self).run_validation(data)
|
return super().run_validation(data)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
# We're lenient with allowing basic numerics to be coerced into strings,
|
# We're lenient with allowing basic numerics to be coerced into strings,
|
||||||
# but other types should fail. Eg. unclear if booleans should represent as `true` or `True`,
|
# but other types should fail. Eg. unclear if booleans should represent as `true` or `True`,
|
||||||
# and composites such as lists are likely user error.
|
# and composites such as lists are likely user error.
|
||||||
if isinstance(data, bool) or not isinstance(data, six.string_types + six.integer_types + (float,)):
|
if isinstance(data, bool) or not isinstance(data, (str, int, float,)):
|
||||||
self.fail('invalid')
|
self.fail('invalid')
|
||||||
value = six.text_type(data)
|
value = str(data)
|
||||||
return value.strip() if self.trim_whitespace else value
|
return value.strip() if self.trim_whitespace else value
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return six.text_type(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
class EmailField(CharField):
|
class EmailField(CharField):
|
||||||
|
@ -811,7 +785,7 @@ class EmailField(CharField):
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(EmailField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
validator = EmailValidator(message=self.error_messages['invalid'])
|
validator = EmailValidator(message=self.error_messages['invalid'])
|
||||||
self.validators.append(validator)
|
self.validators.append(validator)
|
||||||
|
|
||||||
|
@ -822,7 +796,7 @@ class RegexField(CharField):
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, regex, **kwargs):
|
def __init__(self, regex, **kwargs):
|
||||||
super(RegexField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
validator = RegexValidator(regex, message=self.error_messages['invalid'])
|
validator = RegexValidator(regex, message=self.error_messages['invalid'])
|
||||||
self.validators.append(validator)
|
self.validators.append(validator)
|
||||||
|
|
||||||
|
@ -834,7 +808,7 @@ class SlugField(CharField):
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, allow_unicode=False, **kwargs):
|
def __init__(self, allow_unicode=False, **kwargs):
|
||||||
super(SlugField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.allow_unicode = allow_unicode
|
self.allow_unicode = allow_unicode
|
||||||
if self.allow_unicode:
|
if self.allow_unicode:
|
||||||
validator = RegexValidator(re.compile(r'^[-\w]+\Z', re.UNICODE), message=self.error_messages['invalid_unicode'])
|
validator = RegexValidator(re.compile(r'^[-\w]+\Z', re.UNICODE), message=self.error_messages['invalid_unicode'])
|
||||||
|
@ -849,7 +823,7 @@ class URLField(CharField):
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(URLField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
validator = URLValidator(message=self.error_messages['invalid'])
|
validator = URLValidator(message=self.error_messages['invalid'])
|
||||||
self.validators.append(validator)
|
self.validators.append(validator)
|
||||||
|
|
||||||
|
@ -866,16 +840,16 @@ class UUIDField(Field):
|
||||||
if self.uuid_format not in self.valid_formats:
|
if self.uuid_format not in self.valid_formats:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Invalid format for uuid representation. '
|
'Invalid format for uuid representation. '
|
||||||
'Must be one of "{0}"'.format('", "'.join(self.valid_formats))
|
'Must be one of "{}"'.format('", "'.join(self.valid_formats))
|
||||||
)
|
)
|
||||||
super(UUIDField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if not isinstance(data, uuid.UUID):
|
if not isinstance(data, uuid.UUID):
|
||||||
try:
|
try:
|
||||||
if isinstance(data, six.integer_types):
|
if isinstance(data, int):
|
||||||
return uuid.UUID(int=data)
|
return uuid.UUID(int=data)
|
||||||
elif isinstance(data, six.string_types):
|
elif isinstance(data, str):
|
||||||
return uuid.UUID(hex=data)
|
return uuid.UUID(hex=data)
|
||||||
else:
|
else:
|
||||||
self.fail('invalid', value=data)
|
self.fail('invalid', value=data)
|
||||||
|
@ -900,12 +874,12 @@ class IPAddressField(CharField):
|
||||||
def __init__(self, protocol='both', **kwargs):
|
def __init__(self, protocol='both', **kwargs):
|
||||||
self.protocol = protocol.lower()
|
self.protocol = protocol.lower()
|
||||||
self.unpack_ipv4 = (self.protocol == 'both')
|
self.unpack_ipv4 = (self.protocol == 'both')
|
||||||
super(IPAddressField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
validators, error_message = ip_address_validators(protocol, self.unpack_ipv4)
|
validators, error_message = ip_address_validators(protocol, self.unpack_ipv4)
|
||||||
self.validators.extend(validators)
|
self.validators.extend(validators)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if not isinstance(data, six.string_types):
|
if not isinstance(data, str):
|
||||||
self.fail('invalid', value=data)
|
self.fail('invalid', value=data)
|
||||||
|
|
||||||
if ':' in data:
|
if ':' in data:
|
||||||
|
@ -915,7 +889,7 @@ class IPAddressField(CharField):
|
||||||
except DjangoValidationError:
|
except DjangoValidationError:
|
||||||
self.fail('invalid', value=data)
|
self.fail('invalid', value=data)
|
||||||
|
|
||||||
return super(IPAddressField, self).to_internal_value(data)
|
return super().to_internal_value(data)
|
||||||
|
|
||||||
|
|
||||||
# Number types...
|
# Number types...
|
||||||
|
@ -933,22 +907,20 @@ class IntegerField(Field):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.max_value = kwargs.pop('max_value', None)
|
self.max_value = kwargs.pop('max_value', None)
|
||||||
self.min_value = kwargs.pop('min_value', None)
|
self.min_value = kwargs.pop('min_value', None)
|
||||||
super(IntegerField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
if self.max_value is not None:
|
if self.max_value is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['max_value'].format,
|
self.error_messages['max_value'].format, str)(max_value=self.max_value)
|
||||||
six.text_type)(max_value=self.max_value)
|
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MaxValueValidator(self.max_value, message=message))
|
MaxValueValidator(self.max_value, message=message))
|
||||||
if self.min_value is not None:
|
if self.min_value is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['min_value'].format,
|
self.error_messages['min_value'].format, str)(min_value=self.min_value)
|
||||||
six.text_type)(min_value=self.min_value)
|
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MinValueValidator(self.min_value, message=message))
|
MinValueValidator(self.min_value, message=message))
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH:
|
if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH:
|
||||||
self.fail('max_string_length')
|
self.fail('max_string_length')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -973,23 +945,23 @@ class FloatField(Field):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.max_value = kwargs.pop('max_value', None)
|
self.max_value = kwargs.pop('max_value', None)
|
||||||
self.min_value = kwargs.pop('min_value', None)
|
self.min_value = kwargs.pop('min_value', None)
|
||||||
super(FloatField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
if self.max_value is not None:
|
if self.max_value is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['max_value'].format,
|
self.error_messages['max_value'].format,
|
||||||
six.text_type)(max_value=self.max_value)
|
str)(max_value=self.max_value)
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MaxValueValidator(self.max_value, message=message))
|
MaxValueValidator(self.max_value, message=message))
|
||||||
if self.min_value is not None:
|
if self.min_value is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['min_value'].format,
|
self.error_messages['min_value'].format,
|
||||||
six.text_type)(min_value=self.min_value)
|
str)(min_value=self.min_value)
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MinValueValidator(self.min_value, message=message))
|
MinValueValidator(self.min_value, message=message))
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
|
|
||||||
if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH:
|
if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH:
|
||||||
self.fail('max_string_length')
|
self.fail('max_string_length')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1031,18 +1003,17 @@ class DecimalField(Field):
|
||||||
else:
|
else:
|
||||||
self.max_whole_digits = None
|
self.max_whole_digits = None
|
||||||
|
|
||||||
super(DecimalField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
if self.max_value is not None:
|
if self.max_value is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['max_value'].format,
|
self.error_messages['max_value'].format,
|
||||||
six.text_type)(max_value=self.max_value)
|
str)(max_value=self.max_value)
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MaxValueValidator(self.max_value, message=message))
|
MaxValueValidator(self.max_value, message=message))
|
||||||
if self.min_value is not None:
|
if self.min_value is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['min_value'].format,
|
self.error_messages['min_value'].format, str)(min_value=self.min_value)
|
||||||
six.text_type)(min_value=self.min_value)
|
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MinValueValidator(self.min_value, message=message))
|
MinValueValidator(self.min_value, message=message))
|
||||||
|
|
||||||
|
@ -1121,7 +1092,7 @@ class DecimalField(Field):
|
||||||
coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING)
|
coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING)
|
||||||
|
|
||||||
if not isinstance(value, decimal.Decimal):
|
if not isinstance(value, decimal.Decimal):
|
||||||
value = decimal.Decimal(six.text_type(value).strip())
|
value = decimal.Decimal(str(value).strip())
|
||||||
|
|
||||||
quantized = self.quantize(value)
|
quantized = self.quantize(value)
|
||||||
|
|
||||||
|
@ -1130,7 +1101,7 @@ class DecimalField(Field):
|
||||||
if self.localize:
|
if self.localize:
|
||||||
return localize_input(quantized)
|
return localize_input(quantized)
|
||||||
|
|
||||||
return '{0:f}'.format(quantized)
|
return '{:f}'.format(quantized)
|
||||||
|
|
||||||
def quantize(self, value):
|
def quantize(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -1167,7 +1138,7 @@ class DateTimeField(Field):
|
||||||
self.input_formats = input_formats
|
self.input_formats = input_formats
|
||||||
if default_timezone is not None:
|
if default_timezone is not None:
|
||||||
self.timezone = default_timezone
|
self.timezone = default_timezone
|
||||||
super(DateTimeField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def enforce_timezone(self, value):
|
def enforce_timezone(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -1226,7 +1197,7 @@ class DateTimeField(Field):
|
||||||
|
|
||||||
output_format = getattr(self, 'format', api_settings.DATETIME_FORMAT)
|
output_format = getattr(self, 'format', api_settings.DATETIME_FORMAT)
|
||||||
|
|
||||||
if output_format is None or isinstance(value, six.string_types):
|
if output_format is None or isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
value = self.enforce_timezone(value)
|
value = self.enforce_timezone(value)
|
||||||
|
@ -1251,7 +1222,7 @@ class DateField(Field):
|
||||||
self.format = format
|
self.format = format
|
||||||
if input_formats is not None:
|
if input_formats is not None:
|
||||||
self.input_formats = input_formats
|
self.input_formats = input_formats
|
||||||
super(DateField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, value):
|
def to_internal_value(self, value):
|
||||||
input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS)
|
input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS)
|
||||||
|
@ -1288,7 +1259,7 @@ class DateField(Field):
|
||||||
|
|
||||||
output_format = getattr(self, 'format', api_settings.DATE_FORMAT)
|
output_format = getattr(self, 'format', api_settings.DATE_FORMAT)
|
||||||
|
|
||||||
if output_format is None or isinstance(value, six.string_types):
|
if output_format is None or isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# Applying a `DateField` to a datetime value is almost always
|
# Applying a `DateField` to a datetime value is almost always
|
||||||
|
@ -1317,7 +1288,7 @@ class TimeField(Field):
|
||||||
self.format = format
|
self.format = format
|
||||||
if input_formats is not None:
|
if input_formats is not None:
|
||||||
self.input_formats = input_formats
|
self.input_formats = input_formats
|
||||||
super(TimeField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, value):
|
def to_internal_value(self, value):
|
||||||
input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS)
|
input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS)
|
||||||
|
@ -1351,7 +1322,7 @@ class TimeField(Field):
|
||||||
|
|
||||||
output_format = getattr(self, 'format', api_settings.TIME_FORMAT)
|
output_format = getattr(self, 'format', api_settings.TIME_FORMAT)
|
||||||
|
|
||||||
if output_format is None or isinstance(value, six.string_types):
|
if output_format is None or isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# Applying a `TimeField` to a datetime value is almost always
|
# Applying a `TimeField` to a datetime value is almost always
|
||||||
|
@ -1378,24 +1349,24 @@ class DurationField(Field):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.max_value = kwargs.pop('max_value', None)
|
self.max_value = kwargs.pop('max_value', None)
|
||||||
self.min_value = kwargs.pop('min_value', None)
|
self.min_value = kwargs.pop('min_value', None)
|
||||||
super(DurationField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
if self.max_value is not None:
|
if self.max_value is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['max_value'].format,
|
self.error_messages['max_value'].format,
|
||||||
six.text_type)(max_value=self.max_value)
|
str)(max_value=self.max_value)
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MaxValueValidator(self.max_value, message=message))
|
MaxValueValidator(self.max_value, message=message))
|
||||||
if self.min_value is not None:
|
if self.min_value is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['min_value'].format,
|
self.error_messages['min_value'].format,
|
||||||
six.text_type)(min_value=self.min_value)
|
str)(min_value=self.min_value)
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MinValueValidator(self.min_value, message=message))
|
MinValueValidator(self.min_value, message=message))
|
||||||
|
|
||||||
def to_internal_value(self, value):
|
def to_internal_value(self, value):
|
||||||
if isinstance(value, datetime.timedelta):
|
if isinstance(value, datetime.timedelta):
|
||||||
return value
|
return value
|
||||||
parsed = parse_duration(six.text_type(value))
|
parsed = parse_duration(str(value))
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
return parsed
|
return parsed
|
||||||
self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]')
|
self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]')
|
||||||
|
@ -1420,21 +1391,21 @@ class ChoiceField(Field):
|
||||||
|
|
||||||
self.allow_blank = kwargs.pop('allow_blank', False)
|
self.allow_blank = kwargs.pop('allow_blank', False)
|
||||||
|
|
||||||
super(ChoiceField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if data == '' and self.allow_blank:
|
if data == '' and self.allow_blank:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.choice_strings_to_values[six.text_type(data)]
|
return self.choice_strings_to_values[str(data)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.fail('invalid_choice', input=data)
|
self.fail('invalid_choice', input=data)
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
if value in ('', None):
|
if value in ('', None):
|
||||||
return value
|
return value
|
||||||
return self.choice_strings_to_values.get(six.text_type(value), value)
|
return self.choice_strings_to_values.get(str(value), value)
|
||||||
|
|
||||||
def iter_options(self):
|
def iter_options(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1457,7 +1428,7 @@ class ChoiceField(Field):
|
||||||
# Allows us to deal with eg. integer choices while supporting either
|
# Allows us to deal with eg. integer choices while supporting either
|
||||||
# integer or string input, but still get the correct datatype out.
|
# integer or string input, but still get the correct datatype out.
|
||||||
self.choice_strings_to_values = {
|
self.choice_strings_to_values = {
|
||||||
six.text_type(key): key for key in self.choices
|
str(key): key for key in self.choices
|
||||||
}
|
}
|
||||||
|
|
||||||
choices = property(_get_choices, _set_choices)
|
choices = property(_get_choices, _set_choices)
|
||||||
|
@ -1473,7 +1444,7 @@ class MultipleChoiceField(ChoiceField):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.allow_empty = kwargs.pop('allow_empty', True)
|
self.allow_empty = kwargs.pop('allow_empty', True)
|
||||||
super(MultipleChoiceField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
if self.field_name not in dictionary:
|
if self.field_name not in dictionary:
|
||||||
|
@ -1486,7 +1457,7 @@ class MultipleChoiceField(ChoiceField):
|
||||||
return dictionary.get(self.field_name, empty)
|
return dictionary.get(self.field_name, empty)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if isinstance(data, six.text_type) or not hasattr(data, '__iter__'):
|
if isinstance(data, str) or not hasattr(data, '__iter__'):
|
||||||
self.fail('not_a_list', input_type=type(data).__name__)
|
self.fail('not_a_list', input_type=type(data).__name__)
|
||||||
if not self.allow_empty and len(data) == 0:
|
if not self.allow_empty and len(data) == 0:
|
||||||
self.fail('empty')
|
self.fail('empty')
|
||||||
|
@ -1498,7 +1469,7 @@ class MultipleChoiceField(ChoiceField):
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return {
|
return {
|
||||||
self.choice_strings_to_values.get(six.text_type(item), item) for item in value
|
self.choice_strings_to_values.get(str(item), item) for item in value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1516,7 +1487,7 @@ class FilePathField(ChoiceField):
|
||||||
allow_folders=allow_folders, required=required
|
allow_folders=allow_folders, required=required
|
||||||
)
|
)
|
||||||
kwargs['choices'] = field.choices
|
kwargs['choices'] = field.choices
|
||||||
super(FilePathField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
# File types...
|
# File types...
|
||||||
|
@ -1535,7 +1506,7 @@ class FileField(Field):
|
||||||
self.allow_empty_file = kwargs.pop('allow_empty_file', False)
|
self.allow_empty_file = kwargs.pop('allow_empty_file', False)
|
||||||
if 'use_url' in kwargs:
|
if 'use_url' in kwargs:
|
||||||
self.use_url = kwargs.pop('use_url')
|
self.use_url = kwargs.pop('use_url')
|
||||||
super(FileField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
try:
|
try:
|
||||||
|
@ -1581,13 +1552,13 @@ class ImageField(FileField):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField)
|
self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField)
|
||||||
super(ImageField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
# Image validation is a bit grungy, so we'll just outright
|
# Image validation is a bit grungy, so we'll just outright
|
||||||
# defer to Django's implementation so we don't need to
|
# defer to Django's implementation so we don't need to
|
||||||
# consider it, or treat PIL as a test dependency.
|
# consider it, or treat PIL as a test dependency.
|
||||||
file_object = super(ImageField, self).to_internal_value(data)
|
file_object = super().to_internal_value(data)
|
||||||
django_field = self._DjangoImageField()
|
django_field = self._DjangoImageField()
|
||||||
django_field.error_messages = self.error_messages
|
django_field.error_messages = self.error_messages
|
||||||
return django_field.clean(file_object)
|
return django_field.clean(file_object)
|
||||||
|
@ -1597,7 +1568,7 @@ class ImageField(FileField):
|
||||||
|
|
||||||
class _UnvalidatedField(Field):
|
class _UnvalidatedField(Field):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(_UnvalidatedField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.allow_blank = True
|
self.allow_blank = True
|
||||||
self.allow_null = True
|
self.allow_null = True
|
||||||
|
|
||||||
|
@ -1630,7 +1601,7 @@ class ListField(Field):
|
||||||
"Remove `source=` from the field declaration."
|
"Remove `source=` from the field declaration."
|
||||||
)
|
)
|
||||||
|
|
||||||
super(ListField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.child.bind(field_name='', parent=self)
|
self.child.bind(field_name='', parent=self)
|
||||||
if self.max_length is not None:
|
if self.max_length is not None:
|
||||||
message = self.error_messages['max_length'].format(max_length=self.max_length)
|
message = self.error_messages['max_length'].format(max_length=self.max_length)
|
||||||
|
@ -1660,7 +1631,7 @@ class ListField(Field):
|
||||||
"""
|
"""
|
||||||
if html.is_html_input(data):
|
if html.is_html_input(data):
|
||||||
data = html.parse_html_list(data, default=[])
|
data = html.parse_html_list(data, default=[])
|
||||||
if isinstance(data, (six.text_type, Mapping)) or not hasattr(data, '__iter__'):
|
if isinstance(data, (str, Mapping)) or not hasattr(data, '__iter__'):
|
||||||
self.fail('not_a_list', input_type=type(data).__name__)
|
self.fail('not_a_list', input_type=type(data).__name__)
|
||||||
if not self.allow_empty and len(data) == 0:
|
if not self.allow_empty and len(data) == 0:
|
||||||
self.fail('empty')
|
self.fail('empty')
|
||||||
|
@ -1703,7 +1674,7 @@ class DictField(Field):
|
||||||
"Remove `source=` from the field declaration."
|
"Remove `source=` from the field declaration."
|
||||||
)
|
)
|
||||||
|
|
||||||
super(DictField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.child.bind(field_name='', parent=self)
|
self.child.bind(field_name='', parent=self)
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
|
@ -1725,7 +1696,7 @@ class DictField(Field):
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return {
|
return {
|
||||||
six.text_type(key): self.child.to_representation(val) if val is not None else None
|
str(key): self.child.to_representation(val) if val is not None else None
|
||||||
for key, val in value.items()
|
for key, val in value.items()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1734,7 +1705,7 @@ class DictField(Field):
|
||||||
errors = OrderedDict()
|
errors = OrderedDict()
|
||||||
|
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
key = six.text_type(key)
|
key = str(key)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result[key] = self.child.run_validation(value)
|
result[key] = self.child.run_validation(value)
|
||||||
|
@ -1750,7 +1721,7 @@ class HStoreField(DictField):
|
||||||
child = CharField(allow_blank=True, allow_null=True)
|
child = CharField(allow_blank=True, allow_null=True)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(HStoreField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
assert isinstance(self.child, CharField), (
|
assert isinstance(self.child, CharField), (
|
||||||
"The `child` argument must be an instance of `CharField`, "
|
"The `child` argument must be an instance of `CharField`, "
|
||||||
"as the hstore extension stores values as strings."
|
"as the hstore extension stores values as strings."
|
||||||
|
@ -1764,15 +1735,15 @@ class JSONField(Field):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.binary = kwargs.pop('binary', False)
|
self.binary = kwargs.pop('binary', False)
|
||||||
super(JSONField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
if html.is_html_input(dictionary) and self.field_name in dictionary:
|
if html.is_html_input(dictionary) and self.field_name in dictionary:
|
||||||
# When HTML form input is used, mark up the input
|
# When HTML form input is used, mark up the input
|
||||||
# as being a JSON string, rather than a JSON primitive.
|
# as being a JSON string, rather than a JSON primitive.
|
||||||
class JSONString(six.text_type):
|
class JSONString(str):
|
||||||
def __new__(self, value):
|
def __new__(self, value):
|
||||||
ret = six.text_type.__new__(self, value)
|
ret = str.__new__(self, value)
|
||||||
ret.is_json_string = True
|
ret.is_json_string = True
|
||||||
return ret
|
return ret
|
||||||
return JSONString(dictionary[self.field_name])
|
return JSONString(dictionary[self.field_name])
|
||||||
|
@ -1795,7 +1766,7 @@ class JSONField(Field):
|
||||||
value = json.dumps(value)
|
value = json.dumps(value)
|
||||||
# On python 2.x the return type for json.dumps() is underspecified.
|
# On python 2.x the return type for json.dumps() is underspecified.
|
||||||
# On python 3.x json.dumps() returns unicode strings.
|
# On python 3.x json.dumps() returns unicode strings.
|
||||||
if isinstance(value, six.text_type):
|
if isinstance(value, str):
|
||||||
value = bytes(value.encode('utf-8'))
|
value = bytes(value.encode('utf-8'))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -1817,7 +1788,7 @@ class ReadOnlyField(Field):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
kwargs['read_only'] = True
|
kwargs['read_only'] = True
|
||||||
super(ReadOnlyField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return value
|
return value
|
||||||
|
@ -1834,7 +1805,7 @@ class HiddenField(Field):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
assert 'default' in kwargs, 'default is a required argument.'
|
assert 'default' in kwargs, 'default is a required argument.'
|
||||||
kwargs['write_only'] = True
|
kwargs['write_only'] = True
|
||||||
super(HiddenField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
# We always use the default value for `HiddenField`.
|
# We always use the default value for `HiddenField`.
|
||||||
|
@ -1864,7 +1835,7 @@ class SerializerMethodField(Field):
|
||||||
self.method_name = method_name
|
self.method_name = method_name
|
||||||
kwargs['source'] = '*'
|
kwargs['source'] = '*'
|
||||||
kwargs['read_only'] = True
|
kwargs['read_only'] = True
|
||||||
super(SerializerMethodField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def bind(self, field_name, parent):
|
def bind(self, field_name, parent):
|
||||||
# In order to enforce a consistent style, we error if a redundant
|
# In order to enforce a consistent style, we error if a redundant
|
||||||
|
@ -1882,7 +1853,7 @@ class SerializerMethodField(Field):
|
||||||
if self.method_name is None:
|
if self.method_name is None:
|
||||||
self.method_name = default_method_name
|
self.method_name = default_method_name
|
||||||
|
|
||||||
super(SerializerMethodField, self).bind(field_name, parent)
|
super().bind(field_name, parent)
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
method = getattr(self.parent, self.method_name)
|
method = getattr(self.parent, self.method_name)
|
||||||
|
@ -1905,11 +1876,10 @@ class ModelField(Field):
|
||||||
# The `max_length` option is supported by Django's base `Field` class,
|
# The `max_length` option is supported by Django's base `Field` class,
|
||||||
# so we'd better support it here.
|
# so we'd better support it here.
|
||||||
max_length = kwargs.pop('max_length', None)
|
max_length = kwargs.pop('max_length', None)
|
||||||
super(ModelField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
if max_length is not None:
|
if max_length is not None:
|
||||||
message = lazy(
|
message = lazy(
|
||||||
self.error_messages['max_length'].format,
|
self.error_messages['max_length'].format, str)(max_length=self.max_length)
|
||||||
six.text_type)(max_length=self.max_length)
|
|
||||||
self.validators.append(
|
self.validators.append(
|
||||||
MaxLengthValidator(self.max_length, message=message))
|
MaxLengthValidator(self.max_length, message=message))
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
Provides generic filtering backends that can be used to filter the results
|
Provides generic filtering backends that can be used to filter the results
|
||||||
returned by list views.
|
returned by list views.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import operator
|
import operator
|
||||||
import warnings
|
import warnings
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
@ -13,7 +11,6 @@ from django.db import models
|
||||||
from django.db.models.constants import LOOKUP_SEP
|
from django.db.models.constants import LOOKUP_SEP
|
||||||
from django.db.models.sql.constants import ORDER_PATTERN
|
from django.db.models.sql.constants import ORDER_PATTERN
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.utils import six
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -24,7 +21,7 @@ from rest_framework.compat import (
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
class BaseFilterBackend(object):
|
class BaseFilterBackend:
|
||||||
"""
|
"""
|
||||||
A base class from which all filter backend classes should inherit.
|
A base class from which all filter backend classes should inherit.
|
||||||
"""
|
"""
|
||||||
|
@ -109,7 +106,7 @@ class SearchFilter(BaseFilterBackend):
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
orm_lookups = [
|
orm_lookups = [
|
||||||
self.construct_search(six.text_type(search_field))
|
self.construct_search(str(search_field))
|
||||||
for search_field in search_fields
|
for search_field in search_fields
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -188,7 +185,7 @@ class OrderingFilter(BaseFilterBackend):
|
||||||
|
|
||||||
def get_default_ordering(self, view):
|
def get_default_ordering(self, view):
|
||||||
ordering = getattr(view, 'ordering', None)
|
ordering = getattr(view, 'ordering', None)
|
||||||
if isinstance(ordering, six.string_types):
|
if isinstance(ordering, str):
|
||||||
return (ordering,)
|
return (ordering,)
|
||||||
return ordering
|
return ordering
|
||||||
|
|
||||||
|
@ -237,7 +234,7 @@ class OrderingFilter(BaseFilterBackend):
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
valid_fields = [
|
valid_fields = [
|
||||||
(item, item) if isinstance(item, six.string_types) else item
|
(item, item) if isinstance(item, str) else item
|
||||||
for item in valid_fields
|
for item in valid_fields
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""
|
"""
|
||||||
Generic views that provide commonly needed behaviour.
|
Generic views that provide commonly needed behaviour.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
|
|
@ -6,8 +6,6 @@ some fairly ad-hoc information about the view.
|
||||||
Future implementations might use JSON schema or other definitions in order
|
Future implementations might use JSON schema or other definitions in order
|
||||||
to return this information in a more standardized way.
|
to return this information in a more standardized way.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
@ -19,7 +17,7 @@ from rest_framework.request import clone_request
|
||||||
from rest_framework.utils.field_mapping import ClassLookupDict
|
from rest_framework.utils.field_mapping import ClassLookupDict
|
||||||
|
|
||||||
|
|
||||||
class BaseMetadata(object):
|
class BaseMetadata:
|
||||||
def determine_metadata(self, request, view):
|
def determine_metadata(self, request, view):
|
||||||
"""
|
"""
|
||||||
Return a dictionary of metadata about the view.
|
Return a dictionary of metadata about the view.
|
||||||
|
|
|
@ -4,14 +4,12 @@ Basic building blocks for generic class based views.
|
||||||
We don't bind behaviour to http method handlers yet,
|
We don't bind behaviour to http method handlers yet,
|
||||||
which allows mixin classes to be composed in interesting ways.
|
which allows mixin classes to be composed in interesting ways.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
class CreateModelMixin(object):
|
class CreateModelMixin:
|
||||||
"""
|
"""
|
||||||
Create a model instance.
|
Create a model instance.
|
||||||
"""
|
"""
|
||||||
|
@ -32,7 +30,7 @@ class CreateModelMixin(object):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class ListModelMixin(object):
|
class ListModelMixin:
|
||||||
"""
|
"""
|
||||||
List a queryset.
|
List a queryset.
|
||||||
"""
|
"""
|
||||||
|
@ -48,7 +46,7 @@ class ListModelMixin(object):
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class RetrieveModelMixin(object):
|
class RetrieveModelMixin:
|
||||||
"""
|
"""
|
||||||
Retrieve a model instance.
|
Retrieve a model instance.
|
||||||
"""
|
"""
|
||||||
|
@ -58,7 +56,7 @@ class RetrieveModelMixin(object):
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class UpdateModelMixin(object):
|
class UpdateModelMixin:
|
||||||
"""
|
"""
|
||||||
Update a model instance.
|
Update a model instance.
|
||||||
"""
|
"""
|
||||||
|
@ -84,7 +82,7 @@ class UpdateModelMixin(object):
|
||||||
return self.update(request, *args, **kwargs)
|
return self.update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class DestroyModelMixin(object):
|
class DestroyModelMixin:
|
||||||
"""
|
"""
|
||||||
Destroy a model instance.
|
Destroy a model instance.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
Content negotiation deals with selecting an appropriate renderer given the
|
Content negotiation deals with selecting an appropriate renderer given the
|
||||||
incoming request. Typically this will be based on the request's Accept header.
|
incoming request. Typically this will be based on the request's Accept header.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
|
||||||
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
||||||
|
@ -13,7 +11,7 @@ from rest_framework.utils.mediatypes import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseContentNegotiation(object):
|
class BaseContentNegotiation:
|
||||||
def select_parser(self, request, parsers):
|
def select_parser(self, request, parsers):
|
||||||
raise NotImplementedError('.select_parser() must be implemented')
|
raise NotImplementedError('.select_parser() must be implemented')
|
||||||
|
|
||||||
|
@ -66,7 +64,7 @@ class DefaultContentNegotiation(BaseContentNegotiation):
|
||||||
# Accepted media type is 'application/json'
|
# Accepted media type is 'application/json'
|
||||||
full_media_type = ';'.join(
|
full_media_type = ';'.join(
|
||||||
(renderer.media_type,) +
|
(renderer.media_type,) +
|
||||||
tuple('{0}={1}'.format(
|
tuple('{}={}'.format(
|
||||||
key, value.decode(HTTP_HEADER_ENCODING))
|
key, value.decode(HTTP_HEADER_ENCODING))
|
||||||
for key, value in media_type_wrapper.params.items()))
|
for key, value in media_type_wrapper.params.items()))
|
||||||
return renderer, full_media_type
|
return renderer, full_media_type
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
# coding: utf-8
|
|
||||||
"""
|
"""
|
||||||
Pagination serializers determine the structure of the output that should
|
Pagination serializers determine the structure of the output that should
|
||||||
be used for paginated responses.
|
be used for paginated responses.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
from collections import OrderedDict, namedtuple
|
from collections import OrderedDict, namedtuple
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
from django.core.paginator import InvalidPage
|
from django.core.paginator import InvalidPage
|
||||||
from django.core.paginator import Paginator as DjangoPaginator
|
from django.core.paginator import Paginator as DjangoPaginator
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.utils import six
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.compat import coreapi, coreschema
|
from rest_framework.compat import coreapi, coreschema
|
||||||
|
@ -133,7 +129,7 @@ PageLink = namedtuple('PageLink', ['url', 'number', 'is_active', 'is_break'])
|
||||||
PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True)
|
PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True)
|
||||||
|
|
||||||
|
|
||||||
class BasePagination(object):
|
class BasePagination:
|
||||||
display_page_controls = False
|
display_page_controls = False
|
||||||
|
|
||||||
def paginate_queryset(self, queryset, request, view=None): # pragma: no cover
|
def paginate_queryset(self, queryset, request, view=None): # pragma: no cover
|
||||||
|
@ -204,7 +200,7 @@ class PageNumberPagination(BasePagination):
|
||||||
self.page = paginator.page(page_number)
|
self.page = paginator.page(page_number)
|
||||||
except InvalidPage as exc:
|
except InvalidPage as exc:
|
||||||
msg = self.invalid_page_message.format(
|
msg = self.invalid_page_message.format(
|
||||||
page_number=page_number, message=six.text_type(exc)
|
page_number=page_number, message=str(exc)
|
||||||
)
|
)
|
||||||
raise NotFound(msg)
|
raise NotFound(msg)
|
||||||
|
|
||||||
|
@ -716,13 +712,13 @@ class CursorPagination(BasePagination):
|
||||||
'nearly-unique field on the model, such as "-created" or "pk".'
|
'nearly-unique field on the model, such as "-created" or "pk".'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert isinstance(ordering, (six.string_types, list, tuple)), (
|
assert isinstance(ordering, (str, list, tuple)), (
|
||||||
'Invalid ordering. Expected string or tuple, but got {type}'.format(
|
'Invalid ordering. Expected string or tuple, but got {type}'.format(
|
||||||
type=type(ordering).__name__
|
type=type(ordering).__name__
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(ordering, six.string_types):
|
if isinstance(ordering, str):
|
||||||
return (ordering,)
|
return (ordering,)
|
||||||
return tuple(ordering)
|
return tuple(ordering)
|
||||||
|
|
||||||
|
@ -737,7 +733,7 @@ class CursorPagination(BasePagination):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
querystring = b64decode(encoded.encode('ascii')).decode('ascii')
|
querystring = b64decode(encoded.encode('ascii')).decode('ascii')
|
||||||
tokens = urlparse.parse_qs(querystring, keep_blank_values=True)
|
tokens = parse.parse_qs(querystring, keep_blank_values=True)
|
||||||
|
|
||||||
offset = tokens.get('o', ['0'])[0]
|
offset = tokens.get('o', ['0'])[0]
|
||||||
offset = _positive_int(offset, cutoff=self.offset_cutoff)
|
offset = _positive_int(offset, cutoff=self.offset_cutoff)
|
||||||
|
@ -763,7 +759,7 @@ class CursorPagination(BasePagination):
|
||||||
if cursor.position is not None:
|
if cursor.position is not None:
|
||||||
tokens['p'] = cursor.position
|
tokens['p'] = cursor.position
|
||||||
|
|
||||||
querystring = urlparse.urlencode(tokens, doseq=True)
|
querystring = parse.urlencode(tokens, doseq=True)
|
||||||
encoded = b64encode(querystring.encode('ascii')).decode('ascii')
|
encoded = b64encode(querystring.encode('ascii')).decode('ascii')
|
||||||
return replace_query_param(self.base_url, self.cursor_query_param, encoded)
|
return replace_query_param(self.base_url, self.cursor_query_param, encoded)
|
||||||
|
|
||||||
|
@ -773,7 +769,7 @@ class CursorPagination(BasePagination):
|
||||||
attr = instance[field_name]
|
attr = instance[field_name]
|
||||||
else:
|
else:
|
||||||
attr = getattr(instance, field_name)
|
attr = getattr(instance, field_name)
|
||||||
return six.text_type(attr)
|
return str(attr)
|
||||||
|
|
||||||
def get_paginated_response(self, data):
|
def get_paginated_response(self, data):
|
||||||
return Response(OrderedDict([
|
return Response(OrderedDict([
|
||||||
|
|
|
@ -4,9 +4,8 @@ Parsers are used to parse the content of incoming HTTP requests.
|
||||||
They give us a generic way of being able to handle various media types
|
They give us a generic way of being able to handle various media types
|
||||||
on the request, such as form content or json encoded data.
|
on the request, such as form content or json encoded data.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files.uploadhandler import StopFutureHandlers
|
from django.core.files.uploadhandler import StopFutureHandlers
|
||||||
|
@ -15,9 +14,7 @@ from django.http.multipartparser import ChunkIter
|
||||||
from django.http.multipartparser import \
|
from django.http.multipartparser import \
|
||||||
MultiPartParser as DjangoMultiPartParser
|
MultiPartParser as DjangoMultiPartParser
|
||||||
from django.http.multipartparser import MultiPartParserError, parse_header
|
from django.http.multipartparser import MultiPartParserError, parse_header
|
||||||
from django.utils import six
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
|
||||||
|
|
||||||
from rest_framework import renderers
|
from rest_framework import renderers
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
|
@ -25,13 +22,13 @@ from rest_framework.settings import api_settings
|
||||||
from rest_framework.utils import json
|
from rest_framework.utils import json
|
||||||
|
|
||||||
|
|
||||||
class DataAndFiles(object):
|
class DataAndFiles:
|
||||||
def __init__(self, data, files):
|
def __init__(self, data, files):
|
||||||
self.data = data
|
self.data = data
|
||||||
self.files = files
|
self.files = files
|
||||||
|
|
||||||
|
|
||||||
class BaseParser(object):
|
class BaseParser:
|
||||||
"""
|
"""
|
||||||
All parsers should extend `BaseParser`, specifying a `media_type`
|
All parsers should extend `BaseParser`, specifying a `media_type`
|
||||||
attribute, and overriding the `.parse()` method.
|
attribute, and overriding the `.parse()` method.
|
||||||
|
@ -67,7 +64,7 @@ class JSONParser(BaseParser):
|
||||||
parse_constant = json.strict_constant if self.strict else None
|
parse_constant = json.strict_constant if self.strict else None
|
||||||
return json.load(decoded_stream, parse_constant=parse_constant)
|
return json.load(decoded_stream, parse_constant=parse_constant)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise ParseError('JSON parse error - %s' % six.text_type(exc))
|
raise ParseError('JSON parse error - %s' % str(exc))
|
||||||
|
|
||||||
|
|
||||||
class FormParser(BaseParser):
|
class FormParser(BaseParser):
|
||||||
|
@ -113,7 +110,7 @@ class MultiPartParser(BaseParser):
|
||||||
data, files = parser.parse()
|
data, files = parser.parse()
|
||||||
return DataAndFiles(data, files)
|
return DataAndFiles(data, files)
|
||||||
except MultiPartParserError as exc:
|
except MultiPartParserError as exc:
|
||||||
raise ParseError('Multipart form parse error - %s' % six.text_type(exc))
|
raise ParseError('Multipart form parse error - %s' % str(exc))
|
||||||
|
|
||||||
|
|
||||||
class FileUploadParser(BaseParser):
|
class FileUploadParser(BaseParser):
|
||||||
|
@ -221,7 +218,7 @@ class FileUploadParser(BaseParser):
|
||||||
encoded_filename = force_text(filename_parm['filename*'])
|
encoded_filename = force_text(filename_parm['filename*'])
|
||||||
try:
|
try:
|
||||||
charset, lang, filename = encoded_filename.split('\'', 2)
|
charset, lang, filename = encoded_filename.split('\'', 2)
|
||||||
filename = urlparse.unquote(filename)
|
filename = parse.unquote(filename)
|
||||||
except (ValueError, LookupError):
|
except (ValueError, LookupError):
|
||||||
filename = force_text(filename_parm['filename'])
|
filename = force_text(filename_parm['filename'])
|
||||||
return filename
|
return filename
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
"""
|
"""
|
||||||
Provides a set of pluggable permission policies.
|
Provides a set of pluggable permission policies.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
|
|
||||||
|
@ -101,8 +98,7 @@ class BasePermissionMetaclass(OperationHolderMixin, type):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(BasePermissionMetaclass)
|
class BasePermission(metaclass=BasePermissionMetaclass):
|
||||||
class BasePermission(object):
|
|
||||||
"""
|
"""
|
||||||
A base class from which all permission classes should inherit.
|
A base class from which all permission classes should inherit.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
# coding: utf-8
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
||||||
from django.db.models import Manager
|
from django.db.models import Manager
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.urls import NoReverseMatch, Resolver404, get_script_prefix, resolve
|
from django.urls import NoReverseMatch, Resolver404, get_script_prefix, resolve
|
||||||
from django.utils import six
|
from django.utils.encoding import smart_text, uri_to_iri
|
||||||
from django.utils.encoding import (
|
|
||||||
python_2_unicode_compatible, smart_text, uri_to_iri
|
|
||||||
)
|
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.fields import (
|
from rest_framework.fields import (
|
||||||
|
@ -46,14 +40,14 @@ class ObjectTypeError(TypeError):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Hyperlink(six.text_type):
|
class Hyperlink(str):
|
||||||
"""
|
"""
|
||||||
A string like object that additionally has an associated name.
|
A string like object that additionally has an associated name.
|
||||||
We use this for hyperlinked URLs that may render as a named link
|
We use this for hyperlinked URLs that may render as a named link
|
||||||
in some contexts, or render as a plain URL in others.
|
in some contexts, or render as a plain URL in others.
|
||||||
"""
|
"""
|
||||||
def __new__(self, url, obj):
|
def __new__(self, url, obj):
|
||||||
ret = six.text_type.__new__(self, url)
|
ret = str.__new__(self, url)
|
||||||
ret.obj = obj
|
ret.obj = obj
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -65,13 +59,12 @@ class Hyperlink(six.text_type):
|
||||||
# This ensures that we only called `__str__` lazily,
|
# This ensures that we only called `__str__` lazily,
|
||||||
# as in some cases calling __str__ on a model instances *might*
|
# as in some cases calling __str__ on a model instances *might*
|
||||||
# involve a database lookup.
|
# involve a database lookup.
|
||||||
return six.text_type(self.obj)
|
return str(self.obj)
|
||||||
|
|
||||||
is_hyperlink = True
|
is_hyperlink = True
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
class PKOnlyObject:
|
||||||
class PKOnlyObject(object):
|
|
||||||
"""
|
"""
|
||||||
This is a mock object, used for when we only need the pk of the object
|
This is a mock object, used for when we only need the pk of the object
|
||||||
instance, but still want to return an object with a .pk attribute,
|
instance, but still want to return an object with a .pk attribute,
|
||||||
|
@ -121,14 +114,14 @@ class RelatedField(Field):
|
||||||
)
|
)
|
||||||
kwargs.pop('many', None)
|
kwargs.pop('many', None)
|
||||||
kwargs.pop('allow_empty', None)
|
kwargs.pop('allow_empty', None)
|
||||||
super(RelatedField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
# We override this method in order to automagically create
|
# We override this method in order to automagically create
|
||||||
# `ManyRelatedField` classes instead when `many=True` is set.
|
# `ManyRelatedField` classes instead when `many=True` is set.
|
||||||
if kwargs.pop('many', False):
|
if kwargs.pop('many', False):
|
||||||
return cls.many_init(*args, **kwargs)
|
return cls.many_init(*args, **kwargs)
|
||||||
return super(RelatedField, cls).__new__(cls, *args, **kwargs)
|
return super().__new__(cls, *args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def many_init(cls, *args, **kwargs):
|
def many_init(cls, *args, **kwargs):
|
||||||
|
@ -157,7 +150,7 @@ class RelatedField(Field):
|
||||||
# We force empty strings to None values for relational fields.
|
# We force empty strings to None values for relational fields.
|
||||||
if data == '':
|
if data == '':
|
||||||
data = None
|
data = None
|
||||||
return super(RelatedField, self).run_validation(data)
|
return super().run_validation(data)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = self.queryset
|
queryset = self.queryset
|
||||||
|
@ -189,7 +182,7 @@ class RelatedField(Field):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Standard case, return the object instance.
|
# Standard case, return the object instance.
|
||||||
return super(RelatedField, self).get_attribute(instance)
|
return super().get_attribute(instance)
|
||||||
|
|
||||||
def get_choices(self, cutoff=None):
|
def get_choices(self, cutoff=None):
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
|
@ -225,7 +218,7 @@ class RelatedField(Field):
|
||||||
)
|
)
|
||||||
|
|
||||||
def display_value(self, instance):
|
def display_value(self, instance):
|
||||||
return six.text_type(instance)
|
return str(instance)
|
||||||
|
|
||||||
|
|
||||||
class StringRelatedField(RelatedField):
|
class StringRelatedField(RelatedField):
|
||||||
|
@ -236,10 +229,10 @@ class StringRelatedField(RelatedField):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
kwargs['read_only'] = True
|
kwargs['read_only'] = True
|
||||||
super(StringRelatedField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return six.text_type(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
class PrimaryKeyRelatedField(RelatedField):
|
class PrimaryKeyRelatedField(RelatedField):
|
||||||
|
@ -251,7 +244,7 @@ class PrimaryKeyRelatedField(RelatedField):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.pk_field = kwargs.pop('pk_field', None)
|
self.pk_field = kwargs.pop('pk_field', None)
|
||||||
super(PrimaryKeyRelatedField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def use_pk_only_optimization(self):
|
def use_pk_only_optimization(self):
|
||||||
return True
|
return True
|
||||||
|
@ -297,7 +290,7 @@ class HyperlinkedRelatedField(RelatedField):
|
||||||
# implicit `self` argument to be passed.
|
# implicit `self` argument to be passed.
|
||||||
self.reverse = reverse
|
self.reverse = reverse
|
||||||
|
|
||||||
super(HyperlinkedRelatedField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def use_pk_only_optimization(self):
|
def use_pk_only_optimization(self):
|
||||||
return self.lookup_field == 'pk'
|
return self.lookup_field == 'pk'
|
||||||
|
@ -317,10 +310,10 @@ class HyperlinkedRelatedField(RelatedField):
|
||||||
return queryset.get(**lookup_kwargs)
|
return queryset.get(**lookup_kwargs)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
exc = ObjectValueError(str(sys.exc_info()[1]))
|
exc = ObjectValueError(str(sys.exc_info()[1]))
|
||||||
six.reraise(type(exc), exc, sys.exc_info()[2])
|
raise exc.with_traceback(sys.exc_info()[2])
|
||||||
except TypeError:
|
except TypeError:
|
||||||
exc = ObjectTypeError(str(sys.exc_info()[1]))
|
exc = ObjectTypeError(str(sys.exc_info()[1]))
|
||||||
six.reraise(type(exc), exc, sys.exc_info()[2])
|
raise exc.with_traceback(sys.exc_info()[2])
|
||||||
|
|
||||||
def get_url(self, obj, view_name, request, format):
|
def get_url(self, obj, view_name, request, format):
|
||||||
"""
|
"""
|
||||||
|
@ -346,7 +339,7 @@ class HyperlinkedRelatedField(RelatedField):
|
||||||
|
|
||||||
if http_prefix:
|
if http_prefix:
|
||||||
# If needed convert absolute URLs to relative path
|
# If needed convert absolute URLs to relative path
|
||||||
data = urlparse.urlparse(data).path
|
data = parse.urlparse(data).path
|
||||||
prefix = get_script_prefix()
|
prefix = get_script_prefix()
|
||||||
if data.startswith(prefix):
|
if data.startswith(prefix):
|
||||||
data = '/' + data[len(prefix):]
|
data = '/' + data[len(prefix):]
|
||||||
|
@ -432,7 +425,7 @@ class HyperlinkedIdentityField(HyperlinkedRelatedField):
|
||||||
assert view_name is not None, 'The `view_name` argument is required.'
|
assert view_name is not None, 'The `view_name` argument is required.'
|
||||||
kwargs['read_only'] = True
|
kwargs['read_only'] = True
|
||||||
kwargs['source'] = '*'
|
kwargs['source'] = '*'
|
||||||
super(HyperlinkedIdentityField, self).__init__(view_name, **kwargs)
|
super().__init__(view_name, **kwargs)
|
||||||
|
|
||||||
def use_pk_only_optimization(self):
|
def use_pk_only_optimization(self):
|
||||||
# We have the complete object instance already. We don't need
|
# We have the complete object instance already. We don't need
|
||||||
|
@ -453,7 +446,7 @@ class SlugRelatedField(RelatedField):
|
||||||
def __init__(self, slug_field=None, **kwargs):
|
def __init__(self, slug_field=None, **kwargs):
|
||||||
assert slug_field is not None, 'The `slug_field` argument is required.'
|
assert slug_field is not None, 'The `slug_field` argument is required.'
|
||||||
self.slug_field = slug_field
|
self.slug_field = slug_field
|
||||||
super(SlugRelatedField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
try:
|
try:
|
||||||
|
@ -502,7 +495,7 @@ class ManyRelatedField(Field):
|
||||||
self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT)
|
self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT)
|
||||||
)
|
)
|
||||||
assert child_relation is not None, '`child_relation` is a required argument.'
|
assert child_relation is not None, '`child_relation` is a required argument.'
|
||||||
super(ManyRelatedField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.child_relation.bind(field_name='', parent=self)
|
self.child_relation.bind(field_name='', parent=self)
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
|
@ -518,7 +511,7 @@ class ManyRelatedField(Field):
|
||||||
return dictionary.get(self.field_name, empty)
|
return dictionary.get(self.field_name, empty)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if isinstance(data, six.text_type) or not hasattr(data, '__iter__'):
|
if isinstance(data, str) or not hasattr(data, '__iter__'):
|
||||||
self.fail('not_a_list', input_type=type(data).__name__)
|
self.fail('not_a_list', input_type=type(data).__name__)
|
||||||
if not self.allow_empty and len(data) == 0:
|
if not self.allow_empty and len(data) == 0:
|
||||||
self.fail('empty')
|
self.fail('empty')
|
||||||
|
|
|
@ -6,10 +6,9 @@ on the response, such as JSON encoded data or HTML output.
|
||||||
|
|
||||||
REST framework also provides an HTML renderer that renders the browsable API.
|
REST framework also provides an HTML renderer that renders the browsable API.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -19,9 +18,7 @@ from django.http.multipartparser import parse_header
|
||||||
from django.template import engines, loader
|
from django.template import engines, loader
|
||||||
from django.test.client import encode_multipart
|
from django.test.client import encode_multipart
|
||||||
from django.urls import NoReverseMatch
|
from django.urls import NoReverseMatch
|
||||||
from django.utils import six
|
|
||||||
from django.utils.html import mark_safe
|
from django.utils.html import mark_safe
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
|
||||||
|
|
||||||
from rest_framework import VERSION, exceptions, serializers, status
|
from rest_framework import VERSION, exceptions, serializers, status
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
|
@ -40,7 +37,7 @@ def zero_as_none(value):
|
||||||
return None if value == 0 else value
|
return None if value == 0 else value
|
||||||
|
|
||||||
|
|
||||||
class BaseRenderer(object):
|
class BaseRenderer:
|
||||||
"""
|
"""
|
||||||
All renderers should extend this class, setting the `media_type`
|
All renderers should extend this class, setting the `media_type`
|
||||||
and `format` attributes, and override the `.render()` method.
|
and `format` attributes, and override the `.render()` method.
|
||||||
|
@ -111,7 +108,7 @@ class JSONRenderer(BaseRenderer):
|
||||||
# but if ensure_ascii=False, the return type is underspecified,
|
# but if ensure_ascii=False, the return type is underspecified,
|
||||||
# and may (or may not) be unicode.
|
# and may (or may not) be unicode.
|
||||||
# On python 3.x json.dumps() returns unicode strings.
|
# On python 3.x json.dumps() returns unicode strings.
|
||||||
if isinstance(ret, six.text_type):
|
if isinstance(ret, str):
|
||||||
# We always fully escape \u2028 and \u2029 to ensure we output JSON
|
# We always fully escape \u2028 and \u2029 to ensure we output JSON
|
||||||
# that is a strict javascript subset. If bytes were returned
|
# that is a strict javascript subset. If bytes were returned
|
||||||
# by json.dumps() then we don't have these characters in any case.
|
# by json.dumps() then we don't have these characters in any case.
|
||||||
|
@ -349,7 +346,7 @@ class HTMLFormRenderer(BaseRenderer):
|
||||||
# Get a clone of the field with text-only value representation.
|
# Get a clone of the field with text-only value representation.
|
||||||
field = field.as_form_field()
|
field = field.as_form_field()
|
||||||
|
|
||||||
if style.get('input_type') == 'datetime-local' and isinstance(field.value, six.text_type):
|
if style.get('input_type') == 'datetime-local' and isinstance(field.value, str):
|
||||||
field.value = field.value.rstrip('Z')
|
field.value = field.value.rstrip('Z')
|
||||||
|
|
||||||
if 'template' in style:
|
if 'template' in style:
|
||||||
|
@ -791,7 +788,7 @@ class AdminRenderer(BrowsableAPIRenderer):
|
||||||
"""
|
"""
|
||||||
Render the HTML for the browsable API representation.
|
Render the HTML for the browsable API representation.
|
||||||
"""
|
"""
|
||||||
context = super(AdminRenderer, self).get_context(
|
context = super().get_context(
|
||||||
data, accepted_media_type, renderer_context
|
data, accepted_media_type, renderer_context
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -995,14 +992,14 @@ class _BaseOpenAPIRenderer:
|
||||||
|
|
||||||
tag = None
|
tag = None
|
||||||
for name, link in document.links.items():
|
for name, link in document.links.items():
|
||||||
path = urlparse.urlparse(link.url).path
|
path = parse.urlparse(link.url).path
|
||||||
method = link.action.lower()
|
method = link.action.lower()
|
||||||
paths.setdefault(path, {})
|
paths.setdefault(path, {})
|
||||||
paths[path][method] = self.get_operation(link, name, tag=tag)
|
paths[path][method] = self.get_operation(link, name, tag=tag)
|
||||||
|
|
||||||
for tag, section in document.data.items():
|
for tag, section in document.data.items():
|
||||||
for name, link in section.links.items():
|
for name, link in section.links.items():
|
||||||
path = urlparse.urlparse(link.url).path
|
path = parse.urlparse(link.url).path
|
||||||
method = link.action.lower()
|
method = link.action.lower()
|
||||||
paths.setdefault(path, {})
|
paths.setdefault(path, {})
|
||||||
paths[path][method] = self.get_operation(link, name, tag=tag)
|
paths[path][method] = self.get_operation(link, name, tag=tag)
|
||||||
|
|
|
@ -8,8 +8,6 @@ The wrapped request then offers a richer API, in particular :
|
||||||
- full support of PUT method, including support for file uploads
|
- full support of PUT method, including support for file uploads
|
||||||
- form overloading of HTTP method, content type and content
|
- form overloading of HTTP method, content type and content
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
@ -18,7 +16,6 @@ from django.conf import settings
|
||||||
from django.http import HttpRequest, QueryDict
|
from django.http import HttpRequest, QueryDict
|
||||||
from django.http.multipartparser import parse_header
|
from django.http.multipartparser import parse_header
|
||||||
from django.http.request import RawPostDataException
|
from django.http.request import RawPostDataException
|
||||||
from django.utils import six
|
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
|
|
||||||
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
||||||
|
@ -34,7 +31,7 @@ def is_form_media_type(media_type):
|
||||||
base_media_type == 'multipart/form-data')
|
base_media_type == 'multipart/form-data')
|
||||||
|
|
||||||
|
|
||||||
class override_method(object):
|
class override_method:
|
||||||
"""
|
"""
|
||||||
A context manager that temporarily overrides the method on a request,
|
A context manager that temporarily overrides the method on a request,
|
||||||
additionally setting the `view.request` attribute.
|
additionally setting the `view.request` attribute.
|
||||||
|
@ -78,10 +75,10 @@ def wrap_attributeerrors():
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
info = sys.exc_info()
|
info = sys.exc_info()
|
||||||
exc = WrappedAttributeError(str(info[1]))
|
exc = WrappedAttributeError(str(info[1]))
|
||||||
six.reraise(type(exc), exc, info[2])
|
raise exc.with_traceback(info[2])
|
||||||
|
|
||||||
|
|
||||||
class Empty(object):
|
class Empty:
|
||||||
"""
|
"""
|
||||||
Placeholder for unset attributes.
|
Placeholder for unset attributes.
|
||||||
Cannot use `None`, as that may be a valid value.
|
Cannot use `None`, as that may be a valid value.
|
||||||
|
@ -126,7 +123,7 @@ def clone_request(request, method):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class ForcedAuthentication(object):
|
class ForcedAuthentication:
|
||||||
"""
|
"""
|
||||||
This authentication class is used if the test client or request factory
|
This authentication class is used if the test client or request factory
|
||||||
forcibly authenticated the request.
|
forcibly authenticated the request.
|
||||||
|
@ -140,7 +137,7 @@ class ForcedAuthentication(object):
|
||||||
return (self.force_user, self.force_token)
|
return (self.force_user, self.force_token)
|
||||||
|
|
||||||
|
|
||||||
class Request(object):
|
class Request:
|
||||||
"""
|
"""
|
||||||
Wrapper allowing to enhance a standard `HttpRequest` instance.
|
Wrapper allowing to enhance a standard `HttpRequest` instance.
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,9 @@ it is initialized with unrendered data, instead of a pre-rendered string.
|
||||||
|
|
||||||
The appropriate renderer is called during Django's template response rendering.
|
The appropriate renderer is called during Django's template response rendering.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from http.client import responses
|
||||||
|
|
||||||
from django.template.response import SimpleTemplateResponse
|
from django.template.response import SimpleTemplateResponse
|
||||||
from django.utils import six
|
|
||||||
from django.utils.six.moves.http_client import responses
|
|
||||||
|
|
||||||
from rest_framework.serializers import Serializer
|
from rest_framework.serializers import Serializer
|
||||||
|
|
||||||
|
@ -29,7 +27,7 @@ class Response(SimpleTemplateResponse):
|
||||||
Setting 'renderer' and 'media_type' will typically be deferred,
|
Setting 'renderer' and 'media_type' will typically be deferred,
|
||||||
For example being set automatically by the `APIView`.
|
For example being set automatically by the `APIView`.
|
||||||
"""
|
"""
|
||||||
super(Response, self).__init__(None, status=status)
|
super().__init__(None, status=status)
|
||||||
|
|
||||||
if isinstance(data, Serializer):
|
if isinstance(data, Serializer):
|
||||||
msg = (
|
msg = (
|
||||||
|
@ -45,7 +43,7 @@ class Response(SimpleTemplateResponse):
|
||||||
self.content_type = content_type
|
self.content_type = content_type
|
||||||
|
|
||||||
if headers:
|
if headers:
|
||||||
for name, value in six.iteritems(headers):
|
for name, value in headers.items():
|
||||||
self[name] = value
|
self[name] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -64,13 +62,13 @@ class Response(SimpleTemplateResponse):
|
||||||
content_type = self.content_type
|
content_type = self.content_type
|
||||||
|
|
||||||
if content_type is None and charset is not None:
|
if content_type is None and charset is not None:
|
||||||
content_type = "{0}; charset={1}".format(media_type, charset)
|
content_type = "{}; charset={}".format(media_type, charset)
|
||||||
elif content_type is None:
|
elif content_type is None:
|
||||||
content_type = media_type
|
content_type = media_type
|
||||||
self['Content-Type'] = content_type
|
self['Content-Type'] = content_type
|
||||||
|
|
||||||
ret = renderer.render(self.data, accepted_media_type, context)
|
ret = renderer.render(self.data, accepted_media_type, context)
|
||||||
if isinstance(ret, six.text_type):
|
if isinstance(ret, str):
|
||||||
assert charset, (
|
assert charset, (
|
||||||
'renderer returned unicode, and did not specify '
|
'renderer returned unicode, and did not specify '
|
||||||
'a charset value.'
|
'a charset value.'
|
||||||
|
@ -94,7 +92,7 @@ class Response(SimpleTemplateResponse):
|
||||||
"""
|
"""
|
||||||
Remove attributes from the response that shouldn't be cached.
|
Remove attributes from the response that shouldn't be cached.
|
||||||
"""
|
"""
|
||||||
state = super(Response, self).__getstate__()
|
state = super().__getstate__()
|
||||||
for key in (
|
for key in (
|
||||||
'accepted_renderer', 'renderer_context', 'resolver_match',
|
'accepted_renderer', 'renderer_context', 'resolver_match',
|
||||||
'client', 'request', 'json', 'wsgi_request'
|
'client', 'request', 'json', 'wsgi_request'
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
"""
|
"""
|
||||||
Provide urlresolver functions that return fully qualified URLs or view names
|
Provide urlresolver functions that return fully qualified URLs or view names
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.urls import NoReverseMatch
|
from django.urls import NoReverseMatch
|
||||||
from django.urls import reverse as django_reverse
|
from django.urls import reverse as django_reverse
|
||||||
from django.utils import six
|
|
||||||
from django.utils.functional import lazy
|
from django.utils.functional import lazy
|
||||||
|
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
@ -66,4 +63,4 @@ def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extr
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
reverse_lazy = lazy(reverse, six.text_type)
|
reverse_lazy = lazy(reverse, str)
|
||||||
|
|
|
@ -13,8 +13,6 @@ For example, you might have a `urls.py` that looks something like this:
|
||||||
|
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import warnings
|
import warnings
|
||||||
from collections import OrderedDict, namedtuple
|
from collections import OrderedDict, namedtuple
|
||||||
|
@ -22,7 +20,6 @@ from collections import OrderedDict, namedtuple
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.urls import NoReverseMatch
|
from django.urls import NoReverseMatch
|
||||||
from django.utils import six
|
|
||||||
from django.utils.deprecation import RenameMethodsBase
|
from django.utils.deprecation import RenameMethodsBase
|
||||||
|
|
||||||
from rest_framework import (
|
from rest_framework import (
|
||||||
|
@ -39,7 +36,7 @@ Route = namedtuple('Route', ['url', 'mapping', 'name', 'detail', 'initkwargs'])
|
||||||
DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs'])
|
DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs'])
|
||||||
|
|
||||||
|
|
||||||
class DynamicDetailRoute(object):
|
class DynamicDetailRoute:
|
||||||
def __new__(cls, url, name, initkwargs):
|
def __new__(cls, url, name, initkwargs):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"`DynamicDetailRoute` is deprecated and will be removed in 3.10 "
|
"`DynamicDetailRoute` is deprecated and will be removed in 3.10 "
|
||||||
|
@ -50,7 +47,7 @@ class DynamicDetailRoute(object):
|
||||||
return DynamicRoute(url, name, True, initkwargs)
|
return DynamicRoute(url, name, True, initkwargs)
|
||||||
|
|
||||||
|
|
||||||
class DynamicListRoute(object):
|
class DynamicListRoute:
|
||||||
def __new__(cls, url, name, initkwargs):
|
def __new__(cls, url, name, initkwargs):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"`DynamicListRoute` is deprecated and will be removed in 3.10 in "
|
"`DynamicListRoute` is deprecated and will be removed in 3.10 in "
|
||||||
|
@ -83,7 +80,7 @@ class RenameRouterMethods(RenameMethodsBase):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseRouter(six.with_metaclass(RenameRouterMethods)):
|
class BaseRouter(metaclass=RenameRouterMethods):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.registry = []
|
self.registry = []
|
||||||
|
|
||||||
|
@ -173,7 +170,7 @@ class SimpleRouter(BaseRouter):
|
||||||
|
|
||||||
def __init__(self, trailing_slash=True):
|
def __init__(self, trailing_slash=True):
|
||||||
self.trailing_slash = '/' if trailing_slash else ''
|
self.trailing_slash = '/' if trailing_slash else ''
|
||||||
super(SimpleRouter, self).__init__()
|
super().__init__()
|
||||||
|
|
||||||
def get_default_basename(self, viewset):
|
def get_default_basename(self, viewset):
|
||||||
"""
|
"""
|
||||||
|
@ -365,7 +362,7 @@ class DefaultRouter(SimpleRouter):
|
||||||
self.root_renderers = kwargs.pop('root_renderers')
|
self.root_renderers = kwargs.pop('root_renderers')
|
||||||
else:
|
else:
|
||||||
self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES)
|
self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES)
|
||||||
super(DefaultRouter, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_api_root_view(self, api_urls=None):
|
def get_api_root_view(self, api_urls=None):
|
||||||
"""
|
"""
|
||||||
|
@ -383,7 +380,7 @@ class DefaultRouter(SimpleRouter):
|
||||||
Generate the list of URL patterns, including a default root view
|
Generate the list of URL patterns, including a default root view
|
||||||
for the API, and appending `.json` style format suffixes.
|
for the API, and appending `.json` style format suffixes.
|
||||||
"""
|
"""
|
||||||
urls = super(DefaultRouter, self).get_urls()
|
urls = super().get_urls()
|
||||||
|
|
||||||
if self.include_root_view:
|
if self.include_root_view:
|
||||||
view = self.get_api_root_view(api_urls=urls)
|
view = self.get_api_root_view(api_urls=urls)
|
||||||
|
|
|
@ -11,7 +11,6 @@ from django.conf import settings
|
||||||
from django.contrib.admindocs.views import simplify_regex
|
from django.contrib.admindocs.views import simplify_regex
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
|
@ -68,7 +67,7 @@ class LinkNode(OrderedDict):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.links = []
|
self.links = []
|
||||||
self.methods_counter = Counter()
|
self.methods_counter = Counter()
|
||||||
super(LinkNode, self).__init__()
|
super().__init__()
|
||||||
|
|
||||||
def get_available_key(self, preferred_key):
|
def get_available_key(self, preferred_key):
|
||||||
if preferred_key not in self:
|
if preferred_key not in self:
|
||||||
|
@ -140,7 +139,7 @@ _PATH_PARAMETER_COMPONENT_RE = re.compile(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EndpointEnumerator(object):
|
class EndpointEnumerator:
|
||||||
"""
|
"""
|
||||||
A class to determine the available API endpoints that a project exposes.
|
A class to determine the available API endpoints that a project exposes.
|
||||||
"""
|
"""
|
||||||
|
@ -151,7 +150,7 @@ class EndpointEnumerator(object):
|
||||||
urlconf = settings.ROOT_URLCONF
|
urlconf = settings.ROOT_URLCONF
|
||||||
|
|
||||||
# Load the given URLconf module
|
# Load the given URLconf module
|
||||||
if isinstance(urlconf, six.string_types):
|
if isinstance(urlconf, str):
|
||||||
urls = import_module(urlconf)
|
urls = import_module(urlconf)
|
||||||
else:
|
else:
|
||||||
urls = urlconf
|
urls = urlconf
|
||||||
|
@ -232,7 +231,7 @@ class EndpointEnumerator(object):
|
||||||
return [method for method in methods if method not in ('OPTIONS', 'HEAD')]
|
return [method for method in methods if method not in ('OPTIONS', 'HEAD')]
|
||||||
|
|
||||||
|
|
||||||
class SchemaGenerator(object):
|
class SchemaGenerator:
|
||||||
# Map HTTP methods onto actions.
|
# Map HTTP methods onto actions.
|
||||||
default_mapping = {
|
default_mapping = {
|
||||||
'get': 'retrieve',
|
'get': 'retrieve',
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
inspectors.py # Per-endpoint view introspection
|
inspectors.py # Per-endpoint view introspection
|
||||||
|
|
||||||
|
@ -7,11 +6,11 @@ See schemas.__init__.py for package overview.
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from urllib import parse
|
||||||
from weakref import WeakKeyDictionary
|
from weakref import WeakKeyDictionary
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import force_text, smart_text
|
from django.utils.encoding import force_text, smart_text
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import exceptions, serializers
|
from rest_framework import exceptions, serializers
|
||||||
|
@ -125,7 +124,7 @@ def get_pk_description(model, model_field):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ViewInspector(object):
|
class ViewInspector:
|
||||||
"""
|
"""
|
||||||
Descriptor class on APIView.
|
Descriptor class on APIView.
|
||||||
|
|
||||||
|
@ -207,7 +206,7 @@ class AutoSchema(ViewInspector):
|
||||||
* `manual_fields`: list of `coreapi.Field` instances that
|
* `manual_fields`: list of `coreapi.Field` instances that
|
||||||
will be added to auto-generated fields, overwriting on `Field.name`
|
will be added to auto-generated fields, overwriting on `Field.name`
|
||||||
"""
|
"""
|
||||||
super(AutoSchema, self).__init__()
|
super().__init__()
|
||||||
if manual_fields is None:
|
if manual_fields is None:
|
||||||
manual_fields = []
|
manual_fields = []
|
||||||
self._manual_fields = manual_fields
|
self._manual_fields = manual_fields
|
||||||
|
@ -232,7 +231,7 @@ class AutoSchema(ViewInspector):
|
||||||
path = path[1:]
|
path = path[1:]
|
||||||
|
|
||||||
return coreapi.Link(
|
return coreapi.Link(
|
||||||
url=urlparse.urljoin(base_url, path),
|
url=parse.urljoin(base_url, path),
|
||||||
action=method.lower(),
|
action=method.lower(),
|
||||||
encoding=encoding,
|
encoding=encoding,
|
||||||
fields=fields,
|
fields=fields,
|
||||||
|
@ -475,7 +474,7 @@ class ManualSchema(ViewInspector):
|
||||||
* `fields`: list of `coreapi.Field` instances.
|
* `fields`: list of `coreapi.Field` instances.
|
||||||
* `description`: String description for view. Optional.
|
* `description`: String description for view. Optional.
|
||||||
"""
|
"""
|
||||||
super(ManualSchema, self).__init__()
|
super().__init__()
|
||||||
assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances"
|
assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances"
|
||||||
self._fields = fields
|
self._fields = fields
|
||||||
self._description = description
|
self._description = description
|
||||||
|
@ -487,7 +486,7 @@ class ManualSchema(ViewInspector):
|
||||||
path = path[1:]
|
path = path[1:]
|
||||||
|
|
||||||
return coreapi.Link(
|
return coreapi.Link(
|
||||||
url=urlparse.urljoin(base_url, path),
|
url=parse.urljoin(base_url, path),
|
||||||
action=method.lower(),
|
action=method.lower(),
|
||||||
encoding=self._encoding,
|
encoding=self._encoding,
|
||||||
fields=self._fields,
|
fields=self._fields,
|
||||||
|
@ -498,7 +497,7 @@ class ManualSchema(ViewInspector):
|
||||||
class DefaultSchema(ViewInspector):
|
class DefaultSchema(ViewInspector):
|
||||||
"""Allows overriding AutoSchema using DEFAULT_SCHEMA_CLASS setting"""
|
"""Allows overriding AutoSchema using DEFAULT_SCHEMA_CLASS setting"""
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
result = super(DefaultSchema, self).__get__(instance, owner)
|
result = super().__get__(instance, owner)
|
||||||
if not isinstance(result, DefaultSchema):
|
if not isinstance(result, DefaultSchema):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ class SchemaView(APIView):
|
||||||
public = False
|
public = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SchemaView, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
if self.renderer_classes is None:
|
if self.renderer_classes is None:
|
||||||
self.renderer_classes = [
|
self.renderer_classes = [
|
||||||
renderers.OpenAPIRenderer,
|
renderers.OpenAPIRenderer,
|
||||||
|
@ -38,4 +38,4 @@ class SchemaView(APIView):
|
||||||
self.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
|
self.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
|
||||||
neg = self.perform_content_negotiation(self.request, force=True)
|
neg = self.perform_content_negotiation(self.request, force=True)
|
||||||
self.request.accepted_renderer, self.request.accepted_media_type = neg
|
self.request.accepted_renderer, self.request.accepted_media_type = neg
|
||||||
return super(SchemaView, self).handle_exception(exc)
|
return super().handle_exception(exc)
|
||||||
|
|
|
@ -10,8 +10,6 @@ python primitives.
|
||||||
2. The process of marshalling between python primitives and request and
|
2. The process of marshalling between python primitives and request and
|
||||||
response content is handled by parsers and renderers.
|
response content is handled by parsers and renderers.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -23,11 +21,11 @@ from django.db import models
|
||||||
from django.db.models import DurationField as ModelDurationField
|
from django.db.models import DurationField as ModelDurationField
|
||||||
from django.db.models.fields import Field as DjangoModelField
|
from django.db.models.fields import Field as DjangoModelField
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
from django.utils import six, timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.compat import Mapping, postgres_fields, unicode_to_repr
|
from rest_framework.compat import Mapping, postgres_fields
|
||||||
from rest_framework.exceptions import ErrorDetail, ValidationError
|
from rest_framework.exceptions import ErrorDetail, ValidationError
|
||||||
from rest_framework.fields import get_error_detail, set_value
|
from rest_framework.fields import get_error_detail, set_value
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
@ -115,14 +113,14 @@ class BaseSerializer(Field):
|
||||||
self.partial = kwargs.pop('partial', False)
|
self.partial = kwargs.pop('partial', False)
|
||||||
self._context = kwargs.pop('context', {})
|
self._context = kwargs.pop('context', {})
|
||||||
kwargs.pop('many', None)
|
kwargs.pop('many', None)
|
||||||
super(BaseSerializer, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
# We override this method in order to automagically create
|
# We override this method in order to automagically create
|
||||||
# `ListSerializer` classes instead when `many=True` is set.
|
# `ListSerializer` classes instead when `many=True` is set.
|
||||||
if kwargs.pop('many', False):
|
if kwargs.pop('many', False):
|
||||||
return cls.many_init(*args, **kwargs)
|
return cls.many_init(*args, **kwargs)
|
||||||
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
|
return super().__new__(cls, *args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def many_init(cls, *args, **kwargs):
|
def many_init(cls, *args, **kwargs):
|
||||||
|
@ -315,7 +313,7 @@ class SerializerMetaclass(type):
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
|
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
|
||||||
return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
|
return super().__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
def as_serializer_error(exc):
|
def as_serializer_error(exc):
|
||||||
|
@ -344,8 +342,7 @@ def as_serializer_error(exc):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(SerializerMetaclass)
|
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
|
||||||
class Serializer(BaseSerializer):
|
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.')
|
'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.')
|
||||||
}
|
}
|
||||||
|
@ -466,7 +463,7 @@ class Serializer(BaseSerializer):
|
||||||
to_validate.update(value)
|
to_validate.update(value)
|
||||||
else:
|
else:
|
||||||
to_validate = value
|
to_validate = value
|
||||||
super(Serializer, self).run_validators(to_validate)
|
super().run_validators(to_validate)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
"""
|
"""
|
||||||
|
@ -535,7 +532,7 @@ class Serializer(BaseSerializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr(representation.serializer_repr(self, indent=1))
|
return representation.serializer_repr(self, indent=1)
|
||||||
|
|
||||||
# The following are used for accessing `BoundField` instances on the
|
# The following are used for accessing `BoundField` instances on the
|
||||||
# serializer, for the purposes of presenting a form-like API onto the
|
# serializer, for the purposes of presenting a form-like API onto the
|
||||||
|
@ -560,12 +557,12 @@ class Serializer(BaseSerializer):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
ret = super(Serializer, self).data
|
ret = super().data
|
||||||
return ReturnDict(ret, serializer=self)
|
return ReturnDict(ret, serializer=self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def errors(self):
|
def errors(self):
|
||||||
ret = super(Serializer, self).errors
|
ret = super().errors
|
||||||
if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
|
if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
|
||||||
# Edge case. Provide a more descriptive error than
|
# Edge case. Provide a more descriptive error than
|
||||||
# "this field may not be null", when no data is passed.
|
# "this field may not be null", when no data is passed.
|
||||||
|
@ -591,11 +588,11 @@ class ListSerializer(BaseSerializer):
|
||||||
self.allow_empty = kwargs.pop('allow_empty', True)
|
self.allow_empty = kwargs.pop('allow_empty', True)
|
||||||
assert self.child is not None, '`child` is a required argument.'
|
assert self.child is not None, '`child` is a required argument.'
|
||||||
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
||||||
super(ListSerializer, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.child.bind(field_name='', parent=self)
|
self.child.bind(field_name='', parent=self)
|
||||||
|
|
||||||
def bind(self, field_name, parent):
|
def bind(self, field_name, parent):
|
||||||
super(ListSerializer, self).bind(field_name, parent)
|
super().bind(field_name, parent)
|
||||||
self.partial = self.parent.partial
|
self.partial = self.parent.partial
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
|
@ -758,19 +755,19 @@ class ListSerializer(BaseSerializer):
|
||||||
return not bool(self._errors)
|
return not bool(self._errors)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr(representation.list_repr(self, indent=1))
|
return representation.list_repr(self, indent=1)
|
||||||
|
|
||||||
# Include a backlink to the serializer class on return objects.
|
# Include a backlink to the serializer class on return objects.
|
||||||
# Allows renderers such as HTMLFormRenderer to get the full field info.
|
# Allows renderers such as HTMLFormRenderer to get the full field info.
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
ret = super(ListSerializer, self).data
|
ret = super().data
|
||||||
return ReturnList(ret, serializer=self)
|
return ReturnList(ret, serializer=self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def errors(self):
|
def errors(self):
|
||||||
ret = super(ListSerializer, self).errors
|
ret = super().errors
|
||||||
if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
|
if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
|
||||||
# Edge case. Provide a more descriptive error than
|
# Edge case. Provide a more descriptive error than
|
||||||
# "this field may not be null", when no data is passed.
|
# "this field may not be null", when no data is passed.
|
||||||
|
|
|
@ -18,13 +18,10 @@ This module provides the `api_setting` object, that is used to access
|
||||||
REST framework settings, checking for user settings first, then falling
|
REST framework settings, checking for user settings first, then falling
|
||||||
back to the defaults.
|
back to the defaults.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.test.signals import setting_changed
|
from django.test.signals import setting_changed
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import ISO_8601
|
from rest_framework import ISO_8601
|
||||||
|
|
||||||
|
@ -166,7 +163,7 @@ def perform_import(val, setting_name):
|
||||||
"""
|
"""
|
||||||
if val is None:
|
if val is None:
|
||||||
return None
|
return None
|
||||||
elif isinstance(val, six.string_types):
|
elif isinstance(val, str):
|
||||||
return import_from_string(val, setting_name)
|
return import_from_string(val, setting_name)
|
||||||
elif isinstance(val, (list, tuple)):
|
elif isinstance(val, (list, tuple)):
|
||||||
return [import_from_string(item, setting_name) for item in val]
|
return [import_from_string(item, setting_name) for item in val]
|
||||||
|
@ -187,7 +184,7 @@ def import_from_string(val, setting_name):
|
||||||
raise ImportError(msg)
|
raise ImportError(msg)
|
||||||
|
|
||||||
|
|
||||||
class APISettings(object):
|
class APISettings:
|
||||||
"""
|
"""
|
||||||
A settings object, that allows API settings to be accessed as properties.
|
A settings object, that allows API settings to be accessed as properties.
|
||||||
For example:
|
For example:
|
||||||
|
|
|
@ -5,7 +5,6 @@ See RFC 2616 - https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||||
And RFC 6585 - https://tools.ietf.org/html/rfc6585
|
And RFC 6585 - https://tools.ietf.org/html/rfc6585
|
||||||
And RFC 4918 - https://tools.ietf.org/html/rfc4918
|
And RFC 4918 - https://tools.ietf.org/html/rfc4918
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
|
|
||||||
def is_informational(code):
|
def is_informational(code):
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.urls import NoReverseMatch, reverse
|
from django.urls import NoReverseMatch, reverse
|
||||||
from django.utils import six
|
|
||||||
from django.utils.encoding import force_text, iri_to_uri
|
from django.utils.encoding import force_text, iri_to_uri
|
||||||
from django.utils.html import escape, format_html, smart_urlquote
|
from django.utils.html import escape, format_html, smart_urlquote
|
||||||
from django.utils.safestring import SafeData, mark_safe
|
from django.utils.safestring import SafeData, mark_safe
|
||||||
|
@ -187,7 +184,7 @@ def add_class(value, css_class):
|
||||||
In the case of REST Framework, the filter is used to add Bootstrap-specific
|
In the case of REST Framework, the filter is used to add Bootstrap-specific
|
||||||
classes to the forms.
|
classes to the forms.
|
||||||
"""
|
"""
|
||||||
html = six.text_type(value)
|
html = str(value)
|
||||||
match = class_re.search(html)
|
match = class_re.search(html)
|
||||||
if match:
|
if match:
|
||||||
m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class,
|
m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class,
|
||||||
|
@ -204,7 +201,7 @@ def add_class(value, css_class):
|
||||||
@register.filter
|
@register.filter
|
||||||
def format_value(value):
|
def format_value(value):
|
||||||
if getattr(value, 'is_hyperlink', False):
|
if getattr(value, 'is_hyperlink', False):
|
||||||
name = six.text_type(value.obj)
|
name = str(value.obj)
|
||||||
return mark_safe('<a href=%s>%s</a>' % (value, escape(name)))
|
return mark_safe('<a href=%s>%s</a>' % (value, escape(name)))
|
||||||
if value is None or isinstance(value, bool):
|
if value is None or isinstance(value, bool):
|
||||||
return mark_safe('<code>%s</code>' % {True: 'true', False: 'false', None: 'null'}[value])
|
return mark_safe('<code>%s</code>' % {True: 'true', False: 'false', None: 'null'}[value])
|
||||||
|
@ -219,7 +216,7 @@ def format_value(value):
|
||||||
template = loader.get_template('rest_framework/admin/dict_value.html')
|
template = loader.get_template('rest_framework/admin/dict_value.html')
|
||||||
context = {'value': value}
|
context = {'value': value}
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
elif isinstance(value, six.string_types):
|
elif isinstance(value, str):
|
||||||
if (
|
if (
|
||||||
(value.startswith('http:') or value.startswith('https:')) and not
|
(value.startswith('http:') or value.startswith('https:')) and not
|
||||||
re.search(r'\s', value)
|
re.search(r'\s', value)
|
||||||
|
@ -229,7 +226,7 @@ def format_value(value):
|
||||||
return mark_safe('<a href="mailto:{value}">{value}</a>'.format(value=escape(value)))
|
return mark_safe('<a href="mailto:{value}">{value}</a>'.format(value=escape(value)))
|
||||||
elif '\n' in value:
|
elif '\n' in value:
|
||||||
return mark_safe('<pre>%s</pre>' % escape(value))
|
return mark_safe('<pre>%s</pre>' % escape(value))
|
||||||
return six.text_type(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
# -- coding: utf-8 --
|
|
||||||
|
|
||||||
# Note that we import as `DjangoRequestFactory` and `DjangoClient` in order
|
# Note that we import as `DjangoRequestFactory` and `DjangoClient` in order
|
||||||
# to make it harder for the user to import the wrong thing without realizing.
|
# to make it harder for the user to import the wrong thing without realizing.
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import io
|
import io
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
|
@ -14,7 +10,6 @@ from django.test import override_settings, testcases
|
||||||
from django.test.client import Client as DjangoClient
|
from django.test.client import Client as DjangoClient
|
||||||
from django.test.client import ClientHandler
|
from django.test.client import ClientHandler
|
||||||
from django.test.client import RequestFactory as DjangoRequestFactory
|
from django.test.client import RequestFactory as DjangoRequestFactory
|
||||||
from django.utils import six
|
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
|
|
||||||
|
@ -32,7 +27,7 @@ if requests is not None:
|
||||||
def get_all(self, key, default):
|
def get_all(self, key, default):
|
||||||
return self.getheaders(key)
|
return self.getheaders(key)
|
||||||
|
|
||||||
class MockOriginalResponse(object):
|
class MockOriginalResponse:
|
||||||
def __init__(self, headers):
|
def __init__(self, headers):
|
||||||
self.msg = HeaderDict(headers)
|
self.msg = HeaderDict(headers)
|
||||||
self.closed = False
|
self.closed = False
|
||||||
|
@ -109,7 +104,7 @@ if requests is not None:
|
||||||
|
|
||||||
class RequestsClient(requests.Session):
|
class RequestsClient(requests.Session):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(RequestsClient, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
adapter = DjangoTestAdapter()
|
adapter = DjangoTestAdapter()
|
||||||
self.mount('http://', adapter)
|
self.mount('http://', adapter)
|
||||||
self.mount('https://', adapter)
|
self.mount('https://', adapter)
|
||||||
|
@ -117,7 +112,7 @@ if requests is not None:
|
||||||
def request(self, method, url, *args, **kwargs):
|
def request(self, method, url, *args, **kwargs):
|
||||||
if not url.startswith('http'):
|
if not url.startswith('http'):
|
||||||
raise ValueError('Missing "http:" or "https:". Use a fully qualified URL, eg "http://testserver%s"' % url)
|
raise ValueError('Missing "http:" or "https:". Use a fully qualified URL, eg "http://testserver%s"' % url)
|
||||||
return super(RequestsClient, self).request(method, url, *args, **kwargs)
|
return super().request(method, url, *args, **kwargs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
def RequestsClient(*args, **kwargs):
|
def RequestsClient(*args, **kwargs):
|
||||||
|
@ -129,7 +124,7 @@ if coreapi is not None:
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._session = RequestsClient()
|
self._session = RequestsClient()
|
||||||
kwargs['transports'] = [coreapi.transports.HTTPTransport(session=self.session)]
|
kwargs['transports'] = [coreapi.transports.HTTPTransport(session=self.session)]
|
||||||
return super(CoreAPIClient, self).__init__(*args, **kwargs)
|
return super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def session(self):
|
def session(self):
|
||||||
|
@ -149,7 +144,7 @@ class APIRequestFactory(DjangoRequestFactory):
|
||||||
self.renderer_classes = {}
|
self.renderer_classes = {}
|
||||||
for cls in self.renderer_classes_list:
|
for cls in self.renderer_classes_list:
|
||||||
self.renderer_classes[cls.format] = cls
|
self.renderer_classes[cls.format] = cls
|
||||||
super(APIRequestFactory, self).__init__(**defaults)
|
super().__init__(**defaults)
|
||||||
|
|
||||||
def _encode_data(self, data, format=None, content_type=None):
|
def _encode_data(self, data, format=None, content_type=None):
|
||||||
"""
|
"""
|
||||||
|
@ -171,7 +166,7 @@ class APIRequestFactory(DjangoRequestFactory):
|
||||||
format = format or self.default_format
|
format = format or self.default_format
|
||||||
|
|
||||||
assert format in self.renderer_classes, (
|
assert format in self.renderer_classes, (
|
||||||
"Invalid format '{0}'. Available formats are {1}. "
|
"Invalid format '{}'. Available formats are {}. "
|
||||||
"Set TEST_REQUEST_RENDERER_CLASSES to enable "
|
"Set TEST_REQUEST_RENDERER_CLASSES to enable "
|
||||||
"extra request formats.".format(
|
"extra request formats.".format(
|
||||||
format,
|
format,
|
||||||
|
@ -184,12 +179,12 @@ class APIRequestFactory(DjangoRequestFactory):
|
||||||
ret = renderer.render(data)
|
ret = renderer.render(data)
|
||||||
|
|
||||||
# Determine the content-type header from the renderer
|
# Determine the content-type header from the renderer
|
||||||
content_type = "{0}; charset={1}".format(
|
content_type = "{}; charset={}".format(
|
||||||
renderer.media_type, renderer.charset
|
renderer.media_type, renderer.charset
|
||||||
)
|
)
|
||||||
|
|
||||||
# Coerce text to bytes if required.
|
# Coerce text to bytes if required.
|
||||||
if isinstance(ret, six.text_type):
|
if isinstance(ret, str):
|
||||||
ret = bytes(ret.encode(renderer.charset))
|
ret = bytes(ret.encode(renderer.charset))
|
||||||
|
|
||||||
return ret, content_type
|
return ret, content_type
|
||||||
|
@ -202,8 +197,7 @@ class APIRequestFactory(DjangoRequestFactory):
|
||||||
# Fix to support old behavior where you have the arguments in the
|
# Fix to support old behavior where you have the arguments in the
|
||||||
# url. See #1461.
|
# url. See #1461.
|
||||||
query_string = force_bytes(path.split('?')[1])
|
query_string = force_bytes(path.split('?')[1])
|
||||||
if six.PY3:
|
query_string = query_string.decode('iso-8859-1')
|
||||||
query_string = query_string.decode('iso-8859-1')
|
|
||||||
r['QUERY_STRING'] = query_string
|
r['QUERY_STRING'] = query_string
|
||||||
r.update(extra)
|
r.update(extra)
|
||||||
return self.generic('GET', path, **r)
|
return self.generic('GET', path, **r)
|
||||||
|
@ -234,11 +228,11 @@ class APIRequestFactory(DjangoRequestFactory):
|
||||||
if content_type is not None:
|
if content_type is not None:
|
||||||
extra['CONTENT_TYPE'] = str(content_type)
|
extra['CONTENT_TYPE'] = str(content_type)
|
||||||
|
|
||||||
return super(APIRequestFactory, self).generic(
|
return super().generic(
|
||||||
method, path, data, content_type, secure, **extra)
|
method, path, data, content_type, secure, **extra)
|
||||||
|
|
||||||
def request(self, **kwargs):
|
def request(self, **kwargs):
|
||||||
request = super(APIRequestFactory, self).request(**kwargs)
|
request = super().request(**kwargs)
|
||||||
request._dont_enforce_csrf_checks = not self.enforce_csrf_checks
|
request._dont_enforce_csrf_checks = not self.enforce_csrf_checks
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
@ -252,18 +246,18 @@ class ForceAuthClientHandler(ClientHandler):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._force_user = None
|
self._force_user = None
|
||||||
self._force_token = None
|
self._force_token = None
|
||||||
super(ForceAuthClientHandler, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_response(self, request):
|
def get_response(self, request):
|
||||||
# This is the simplest place we can hook into to patch the
|
# This is the simplest place we can hook into to patch the
|
||||||
# request object.
|
# request object.
|
||||||
force_authenticate(request, self._force_user, self._force_token)
|
force_authenticate(request, self._force_user, self._force_token)
|
||||||
return super(ForceAuthClientHandler, self).get_response(request)
|
return super().get_response(request)
|
||||||
|
|
||||||
|
|
||||||
class APIClient(APIRequestFactory, DjangoClient):
|
class APIClient(APIRequestFactory, DjangoClient):
|
||||||
def __init__(self, enforce_csrf_checks=False, **defaults):
|
def __init__(self, enforce_csrf_checks=False, **defaults):
|
||||||
super(APIClient, self).__init__(**defaults)
|
super().__init__(**defaults)
|
||||||
self.handler = ForceAuthClientHandler(enforce_csrf_checks)
|
self.handler = ForceAuthClientHandler(enforce_csrf_checks)
|
||||||
self._credentials = {}
|
self._credentials = {}
|
||||||
|
|
||||||
|
@ -286,17 +280,17 @@ class APIClient(APIRequestFactory, DjangoClient):
|
||||||
def request(self, **kwargs):
|
def request(self, **kwargs):
|
||||||
# Ensure that any credentials set get added to every request.
|
# Ensure that any credentials set get added to every request.
|
||||||
kwargs.update(self._credentials)
|
kwargs.update(self._credentials)
|
||||||
return super(APIClient, self).request(**kwargs)
|
return super().request(**kwargs)
|
||||||
|
|
||||||
def get(self, path, data=None, follow=False, **extra):
|
def get(self, path, data=None, follow=False, **extra):
|
||||||
response = super(APIClient, self).get(path, data=data, **extra)
|
response = super().get(path, data=data, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def post(self, path, data=None, format=None, content_type=None,
|
def post(self, path, data=None, format=None, content_type=None,
|
||||||
follow=False, **extra):
|
follow=False, **extra):
|
||||||
response = super(APIClient, self).post(
|
response = super().post(
|
||||||
path, data=data, format=format, content_type=content_type, **extra)
|
path, data=data, format=format, content_type=content_type, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
|
@ -304,7 +298,7 @@ class APIClient(APIRequestFactory, DjangoClient):
|
||||||
|
|
||||||
def put(self, path, data=None, format=None, content_type=None,
|
def put(self, path, data=None, format=None, content_type=None,
|
||||||
follow=False, **extra):
|
follow=False, **extra):
|
||||||
response = super(APIClient, self).put(
|
response = super().put(
|
||||||
path, data=data, format=format, content_type=content_type, **extra)
|
path, data=data, format=format, content_type=content_type, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
|
@ -312,7 +306,7 @@ class APIClient(APIRequestFactory, DjangoClient):
|
||||||
|
|
||||||
def patch(self, path, data=None, format=None, content_type=None,
|
def patch(self, path, data=None, format=None, content_type=None,
|
||||||
follow=False, **extra):
|
follow=False, **extra):
|
||||||
response = super(APIClient, self).patch(
|
response = super().patch(
|
||||||
path, data=data, format=format, content_type=content_type, **extra)
|
path, data=data, format=format, content_type=content_type, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
|
@ -320,7 +314,7 @@ class APIClient(APIRequestFactory, DjangoClient):
|
||||||
|
|
||||||
def delete(self, path, data=None, format=None, content_type=None,
|
def delete(self, path, data=None, format=None, content_type=None,
|
||||||
follow=False, **extra):
|
follow=False, **extra):
|
||||||
response = super(APIClient, self).delete(
|
response = super().delete(
|
||||||
path, data=data, format=format, content_type=content_type, **extra)
|
path, data=data, format=format, content_type=content_type, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
|
@ -328,7 +322,7 @@ class APIClient(APIRequestFactory, DjangoClient):
|
||||||
|
|
||||||
def options(self, path, data=None, format=None, content_type=None,
|
def options(self, path, data=None, format=None, content_type=None,
|
||||||
follow=False, **extra):
|
follow=False, **extra):
|
||||||
response = super(APIClient, self).options(
|
response = super().options(
|
||||||
path, data=data, format=format, content_type=content_type, **extra)
|
path, data=data, format=format, content_type=content_type, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
|
@ -342,7 +336,7 @@ class APIClient(APIRequestFactory, DjangoClient):
|
||||||
self.handler._force_token = None
|
self.handler._force_token = None
|
||||||
|
|
||||||
if self.session:
|
if self.session:
|
||||||
super(APIClient, self).logout()
|
super().logout()
|
||||||
|
|
||||||
|
|
||||||
class APITransactionTestCase(testcases.TransactionTestCase):
|
class APITransactionTestCase(testcases.TransactionTestCase):
|
||||||
|
@ -389,11 +383,11 @@ class URLPatternsTestCase(testcases.SimpleTestCase):
|
||||||
cls._module.urlpatterns = cls.urlpatterns
|
cls._module.urlpatterns = cls.urlpatterns
|
||||||
|
|
||||||
cls._override.enable()
|
cls._override.enable()
|
||||||
super(URLPatternsTestCase, cls).setUpClass()
|
super().setUpClass()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
super(URLPatternsTestCase, cls).tearDownClass()
|
super().tearDownClass()
|
||||||
cls._override.disable()
|
cls._override.disable()
|
||||||
|
|
||||||
if hasattr(cls, '_module_urlpatterns'):
|
if hasattr(cls, '_module_urlpatterns'):
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""
|
"""
|
||||||
Provides various throttling policies.
|
Provides various throttling policies.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from django.core.cache import cache as default_cache
|
from django.core.cache import cache as default_cache
|
||||||
|
@ -11,7 +9,7 @@ from django.core.exceptions import ImproperlyConfigured
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
class BaseThrottle(object):
|
class BaseThrottle:
|
||||||
"""
|
"""
|
||||||
Rate throttling of requests.
|
Rate throttling of requests.
|
||||||
"""
|
"""
|
||||||
|
@ -232,7 +230,7 @@ class ScopedRateThrottle(SimpleRateThrottle):
|
||||||
self.num_requests, self.duration = self.parse_rate(self.rate)
|
self.num_requests, self.duration = self.parse_rate(self.rate)
|
||||||
|
|
||||||
# We can now proceed as normal.
|
# We can now proceed as normal.
|
||||||
return super(ScopedRateThrottle, self).allow_request(request, view)
|
return super().allow_request(request, view)
|
||||||
|
|
||||||
def get_cache_key(self, request, view):
|
def get_cache_key(self, request, view):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
|
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
|
|
|
@ -11,8 +11,6 @@ your API requires authentication:
|
||||||
|
|
||||||
You should make sure your authentication settings include `SessionAuthentication`.
|
You should make sure your authentication settings include `SessionAuthentication`.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib.auth import views
|
from django.contrib.auth import views
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.urls import get_script_prefix, resolve
|
from django.urls import get_script_prefix, resolve
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
"""
|
"""
|
||||||
Helper classes for parsers.
|
Helper classes for parsers.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
import json # noqa
|
import json # noqa
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.utils import six, timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.functional import Promise
|
from django.utils.functional import Promise
|
||||||
|
|
||||||
|
@ -39,12 +37,12 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
representation = obj.isoformat()
|
representation = obj.isoformat()
|
||||||
return representation
|
return representation
|
||||||
elif isinstance(obj, datetime.timedelta):
|
elif isinstance(obj, datetime.timedelta):
|
||||||
return six.text_type(obj.total_seconds())
|
return str(obj.total_seconds())
|
||||||
elif isinstance(obj, decimal.Decimal):
|
elif isinstance(obj, decimal.Decimal):
|
||||||
# Serializers will coerce decimals to strings by default.
|
# Serializers will coerce decimals to strings by default.
|
||||||
return float(obj)
|
return float(obj)
|
||||||
elif isinstance(obj, uuid.UUID):
|
elif isinstance(obj, uuid.UUID):
|
||||||
return six.text_type(obj)
|
return str(obj)
|
||||||
elif isinstance(obj, QuerySet):
|
elif isinstance(obj, QuerySet):
|
||||||
return tuple(obj)
|
return tuple(obj)
|
||||||
elif isinstance(obj, bytes):
|
elif isinstance(obj, bytes):
|
||||||
|
@ -65,4 +63,4 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
pass
|
pass
|
||||||
elif hasattr(obj, '__iter__'):
|
elif hasattr(obj, '__iter__'):
|
||||||
return tuple(item for item in obj)
|
return tuple(item for item in obj)
|
||||||
return super(JSONEncoder, self).default(obj)
|
return super().default(obj)
|
||||||
|
|
|
@ -16,7 +16,7 @@ NUMERIC_FIELD_TYPES = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClassLookupDict(object):
|
class ClassLookupDict:
|
||||||
"""
|
"""
|
||||||
Takes a dictionary with classes as keys.
|
Takes a dictionary with classes as keys.
|
||||||
Lookups against this object will traverses the object's inheritance
|
Lookups against this object will traverses the object's inheritance
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""
|
"""
|
||||||
Utility functions to return a formatted name and description for a given view.
|
Utility functions to return a formatted name and description for a given view.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
|
|
@ -5,9 +5,6 @@ REST framework should always import this wrapper module in order to maintain
|
||||||
spec-compliant encoding/decoding. Support for non-standard features should be
|
spec-compliant encoding/decoding. Support for non-standard features should be
|
||||||
handled by users at the renderer and parser layer.
|
handled by users at the renderer and parser layer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import json # noqa
|
import json # noqa
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,7 @@ Handling of media types, as found in HTTP Content-Type and Accept headers.
|
||||||
|
|
||||||
See https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
See https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.http.multipartparser import parse_header
|
from django.http.multipartparser import parse_header
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
|
|
||||||
from rest_framework import HTTP_HEADER_ENCODING
|
from rest_framework import HTTP_HEADER_ENCODING
|
||||||
|
|
||||||
|
@ -46,8 +43,7 @@ def order_by_precedence(media_type_lst):
|
||||||
return [media_types for media_types in ret if media_types]
|
return [media_types for media_types in ret if media_types]
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
class _MediaType:
|
||||||
class _MediaType(object):
|
|
||||||
def __init__(self, media_type_str):
|
def __init__(self, media_type_str):
|
||||||
self.orig = '' if (media_type_str is None) else media_type_str
|
self.orig = '' if (media_type_str is None) else media_type_str
|
||||||
self.full_type, self.params = parse_header(self.orig.encode(HTTP_HEADER_ENCODING))
|
self.full_type, self.params = parse_header(self.orig.encode(HTTP_HEADER_ENCODING))
|
||||||
|
|
|
@ -2,16 +2,12 @@
|
||||||
Helper functions for creating user-friendly representations
|
Helper functions for creating user-friendly representations
|
||||||
of serializer classes and serializer fields.
|
of serializer classes and serializer fields.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.functional import Promise
|
from django.utils.functional import Promise
|
||||||
|
|
||||||
from rest_framework.compat import unicode_repr
|
|
||||||
|
|
||||||
|
|
||||||
def manager_repr(value):
|
def manager_repr(value):
|
||||||
model = value.model
|
model = value.model
|
||||||
|
@ -34,7 +30,7 @@ def smart_repr(value):
|
||||||
if isinstance(value, Promise) and value._delegate_text:
|
if isinstance(value, Promise) and value._delegate_text:
|
||||||
value = force_text(value)
|
value = force_text(value)
|
||||||
|
|
||||||
value = unicode_repr(value)
|
value = repr(value)
|
||||||
|
|
||||||
# Representations like u'help text'
|
# Representations like u'help text'
|
||||||
# should simply be presented as 'help text'
|
# should simply be presented as 'help text'
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
from rest_framework.compat import MutableMapping, unicode_to_repr
|
from rest_framework.compat import MutableMapping
|
||||||
from rest_framework.utils import json
|
from rest_framework.utils import json
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +15,7 @@ class ReturnDict(OrderedDict):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.serializer = kwargs.pop('serializer')
|
self.serializer = kwargs.pop('serializer')
|
||||||
super(ReturnDict, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return ReturnDict(self, serializer=self.serializer)
|
return ReturnDict(self, serializer=self.serializer)
|
||||||
|
@ -40,7 +38,7 @@ class ReturnList(list):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.serializer = kwargs.pop('serializer')
|
self.serializer = kwargs.pop('serializer')
|
||||||
super(ReturnList, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return list.__repr__(self)
|
return list.__repr__(self)
|
||||||
|
@ -51,7 +49,7 @@ class ReturnList(list):
|
||||||
return (list, (list(self),))
|
return (list, (list(self),))
|
||||||
|
|
||||||
|
|
||||||
class BoundField(object):
|
class BoundField:
|
||||||
"""
|
"""
|
||||||
A field object that also includes `.value` and `.error` properties.
|
A field object that also includes `.value` and `.error` properties.
|
||||||
Returned when iterating over a serializer instance,
|
Returned when iterating over a serializer instance,
|
||||||
|
@ -73,9 +71,9 @@ class BoundField(object):
|
||||||
return self._field.__class__
|
return self._field.__class__
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr('<%s value=%s errors=%s>' % (
|
return '<%s value=%s errors=%s>' % (
|
||||||
self.__class__.__name__, self.value, self.errors
|
self.__class__.__name__, self.value, self.errors
|
||||||
))
|
)
|
||||||
|
|
||||||
def as_form_field(self):
|
def as_form_field(self):
|
||||||
value = '' if (self.value is None or self.value is False) else self.value
|
value = '' if (self.value is None or self.value is False) else self.value
|
||||||
|
@ -103,9 +101,9 @@ class NestedBoundField(BoundField):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, field, value, errors, prefix=''):
|
def __init__(self, field, value, errors, prefix=''):
|
||||||
if value is None or value is '':
|
if value is None or value == '':
|
||||||
value = {}
|
value = {}
|
||||||
super(NestedBoundField, self).__init__(field, value, errors, prefix)
|
super().__init__(field, value, errors, prefix)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for field in self.fields.values():
|
for field in self.fields.values():
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
|
||||||
|
|
||||||
|
|
||||||
def replace_query_param(url, key, val):
|
def replace_query_param(url, key, val):
|
||||||
|
@ -7,11 +8,11 @@ def replace_query_param(url, key, val):
|
||||||
Given a URL and a key/val pair, set or replace an item in the query
|
Given a URL and a key/val pair, set or replace an item in the query
|
||||||
parameters of the URL, and return the new URL.
|
parameters of the URL, and return the new URL.
|
||||||
"""
|
"""
|
||||||
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(force_str(url))
|
(scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
|
||||||
query_dict = urlparse.parse_qs(query, keep_blank_values=True)
|
query_dict = parse.parse_qs(query, keep_blank_values=True)
|
||||||
query_dict[force_str(key)] = [force_str(val)]
|
query_dict[force_str(key)] = [force_str(val)]
|
||||||
query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True)
|
query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
|
||||||
return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
|
return parse.urlunsplit((scheme, netloc, path, query, fragment))
|
||||||
|
|
||||||
|
|
||||||
def remove_query_param(url, key):
|
def remove_query_param(url, key):
|
||||||
|
@ -19,8 +20,8 @@ def remove_query_param(url, key):
|
||||||
Given a URL and a key/val pair, remove an item in the query
|
Given a URL and a key/val pair, remove an item in the query
|
||||||
parameters of the URL, and return the new URL.
|
parameters of the URL, and return the new URL.
|
||||||
"""
|
"""
|
||||||
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(force_str(url))
|
(scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
|
||||||
query_dict = urlparse.parse_qs(query, keep_blank_values=True)
|
query_dict = parse.parse_qs(query, keep_blank_values=True)
|
||||||
query_dict.pop(key, None)
|
query_dict.pop(key, None)
|
||||||
query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True)
|
query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
|
||||||
return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
|
return parse.urlunsplit((scheme, netloc, path, query, fragment))
|
||||||
|
|
|
@ -6,12 +6,9 @@ This gives us better separation of concerns, allows us to use single-step
|
||||||
object creation, and makes it possible to switch between using the implicit
|
object creation, and makes it possible to switch between using the implicit
|
||||||
`ModelSerializer` class and an equivalent explicit `Serializer` class.
|
`ModelSerializer` class and an equivalent explicit `Serializer` class.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import DataError
|
from django.db import DataError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.compat import unicode_to_repr
|
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.utils.representation import smart_repr
|
from rest_framework.utils.representation import smart_repr
|
||||||
|
|
||||||
|
@ -33,7 +30,7 @@ def qs_filter(queryset, **kwargs):
|
||||||
return queryset.none()
|
return queryset.none()
|
||||||
|
|
||||||
|
|
||||||
class UniqueValidator(object):
|
class UniqueValidator:
|
||||||
"""
|
"""
|
||||||
Validator that corresponds to `unique=True` on a model field.
|
Validator that corresponds to `unique=True` on a model field.
|
||||||
|
|
||||||
|
@ -82,13 +79,13 @@ class UniqueValidator(object):
|
||||||
raise ValidationError(self.message, code='unique')
|
raise ValidationError(self.message, code='unique')
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr('<%s(queryset=%s)>' % (
|
return '<%s(queryset=%s)>' % (
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
smart_repr(self.queryset)
|
smart_repr(self.queryset)
|
||||||
))
|
)
|
||||||
|
|
||||||
|
|
||||||
class UniqueTogetherValidator(object):
|
class UniqueTogetherValidator:
|
||||||
"""
|
"""
|
||||||
Validator that corresponds to `unique_together = (...)` on a model class.
|
Validator that corresponds to `unique_together = (...)` on a model class.
|
||||||
|
|
||||||
|
@ -170,14 +167,14 @@ class UniqueTogetherValidator(object):
|
||||||
raise ValidationError(message, code='unique')
|
raise ValidationError(message, code='unique')
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr('<%s(queryset=%s, fields=%s)>' % (
|
return '<%s(queryset=%s, fields=%s)>' % (
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
smart_repr(self.queryset),
|
smart_repr(self.queryset),
|
||||||
smart_repr(self.fields)
|
smart_repr(self.fields)
|
||||||
))
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseUniqueForValidator(object):
|
class BaseUniqueForValidator:
|
||||||
message = None
|
message = None
|
||||||
missing_message = _('This field is required.')
|
missing_message = _('This field is required.')
|
||||||
|
|
||||||
|
@ -236,12 +233,12 @@ class BaseUniqueForValidator(object):
|
||||||
}, code='unique')
|
}, code='unique')
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_to_repr('<%s(queryset=%s, field=%s, date_field=%s)>' % (
|
return '<%s(queryset=%s, field=%s, date_field=%s)>' % (
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
smart_repr(self.queryset),
|
smart_repr(self.queryset),
|
||||||
smart_repr(self.field),
|
smart_repr(self.field),
|
||||||
smart_repr(self.date_field)
|
smart_repr(self.date_field)
|
||||||
))
|
)
|
||||||
|
|
||||||
|
|
||||||
class UniqueForDateValidator(BaseUniqueForValidator):
|
class UniqueForDateValidator(BaseUniqueForValidator):
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# coding: utf-8
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -13,7 +10,7 @@ from rest_framework.templatetags.rest_framework import replace_query_param
|
||||||
from rest_framework.utils.mediatypes import _MediaType
|
from rest_framework.utils.mediatypes import _MediaType
|
||||||
|
|
||||||
|
|
||||||
class BaseVersioning(object):
|
class BaseVersioning:
|
||||||
default_version = api_settings.DEFAULT_VERSION
|
default_version = api_settings.DEFAULT_VERSION
|
||||||
allowed_versions = api_settings.ALLOWED_VERSIONS
|
allowed_versions = api_settings.ALLOWED_VERSIONS
|
||||||
version_param = api_settings.VERSION_PARAM
|
version_param = api_settings.VERSION_PARAM
|
||||||
|
@ -87,7 +84,7 @@ class URLPathVersioning(BaseVersioning):
|
||||||
kwargs = {} if (kwargs is None) else kwargs
|
kwargs = {} if (kwargs is None) else kwargs
|
||||||
kwargs[self.version_param] = request.version
|
kwargs[self.version_param] = request.version
|
||||||
|
|
||||||
return super(URLPathVersioning, self).reverse(
|
return super().reverse(
|
||||||
viewname, args, kwargs, request, format, **extra
|
viewname, args, kwargs, request, format, **extra
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -133,7 +130,7 @@ class NamespaceVersioning(BaseVersioning):
|
||||||
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
|
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
|
||||||
if request.version is not None:
|
if request.version is not None:
|
||||||
viewname = self.get_versioned_viewname(viewname, request)
|
viewname = self.get_versioned_viewname(viewname, request)
|
||||||
return super(NamespaceVersioning, self).reverse(
|
return super().reverse(
|
||||||
viewname, args, kwargs, request, format, **extra
|
viewname, args, kwargs, request, format, **extra
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -179,7 +176,7 @@ class QueryParameterVersioning(BaseVersioning):
|
||||||
return version
|
return version
|
||||||
|
|
||||||
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
|
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
|
||||||
url = super(QueryParameterVersioning, self).reverse(
|
url = super().reverse(
|
||||||
viewname, args, kwargs, request, format, **extra
|
viewname, args, kwargs, request, format, **extra
|
||||||
)
|
)
|
||||||
if request.version is not None:
|
if request.version is not None:
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""
|
"""
|
||||||
Provides an APIView class that is the base of all views in REST framework.
|
Provides an APIView class that is the base of all views in REST framework.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db import connection, models, transaction
|
from django.db import connection, models, transaction
|
||||||
|
@ -137,7 +135,7 @@ class APIView(View):
|
||||||
)
|
)
|
||||||
cls.queryset._fetch_all = force_evaluation
|
cls.queryset._fetch_all = force_evaluation
|
||||||
|
|
||||||
view = super(APIView, cls).as_view(**initkwargs)
|
view = super().as_view(**initkwargs)
|
||||||
view.cls = cls
|
view.cls = cls
|
||||||
view.initkwargs = initkwargs
|
view.initkwargs = initkwargs
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@ automatically.
|
||||||
router.register(r'users', UserViewSet, 'user')
|
router.register(r'users', UserViewSet, 'user')
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
from inspect import getmembers
|
from inspect import getmembers
|
||||||
|
@ -34,7 +32,7 @@ def _is_extra_action(attr):
|
||||||
return hasattr(attr, 'mapping')
|
return hasattr(attr, 'mapping')
|
||||||
|
|
||||||
|
|
||||||
class ViewSetMixin(object):
|
class ViewSetMixin:
|
||||||
"""
|
"""
|
||||||
This is the magic.
|
This is the magic.
|
||||||
|
|
||||||
|
@ -134,7 +132,7 @@ class ViewSetMixin(object):
|
||||||
"""
|
"""
|
||||||
Set the `.action` attribute on the view, depending on the request method.
|
Set the `.action` attribute on the view, depending on the request method.
|
||||||
"""
|
"""
|
||||||
request = super(ViewSetMixin, self).initialize_request(request, *args, **kwargs)
|
request = super().initialize_request(request, *args, **kwargs)
|
||||||
method = request.method.lower()
|
method = request.method.lower()
|
||||||
if method == 'options':
|
if method == 'options':
|
||||||
# This is a special case as we always provide handling for the
|
# This is a special case as we always provide handling for the
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#! /usr/bin/env python
|
#! /usr/bin/env python3
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
[bdist_wheel]
|
|
||||||
universal = 1
|
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
license_file = LICENSE.md
|
license_file = LICENSE.md
|
||||||
|
|
||||||
|
|
36
setup.py
36
setup.py
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -8,6 +7,34 @@ from io import open
|
||||||
|
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
CURRENT_PYTHON = sys.version_info[:2]
|
||||||
|
REQUIRED_PYTHON = (3, 4)
|
||||||
|
|
||||||
|
# This check and everything above must remain compatible with Python 2.7.
|
||||||
|
if CURRENT_PYTHON < REQUIRED_PYTHON:
|
||||||
|
sys.stderr.write("""
|
||||||
|
==========================
|
||||||
|
Unsupported Python version
|
||||||
|
==========================
|
||||||
|
|
||||||
|
This version of Django REST Framework requires Python {}.{}, but you're trying
|
||||||
|
to install it on Python {}.{}.
|
||||||
|
|
||||||
|
This may be because you are using a version of pip that doesn't
|
||||||
|
understand the python_requires classifier. Make sure you
|
||||||
|
have pip >= 9.0 and setuptools >= 24.2, then try again:
|
||||||
|
|
||||||
|
$ python -m pip install --upgrade pip setuptools
|
||||||
|
$ python -m pip install djangorestframework
|
||||||
|
|
||||||
|
This will install the latest version of Django REST Framework which works on
|
||||||
|
your version of Python. If you can't upgrade your pip (or Python), request
|
||||||
|
an older version of Django REST Framework:
|
||||||
|
|
||||||
|
$ python -m pip install "django<3.10"
|
||||||
|
""".format(*(REQUIRED_PYTHON + CURRENT_PYTHON)))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def read(f):
|
def read(f):
|
||||||
return open(f, 'r', encoding='utf-8').read()
|
return open(f, 'r', encoding='utf-8').read()
|
||||||
|
@ -52,7 +79,7 @@ setup(
|
||||||
packages=find_packages(exclude=['tests*']),
|
packages=find_packages(exclude=['tests*']),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=[],
|
install_requires=[],
|
||||||
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
|
python_requires=">=3.4",
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 5 - Production/Stable',
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
@ -66,13 +93,12 @@ setup(
|
||||||
'License :: OSI Approved :: BSD License',
|
'License :: OSI Approved :: BSD License',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 2',
|
|
||||||
'Programming Language :: Python :: 2.7',
|
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
'Programming Language :: Python :: 3.4',
|
'Programming Language :: Python :: 3.4',
|
||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
|
'Programming Language :: Python :: 3 :: Only',
|
||||||
'Topic :: Internet :: WWW/HTTP',
|
'Topic :: Internet :: WWW/HTTP',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# coding: utf-8
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -10,7 +6,6 @@ from django.conf.urls import include, url
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import (
|
from rest_framework import (
|
||||||
HTTP_HEADER_ENCODING, exceptions, permissions, renderers, status
|
HTTP_HEADER_ENCODING, exceptions, permissions, renderers, status
|
||||||
|
@ -253,7 +248,7 @@ class SessionAuthTests(TestCase):
|
||||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||||
|
|
||||||
|
|
||||||
class BaseTokenAuthTests(object):
|
class BaseTokenAuthTests:
|
||||||
"""Token authentication"""
|
"""Token authentication"""
|
||||||
model = None
|
model = None
|
||||||
path = None
|
path = None
|
||||||
|
@ -381,7 +376,7 @@ class TokenAuthTests(BaseTokenAuthTests, TestCase):
|
||||||
"""Ensure generate_key returns a string"""
|
"""Ensure generate_key returns a string"""
|
||||||
token = self.model()
|
token = self.model()
|
||||||
key = token.generate_key()
|
key = token.generate_key()
|
||||||
assert isinstance(key, six.string_types)
|
assert isinstance(key, str)
|
||||||
|
|
||||||
def test_token_login_json(self):
|
def test_token_login_json(self):
|
||||||
"""Ensure token login view using JSON POST works."""
|
"""Ensure token login view using JSON POST works."""
|
||||||
|
@ -534,7 +529,7 @@ class BasicAuthenticationUnitTests(TestCase):
|
||||||
def test_basic_authentication_raises_error_if_user_not_active(self):
|
def test_basic_authentication_raises_error_if_user_not_active(self):
|
||||||
from rest_framework import authentication
|
from rest_framework import authentication
|
||||||
|
|
||||||
class MockUser(object):
|
class MockUser:
|
||||||
is_active = False
|
is_active = False
|
||||||
old_authenticate = authentication.authenticate
|
old_authenticate = authentication.authenticate
|
||||||
authentication.authenticate = lambda **kwargs: MockUser()
|
authentication.authenticate = lambda **kwargs: MockUser()
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
|
|
||||||
from .views import MockView
|
from .views import MockView
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from .views import MockView
|
from .views import MockView
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from rest_framework import authentication, renderers
|
from rest_framework import authentication, renderers
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.contrib.contenttypes.fields import (
|
from django.contrib.contenttypes.fields import (
|
||||||
GenericForeignKey, GenericRelation
|
GenericForeignKey, GenericRelation
|
||||||
)
|
)
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
"""
|
"""
|
||||||
Tags have a descriptive slug, and are attached to an arbitrary object.
|
Tags have a descriptive slug, and are attached to an arbitrary object.
|
||||||
|
@ -22,7 +18,6 @@ class Tag(models.Model):
|
||||||
return self.tag
|
return self.tag
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Bookmark(models.Model):
|
class Bookmark(models.Model):
|
||||||
"""
|
"""
|
||||||
A URL bookmark that may have multiple tags attached.
|
A URL bookmark that may have multiple tags attached.
|
||||||
|
@ -34,7 +29,6 @@ class Bookmark(models.Model):
|
||||||
return 'Bookmark: %s' % self.url
|
return 'Bookmark: %s' % self.url
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Note(models.Model):
|
class Note(models.Model):
|
||||||
"""
|
"""
|
||||||
A textual note that may have multiple tags attached.
|
A textual note that may have multiple tags attached.
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
@ -38,7 +36,7 @@ class APIExceptionView(APIView):
|
||||||
class NonAtomicAPIExceptionView(APIView):
|
class NonAtomicAPIExceptionView(APIView):
|
||||||
@transaction.non_atomic_requests
|
@transaction.non_atomic_requests
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super(NonAtomicAPIExceptionView, self).dispatch(*args, **kwargs)
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
BasicModel.objects.all()
|
BasicModel.objects.all()
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.contrib.admin import site
|
from django.contrib.admin import site
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.management import CommandError, call_command
|
from django.core.management import CommandError, call_command
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.six import StringIO
|
|
||||||
|
|
||||||
from rest_framework.authtoken.admin import TokenAdmin
|
from rest_framework.authtoken.admin import TokenAdmin
|
||||||
from rest_framework.authtoken.management.commands.drf_create_token import \
|
from rest_framework.authtoken.management.commands.drf_create_token import \
|
||||||
|
|
|
@ -28,7 +28,7 @@ class TestSimpleBoundField:
|
||||||
assert serializer['text'].value == 'abc'
|
assert serializer['text'].value == 'abc'
|
||||||
assert serializer['text'].errors is None
|
assert serializer['text'].errors is None
|
||||||
assert serializer['text'].name == 'text'
|
assert serializer['text'].name == 'text'
|
||||||
assert serializer['amount'].value is 123
|
assert serializer['amount'].value == 123
|
||||||
assert serializer['amount'].errors is None
|
assert serializer['amount'].errors is None
|
||||||
assert serializer['amount'].name == 'amount'
|
assert serializer['amount'].name == 'amount'
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class TestSimpleBoundField:
|
||||||
assert serializer['text'].value == 'x' * 1000
|
assert serializer['text'].value == 'x' * 1000
|
||||||
assert serializer['text'].errors == ['Ensure this field has no more than 100 characters.']
|
assert serializer['text'].errors == ['Ensure this field has no more than 100 characters.']
|
||||||
assert serializer['text'].name == 'text'
|
assert serializer['text'].name == 'text'
|
||||||
assert serializer['amount'].value is 123
|
assert serializer['amount'].value == 123
|
||||||
assert serializer['amount'].errors is None
|
assert serializer['amount'].errors is None
|
||||||
assert serializer['amount'].name == 'amount'
|
assert serializer['amount'].name == 'amount'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
# -- coding: utf-8 --
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
|
|
||||||
from rest_framework.compat import apply_markdown
|
from rest_framework.compat import apply_markdown
|
||||||
from rest_framework.utils.formatting import dedent
|
from rest_framework.utils.formatting import dedent
|
||||||
|
@ -157,8 +152,8 @@ class TestViewNamesAndDescriptions(TestCase):
|
||||||
"""
|
"""
|
||||||
# use a mock object instead of gettext_lazy to ensure that we can't end
|
# use a mock object instead of gettext_lazy to ensure that we can't end
|
||||||
# up with a test case string in our l10n catalog
|
# up with a test case string in our l10n catalog
|
||||||
@python_2_unicode_compatible
|
|
||||||
class MockLazyStr(object):
|
class MockLazyStr:
|
||||||
def __init__(self, string):
|
def __init__(self, string):
|
||||||
self.s = string
|
self.s = string
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from rest_framework.compat import coreapi
|
||||||
from rest_framework.utils.encoders import JSONEncoder
|
from rest_framework.utils.encoders import JSONEncoder
|
||||||
|
|
||||||
|
|
||||||
class MockList(object):
|
class MockList:
|
||||||
def tolist(self):
|
def tolist(self):
|
||||||
return [1, 2, 3]
|
return [1, 2, 3]
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.test import RequestFactory, TestCase
|
from django.test import RequestFactory, TestCase
|
||||||
from django.utils import six, translation
|
from django.utils import translation
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.exceptions import (
|
from rest_framework.exceptions import (
|
||||||
|
@ -46,12 +43,12 @@ class ExceptionTestCase(TestCase):
|
||||||
|
|
||||||
exception = Throttled(wait=2)
|
exception = Throttled(wait=2)
|
||||||
assert exception.get_full_details() == {
|
assert exception.get_full_details() == {
|
||||||
'message': 'Request was throttled. Expected available in {} seconds.'.format(2 if six.PY3 else 2.),
|
'message': 'Request was throttled. Expected available in {} seconds.'.format(2),
|
||||||
'code': 'throttled'}
|
'code': 'throttled'}
|
||||||
|
|
||||||
exception = Throttled(wait=2, detail='Slow down!')
|
exception = Throttled(wait=2, detail='Slow down!')
|
||||||
assert exception.get_full_details() == {
|
assert exception.get_full_details() == {
|
||||||
'message': 'Slow down! Expected available in {} seconds.'.format(2 if six.PY3 else 2.),
|
'message': 'Slow down! Expected available in {} seconds.'.format(2),
|
||||||
'code': 'throttled'}
|
'code': 'throttled'}
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,7 +89,7 @@ class TranslationTests(TestCase):
|
||||||
def test_message(self):
|
def test_message(self):
|
||||||
# this test largely acts as a sanity test to ensure the translation files are present.
|
# this test largely acts as a sanity test to ensure the translation files are present.
|
||||||
self.assertEqual(_('A server error occurred.'), 'Une erreur du serveur est survenue.')
|
self.assertEqual(_('A server error occurred.'), 'Une erreur du serveur est survenue.')
|
||||||
self.assertEqual(six.text_type(APIException()), 'Une erreur du serveur est survenue.')
|
self.assertEqual(str(APIException()), 'Une erreur du serveur est survenue.')
|
||||||
|
|
||||||
|
|
||||||
def test_server_error():
|
def test_server_error():
|
||||||
|
|
|
@ -10,7 +10,6 @@ import pytz
|
||||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
|
||||||
from django.utils.timezone import activate, deactivate, override, utc
|
from django.utils.timezone import activate, deactivate, override, utc
|
||||||
|
|
||||||
import rest_framework
|
import rest_framework
|
||||||
|
@ -167,7 +166,7 @@ class TestEmpty:
|
||||||
"""
|
"""
|
||||||
field = serializers.IntegerField(default=123)
|
field = serializers.IntegerField(default=123)
|
||||||
output = field.run_validation()
|
output = field.run_validation()
|
||||||
assert output is 123
|
assert output == 123
|
||||||
|
|
||||||
|
|
||||||
class TestSource:
|
class TestSource:
|
||||||
|
@ -193,7 +192,7 @@ class TestSource:
|
||||||
class ExampleSerializer(serializers.Serializer):
|
class ExampleSerializer(serializers.Serializer):
|
||||||
example_field = serializers.CharField(source='example_callable')
|
example_field = serializers.CharField(source='example_callable')
|
||||||
|
|
||||||
class ExampleInstance(object):
|
class ExampleInstance:
|
||||||
def example_callable(self):
|
def example_callable(self):
|
||||||
return 'example callable value'
|
return 'example callable value'
|
||||||
|
|
||||||
|
@ -204,7 +203,7 @@ class TestSource:
|
||||||
class ExampleSerializer(serializers.Serializer):
|
class ExampleSerializer(serializers.Serializer):
|
||||||
example_field = serializers.CharField(source='example_callable', read_only=True)
|
example_field = serializers.CharField(source='example_callable', read_only=True)
|
||||||
|
|
||||||
class ExampleInstance(object):
|
class ExampleInstance:
|
||||||
def example_callable(self):
|
def example_callable(self):
|
||||||
raise AttributeError('method call failed')
|
raise AttributeError('method call failed')
|
||||||
|
|
||||||
|
@ -754,7 +753,7 @@ class TestCharField(FieldValues):
|
||||||
def raise_exception(value):
|
def raise_exception(value):
|
||||||
raise exceptions.ValidationError('Raised error')
|
raise exceptions.ValidationError('Raised error')
|
||||||
|
|
||||||
for validators in ([raise_exception], (raise_exception,), set([raise_exception])):
|
for validators in ([raise_exception], (raise_exception,), {raise_exception}):
|
||||||
field = serializers.CharField(validators=validators)
|
field = serializers.CharField(validators=validators)
|
||||||
with pytest.raises(serializers.ValidationError) as exc_info:
|
with pytest.raises(serializers.ValidationError) as exc_info:
|
||||||
field.run_validation(value)
|
field.run_validation(value)
|
||||||
|
@ -822,7 +821,7 @@ class TestSlugField(FieldValues):
|
||||||
|
|
||||||
validation_error = False
|
validation_error = False
|
||||||
try:
|
try:
|
||||||
field.run_validation(u'slug-99-\u0420')
|
field.run_validation('slug-99-\u0420')
|
||||||
except serializers.ValidationError:
|
except serializers.ValidationError:
|
||||||
validation_error = True
|
validation_error = True
|
||||||
|
|
||||||
|
@ -1148,7 +1147,7 @@ class TestLocalizedDecimalField(TestCase):
|
||||||
|
|
||||||
def test_localize_forces_coerce_to_string(self):
|
def test_localize_forces_coerce_to_string(self):
|
||||||
field = serializers.DecimalField(max_digits=2, decimal_places=1, coerce_to_string=False, localize=True)
|
field = serializers.DecimalField(max_digits=2, decimal_places=1, coerce_to_string=False, localize=True)
|
||||||
assert isinstance(field.to_representation(Decimal('1.1')), six.string_types)
|
assert isinstance(field.to_representation(Decimal('1.1')), str)
|
||||||
|
|
||||||
|
|
||||||
class TestQuantizedValueForDecimal(TestCase):
|
class TestQuantizedValueForDecimal(TestCase):
|
||||||
|
@ -1219,7 +1218,7 @@ class TestDateField(FieldValues):
|
||||||
outputs = {
|
outputs = {
|
||||||
datetime.date(2001, 1, 1): '2001-01-01',
|
datetime.date(2001, 1, 1): '2001-01-01',
|
||||||
'2001-01-01': '2001-01-01',
|
'2001-01-01': '2001-01-01',
|
||||||
six.text_type('2016-01-10'): '2016-01-10',
|
str('2016-01-10'): '2016-01-10',
|
||||||
None: None,
|
None: None,
|
||||||
'': None,
|
'': None,
|
||||||
}
|
}
|
||||||
|
@ -1286,7 +1285,7 @@ class TestDateTimeField(FieldValues):
|
||||||
datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00Z',
|
datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00Z',
|
||||||
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=utc): '2001-01-01T13:00:00Z',
|
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=utc): '2001-01-01T13:00:00Z',
|
||||||
'2001-01-01T00:00:00': '2001-01-01T00:00:00',
|
'2001-01-01T00:00:00': '2001-01-01T00:00:00',
|
||||||
six.text_type('2016-01-10T00:00:00'): '2016-01-10T00:00:00',
|
str('2016-01-10T00:00:00'): '2016-01-10T00:00:00',
|
||||||
None: None,
|
None: None,
|
||||||
'': None,
|
'': None,
|
||||||
}
|
}
|
||||||
|
@ -1628,7 +1627,7 @@ class TestChoiceField(FieldValues):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
field.choices = [1]
|
field.choices = [1]
|
||||||
assert field.run_validation(1) is 1
|
assert field.run_validation(1) == 1
|
||||||
with pytest.raises(serializers.ValidationError) as exc_info:
|
with pytest.raises(serializers.ValidationError) as exc_info:
|
||||||
field.run_validation(2)
|
field.run_validation(2)
|
||||||
assert exc_info.value.detail == ['"2" is not a valid choice.']
|
assert exc_info.value.detail == ['"2" is not a valid choice.']
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
from importlib import reload as reload_module
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
@ -8,7 +7,6 @@ from django.db import models
|
||||||
from django.db.models.functions import Concat, Upper
|
from django.db.models.functions import Concat, Upper
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils.six.moves import reload_module
|
|
||||||
|
|
||||||
from rest_framework import filters, generics, serializers
|
from rest_framework import filters, generics, serializers
|
||||||
from rest_framework.compat import coreschema
|
from rest_framework.compat import coreschema
|
||||||
|
@ -163,7 +161,7 @@ class SearchFilterTests(TestCase):
|
||||||
def get_search_fields(self, view, request):
|
def get_search_fields(self, view, request):
|
||||||
if request.query_params.get('title_only'):
|
if request.query_params.get('title_only'):
|
||||||
return ('$title',)
|
return ('$title',)
|
||||||
return super(CustomSearchFilter, self).get_search_fields(view, request)
|
return super().get_search_fields(view, request)
|
||||||
|
|
||||||
class SearchListView(generics.ListAPIView):
|
class SearchListView(generics.ListAPIView):
|
||||||
queryset = SearchFilterModel.objects.all()
|
queryset = SearchFilterModel.objects.all()
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
from __future__ import unicode_literals
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework.compat import coreapi
|
from rest_framework.compat import coreapi
|
||||||
from rest_framework.utils import formatting, json
|
from rest_framework.utils import formatting, json
|
||||||
|
@ -28,9 +27,8 @@ class GenerateSchemaTests(TestCase):
|
||||||
"""Tests for management command generateschema."""
|
"""Tests for management command generateschema."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.out = six.StringIO()
|
self.out = io.StringIO()
|
||||||
|
|
||||||
@pytest.mark.skipif(six.PY2, reason='PyYAML unicode output is malformed on PY2.')
|
|
||||||
def test_renders_default_schema_with_custom_title_url_and_description(self):
|
def test_renders_default_schema_with_custom_title_url_and_description(self):
|
||||||
expected_out = """info:
|
expected_out = """info:
|
||||||
description: Sample description
|
description: Sample description
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import generics, renderers, serializers, status
|
from rest_framework import generics, renderers, serializers, status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
@ -245,7 +242,7 @@ class TestInstanceView(TestCase):
|
||||||
with self.assertNumQueries(2):
|
with self.assertNumQueries(2):
|
||||||
response = self.view(request, pk=1).render()
|
response = self.view(request, pk=1).render()
|
||||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||||
assert response.content == six.b('')
|
assert response.content == b''
|
||||||
ids = [obj.id for obj in self.objects.all()]
|
ids = [obj.id for obj in self.objects.all()]
|
||||||
assert ids == [2, 3]
|
assert ids == [2, 3]
|
||||||
|
|
||||||
|
@ -291,7 +288,7 @@ class TestInstanceView(TestCase):
|
||||||
"""
|
"""
|
||||||
data = {'text': 'foo'}
|
data = {'text': 'foo'}
|
||||||
filtered_out_pk = BasicModel.objects.filter(text='filtered out')[0].pk
|
filtered_out_pk = BasicModel.objects.filter(text='filtered out')[0].pk
|
||||||
request = factory.put('/{0}'.format(filtered_out_pk), data, format='json')
|
request = factory.put('/{}'.format(filtered_out_pk), data, format='json')
|
||||||
response = self.view(request, pk=filtered_out_pk).render()
|
response = self.view(request, pk=filtered_out_pk).render()
|
||||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||||
|
|
||||||
|
@ -446,12 +443,12 @@ class TestM2MBrowsableAPI(TestCase):
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
||||||
|
|
||||||
class InclusiveFilterBackend(object):
|
class InclusiveFilterBackend:
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
return queryset.filter(text='foo')
|
return queryset.filter(text='foo')
|
||||||
|
|
||||||
|
|
||||||
class ExclusiveFilterBackend(object):
|
class ExclusiveFilterBackend:
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
return queryset.filter(text='other')
|
return queryset.filter(text='other')
|
||||||
|
|
||||||
|
@ -653,7 +650,7 @@ class ApiViewsTests(TestCase):
|
||||||
|
|
||||||
class GetObjectOr404Tests(TestCase):
|
class GetObjectOr404Tests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(GetObjectOr404Tests, self).setUp()
|
super().setUp()
|
||||||
self.uuid_object = UUIDForeignKeyTarget.objects.create(name='bar')
|
self.uuid_object = UUIDForeignKeyTarget.objects.create(name='bar')
|
||||||
|
|
||||||
def test_get_object_or_404_with_valid_uuid(self):
|
def test_get_object_or_404_with_valid_uuid(self):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import django.template.loader
|
import django.template.loader
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
@ -7,7 +5,6 @@ from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.template import TemplateDoesNotExist, engines
|
from django.template import TemplateDoesNotExist, engines
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.decorators import api_view, renderer_classes
|
from rest_framework.decorators import api_view, renderer_classes
|
||||||
|
@ -47,7 +44,7 @@ urlpatterns = [
|
||||||
@override_settings(ROOT_URLCONF='tests.test_htmlrenderer')
|
@override_settings(ROOT_URLCONF='tests.test_htmlrenderer')
|
||||||
class TemplateHTMLRendererTests(TestCase):
|
class TemplateHTMLRendererTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class MockResponse(object):
|
class MockResponse:
|
||||||
template_name = None
|
template_name = None
|
||||||
self.mock_response = MockResponse()
|
self.mock_response = MockResponse()
|
||||||
self._monkey_patch_get_template()
|
self._monkey_patch_get_template()
|
||||||
|
@ -85,13 +82,13 @@ class TemplateHTMLRendererTests(TestCase):
|
||||||
def test_not_found_html_view(self):
|
def test_not_found_html_view(self):
|
||||||
response = self.client.get('/not_found')
|
response = self.client.get('/not_found')
|
||||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||||
self.assertEqual(response.content, six.b("404 Not Found"))
|
self.assertEqual(response.content, b"404 Not Found")
|
||||||
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
||||||
|
|
||||||
def test_permission_denied_html_view(self):
|
def test_permission_denied_html_view(self):
|
||||||
response = self.client.get('/permission_denied')
|
response = self.client.get('/permission_denied')
|
||||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertEqual(response.content, six.b("403 Forbidden"))
|
self.assertEqual(response.content, b"403 Forbidden")
|
||||||
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
||||||
|
|
||||||
# 2 tests below are based on order of if statements in corresponding method
|
# 2 tests below are based on order of if statements in corresponding method
|
||||||
|
@ -105,14 +102,14 @@ class TemplateHTMLRendererTests(TestCase):
|
||||||
def test_get_template_names_returns_view_template_name(self):
|
def test_get_template_names_returns_view_template_name(self):
|
||||||
renderer = TemplateHTMLRenderer()
|
renderer = TemplateHTMLRenderer()
|
||||||
|
|
||||||
class MockResponse(object):
|
class MockResponse:
|
||||||
template_name = None
|
template_name = None
|
||||||
|
|
||||||
class MockView(object):
|
class MockView:
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
return ['template from get_template_names method']
|
return ['template from get_template_names method']
|
||||||
|
|
||||||
class MockView2(object):
|
class MockView2:
|
||||||
template_name = 'template from template_name attribute'
|
template_name = 'template from template_name attribute'
|
||||||
|
|
||||||
template_name = renderer.get_template_names(self.mock_response,
|
template_name = renderer.get_template_names(self.mock_response,
|
||||||
|
@ -156,12 +153,11 @@ class TemplateHTMLRendererExceptionTests(TestCase):
|
||||||
response = self.client.get('/not_found')
|
response = self.client.get('/not_found')
|
||||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||||
self.assertTrue(response.content in (
|
self.assertTrue(response.content in (
|
||||||
six.b("404: Not found"), six.b("404 Not Found")))
|
b"404: Not found", b"404 Not Found"))
|
||||||
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
||||||
|
|
||||||
def test_permission_denied_html_view_with_template(self):
|
def test_permission_denied_html_view_with_template(self):
|
||||||
response = self.client.get('/permission_denied')
|
response = self.client.get('/permission_denied')
|
||||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertTrue(response.content in (
|
self.assertTrue(response.content in (b"403: Permission denied", b"403 Forbidden"))
|
||||||
six.b("403: Permission denied"), six.b("403 Forbidden")))
|
|
||||||
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
|
@ -22,7 +22,7 @@ urlpatterns = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class RequestUserMiddleware(object):
|
class RequestUserMiddleware:
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class RequestUserMiddleware(object):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class RequestPOSTMiddleware(object):
|
class RequestPOSTMiddleware:
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,6 @@ shortcuts for automatically creating serializers based on a given model class.
|
||||||
These tests deal with ensuring that we correctly map the model fields onto
|
These tests deal with ensuring that we correctly map the model fields onto
|
||||||
an appropriate set of serializer fields for each case.
|
an appropriate set of serializer fields for each case.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
import sys
|
import sys
|
||||||
|
@ -20,10 +18,9 @@ from django.core.validators import (
|
||||||
)
|
)
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.compat import postgres_fields, unicode_repr
|
from rest_framework.compat import postgres_fields
|
||||||
|
|
||||||
from .models import NestedForeignKeySource
|
from .models import NestedForeignKeySource
|
||||||
|
|
||||||
|
@ -193,7 +190,7 @@ class TestRegularFieldMappings(TestCase):
|
||||||
file_path_field = FilePathField(path='/tmp/')
|
file_path_field = FilePathField(path='/tmp/')
|
||||||
""")
|
""")
|
||||||
|
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_field_options(self):
|
def test_field_options(self):
|
||||||
class TestSerializer(serializers.ModelSerializer):
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
@ -212,14 +209,7 @@ class TestRegularFieldMappings(TestCase):
|
||||||
descriptive_field = IntegerField(help_text='Some help text', label='A label')
|
descriptive_field = IntegerField(help_text='Some help text', label='A label')
|
||||||
choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')))
|
choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')))
|
||||||
""")
|
""")
|
||||||
if six.PY2:
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
# This particular case is too awkward to resolve fully across
|
|
||||||
# both py2 and py3.
|
|
||||||
expected = expected.replace(
|
|
||||||
"('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')",
|
|
||||||
"(u'red', u'Red'), (u'blue', u'Blue'), (u'green', u'Green')"
|
|
||||||
)
|
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
|
||||||
|
|
||||||
# merge this into test_regular_fields / RegularFieldsModel when
|
# merge this into test_regular_fields / RegularFieldsModel when
|
||||||
# Django 2.1 is the minimum supported version
|
# Django 2.1 is the minimum supported version
|
||||||
|
@ -238,7 +228,7 @@ class TestRegularFieldMappings(TestCase):
|
||||||
field = BooleanField(allow_null=True, required=False)
|
field = BooleanField(allow_null=True, required=False)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
self.assertEqual(unicode_repr(NullableBooleanSerializer()), expected)
|
self.assertEqual(repr(NullableBooleanSerializer()), expected)
|
||||||
|
|
||||||
def test_method_field(self):
|
def test_method_field(self):
|
||||||
"""
|
"""
|
||||||
|
@ -382,7 +372,7 @@ class TestDurationFieldMapping(TestCase):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
duration_field = DurationField()
|
duration_field = DurationField()
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_duration_field_with_validators(self):
|
def test_duration_field_with_validators(self):
|
||||||
class ValidatedDurationFieldModel(models.Model):
|
class ValidatedDurationFieldModel(models.Model):
|
||||||
|
@ -407,7 +397,7 @@ class TestDurationFieldMapping(TestCase):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
duration_field = DurationField(max_value=datetime.timedelta(days=3), min_value=datetime.timedelta(days=1))
|
duration_field = DurationField(max_value=datetime.timedelta(days=3), min_value=datetime.timedelta(days=1))
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
|
||||||
class TestGenericIPAddressFieldValidation(TestCase):
|
class TestGenericIPAddressFieldValidation(TestCase):
|
||||||
|
@ -424,7 +414,7 @@ class TestGenericIPAddressFieldValidation(TestCase):
|
||||||
self.assertFalse(s.is_valid())
|
self.assertFalse(s.is_valid())
|
||||||
self.assertEqual(1, len(s.errors['address']),
|
self.assertEqual(1, len(s.errors['address']),
|
||||||
'Unexpected number of validation errors: '
|
'Unexpected number of validation errors: '
|
||||||
'{0}'.format(s.errors))
|
'{}'.format(s.errors))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif('not postgres_fields')
|
@pytest.mark.skipif('not postgres_fields')
|
||||||
|
@ -442,7 +432,7 @@ class TestPosgresFieldsMapping(TestCase):
|
||||||
TestSerializer():
|
TestSerializer():
|
||||||
hstore_field = HStoreField()
|
hstore_field = HStoreField()
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_array_field(self):
|
def test_array_field(self):
|
||||||
class ArrayFieldModel(models.Model):
|
class ArrayFieldModel(models.Model):
|
||||||
|
@ -457,7 +447,7 @@ class TestPosgresFieldsMapping(TestCase):
|
||||||
TestSerializer():
|
TestSerializer():
|
||||||
array_field = ListField(child=CharField(label='Array field', validators=[<django.core.validators.MaxLengthValidator object>]))
|
array_field = ListField(child=CharField(label='Array field', validators=[<django.core.validators.MaxLengthValidator object>]))
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_json_field(self):
|
def test_json_field(self):
|
||||||
class JSONFieldModel(models.Model):
|
class JSONFieldModel(models.Model):
|
||||||
|
@ -472,7 +462,7 @@ class TestPosgresFieldsMapping(TestCase):
|
||||||
TestSerializer():
|
TestSerializer():
|
||||||
json_field = JSONField(style={'base_template': 'textarea.html'})
|
json_field = JSONField(style={'base_template': 'textarea.html'})
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
|
||||||
# Tests for relational field mappings.
|
# Tests for relational field mappings.
|
||||||
|
@ -530,7 +520,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
many_to_many = PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=ManyToManyTargetModel.objects.all())
|
many_to_many = PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=ManyToManyTargetModel.objects.all())
|
||||||
through = PrimaryKeyRelatedField(many=True, read_only=True)
|
through = PrimaryKeyRelatedField(many=True, read_only=True)
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_nested_relations(self):
|
def test_nested_relations(self):
|
||||||
class TestSerializer(serializers.ModelSerializer):
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
@ -555,7 +545,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
name = CharField(max_length=100)
|
name = CharField(max_length=100)
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_hyperlinked_relations(self):
|
def test_hyperlinked_relations(self):
|
||||||
class TestSerializer(serializers.HyperlinkedModelSerializer):
|
class TestSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
@ -571,7 +561,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
many_to_many = HyperlinkedRelatedField(allow_empty=False, many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail')
|
many_to_many = HyperlinkedRelatedField(allow_empty=False, many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail')
|
||||||
through = HyperlinkedRelatedField(many=True, read_only=True, view_name='throughtargetmodel-detail')
|
through = HyperlinkedRelatedField(many=True, read_only=True, view_name='throughtargetmodel-detail')
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_nested_hyperlinked_relations(self):
|
def test_nested_hyperlinked_relations(self):
|
||||||
class TestSerializer(serializers.HyperlinkedModelSerializer):
|
class TestSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
@ -596,7 +586,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail')
|
url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail')
|
||||||
name = CharField(max_length=100)
|
name = CharField(max_length=100)
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_nested_hyperlinked_relations_starred_source(self):
|
def test_nested_hyperlinked_relations_starred_source(self):
|
||||||
class TestSerializer(serializers.HyperlinkedModelSerializer):
|
class TestSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
@ -627,7 +617,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
name = CharField(max_length=100)
|
name = CharField(max_length=100)
|
||||||
""")
|
""")
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_nested_unique_together_relations(self):
|
def test_nested_unique_together_relations(self):
|
||||||
class TestSerializer(serializers.HyperlinkedModelSerializer):
|
class TestSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
@ -646,14 +636,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
url = HyperlinkedIdentityField(view_name='onetoonetargetmodel-detail')
|
url = HyperlinkedIdentityField(view_name='onetoonetargetmodel-detail')
|
||||||
name = CharField(max_length=100)
|
name = CharField(max_length=100)
|
||||||
""")
|
""")
|
||||||
if six.PY2:
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
# This case is also too awkward to resolve fully across both py2
|
|
||||||
# and py3. (See above)
|
|
||||||
expected = expected.replace(
|
|
||||||
"('foreign_key', 'one_to_one')",
|
|
||||||
"(u'foreign_key', u'one_to_one')"
|
|
||||||
)
|
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
|
||||||
|
|
||||||
def test_pk_reverse_foreign_key(self):
|
def test_pk_reverse_foreign_key(self):
|
||||||
class TestSerializer(serializers.ModelSerializer):
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
@ -667,7 +650,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
name = CharField(max_length=100)
|
name = CharField(max_length=100)
|
||||||
reverse_foreign_key = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())
|
reverse_foreign_key = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_pk_reverse_one_to_one(self):
|
def test_pk_reverse_one_to_one(self):
|
||||||
class TestSerializer(serializers.ModelSerializer):
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
@ -681,7 +664,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
name = CharField(max_length=100)
|
name = CharField(max_length=100)
|
||||||
reverse_one_to_one = PrimaryKeyRelatedField(queryset=RelationalModel.objects.all())
|
reverse_one_to_one = PrimaryKeyRelatedField(queryset=RelationalModel.objects.all())
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_pk_reverse_many_to_many(self):
|
def test_pk_reverse_many_to_many(self):
|
||||||
class TestSerializer(serializers.ModelSerializer):
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
@ -695,7 +678,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
name = CharField(max_length=100)
|
name = CharField(max_length=100)
|
||||||
reverse_many_to_many = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())
|
reverse_many_to_many = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
def test_pk_reverse_through(self):
|
def test_pk_reverse_through(self):
|
||||||
class TestSerializer(serializers.ModelSerializer):
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
@ -709,7 +692,7 @@ class TestRelationalFieldMappings(TestCase):
|
||||||
name = CharField(max_length=100)
|
name = CharField(max_length=100)
|
||||||
reverse_through = PrimaryKeyRelatedField(many=True, read_only=True)
|
reverse_through = PrimaryKeyRelatedField(many=True, read_only=True)
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
|
||||||
class DisplayValueTargetModel(models.Model):
|
class DisplayValueTargetModel(models.Model):
|
||||||
|
@ -1078,9 +1061,9 @@ class TestMetaInheritance(TestCase):
|
||||||
char_field = CharField(max_length=100)
|
char_field = CharField(max_length=100)
|
||||||
non_model_field = CharField()
|
non_model_field = CharField()
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(ChildSerializer()), child_expected)
|
self.assertEqual(repr(ChildSerializer()), child_expected)
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), test_expected)
|
self.assertEqual(repr(TestSerializer()), test_expected)
|
||||||
self.assertEqual(unicode_repr(ChildSerializer()), child_expected)
|
self.assertEqual(repr(ChildSerializer()), child_expected)
|
||||||
|
|
||||||
|
|
||||||
class OneToOneTargetTestModel(models.Model):
|
class OneToOneTargetTestModel(models.Model):
|
||||||
|
@ -1149,14 +1132,14 @@ class Issue3674Test(TestCase):
|
||||||
title = CharField(max_length=64)
|
title = CharField(max_length=64)
|
||||||
children = PrimaryKeyRelatedField(many=True, queryset=TestChildModel.objects.all())
|
children = PrimaryKeyRelatedField(many=True, queryset=TestChildModel.objects.all())
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestParentModelSerializer()), parent_expected)
|
self.assertEqual(repr(TestParentModelSerializer()), parent_expected)
|
||||||
|
|
||||||
child_expected = dedent("""
|
child_expected = dedent("""
|
||||||
TestChildModelSerializer():
|
TestChildModelSerializer():
|
||||||
value = CharField(max_length=64, validators=[<UniqueValidator(queryset=TestChildModel.objects.all())>])
|
value = CharField(max_length=64, validators=[<UniqueValidator(queryset=TestChildModel.objects.all())>])
|
||||||
parent = PrimaryKeyRelatedField(queryset=TestParentModel.objects.all())
|
parent = PrimaryKeyRelatedField(queryset=TestParentModel.objects.all())
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestChildModelSerializer()), child_expected)
|
self.assertEqual(repr(TestChildModelSerializer()), child_expected)
|
||||||
|
|
||||||
def test_nonID_PK_foreignkey_model_serializer(self):
|
def test_nonID_PK_foreignkey_model_serializer(self):
|
||||||
|
|
||||||
|
@ -1248,7 +1231,7 @@ class TestFieldSource(TestCase):
|
||||||
number_field = IntegerField(source='integer_field')
|
number_field = IntegerField(source='integer_field')
|
||||||
""")
|
""")
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
|
||||||
class Issue6110TestModel(models.Model):
|
class Issue6110TestModel(models.Model):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
@ -80,7 +78,7 @@ class TestAcceptedMediaType(TestCase):
|
||||||
assert str(mediatype) == 'test/*; foo=bar'
|
assert str(mediatype) == 'test/*; foo=bar'
|
||||||
|
|
||||||
def test_raise_error_if_no_suitable_renderers_found(self):
|
def test_raise_error_if_no_suitable_renderers_found(self):
|
||||||
class MockRenderer(object):
|
class MockRenderer:
|
||||||
format = 'xml'
|
format = 'xml'
|
||||||
renderers = [MockRenderer()]
|
renderers = [MockRenderer()]
|
||||||
with pytest.raises(Http404):
|
with pytest.raises(Http404):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
# coding: utf-8
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.core.paginator import Paginator as DjangoPaginator
|
from django.core.paginator import Paginator as DjangoPaginator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import (
|
from rest_framework import (
|
||||||
exceptions, filters, generics, pagination, serializers, status
|
exceptions, filters, generics, pagination, serializers, status
|
||||||
|
@ -208,7 +204,7 @@ class TestPageNumberPagination:
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
assert self.pagination.display_page_controls
|
assert self.pagination.display_page_controls
|
||||||
assert isinstance(self.pagination.to_html(), six.text_type)
|
assert isinstance(self.pagination.to_html(), str)
|
||||||
|
|
||||||
def test_second_page(self):
|
def test_second_page(self):
|
||||||
request = Request(factory.get('/', {'page': 2}))
|
request = Request(factory.get('/', {'page': 2}))
|
||||||
|
@ -314,7 +310,7 @@ class TestPageNumberPaginationOverride:
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
assert not self.pagination.display_page_controls
|
assert not self.pagination.display_page_controls
|
||||||
assert isinstance(self.pagination.to_html(), six.text_type)
|
assert isinstance(self.pagination.to_html(), str)
|
||||||
|
|
||||||
def test_invalid_page(self):
|
def test_invalid_page(self):
|
||||||
request = Request(factory.get('/', {'page': 'invalid'}))
|
request = Request(factory.get('/', {'page': 'invalid'}))
|
||||||
|
@ -369,7 +365,7 @@ class TestLimitOffset:
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
assert self.pagination.display_page_controls
|
assert self.pagination.display_page_controls
|
||||||
assert isinstance(self.pagination.to_html(), six.text_type)
|
assert isinstance(self.pagination.to_html(), str)
|
||||||
|
|
||||||
def test_pagination_not_applied_if_limit_or_default_limit_not_set(self):
|
def test_pagination_not_applied_if_limit_or_default_limit_not_set(self):
|
||||||
class MockPagination(pagination.LimitOffsetPagination):
|
class MockPagination(pagination.LimitOffsetPagination):
|
||||||
|
@ -503,7 +499,7 @@ class TestLimitOffset:
|
||||||
content = self.get_paginated_content(queryset)
|
content = self.get_paginated_content(queryset)
|
||||||
next_limit = self.pagination.default_limit
|
next_limit = self.pagination.default_limit
|
||||||
next_offset = self.pagination.default_limit
|
next_offset = self.pagination.default_limit
|
||||||
next_url = 'http://testserver/?limit={0}&offset={1}'.format(next_limit, next_offset)
|
next_url = 'http://testserver/?limit={}&offset={}'.format(next_limit, next_offset)
|
||||||
assert queryset == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
assert queryset == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
assert content.get('next') == next_url
|
assert content.get('next') == next_url
|
||||||
|
|
||||||
|
@ -516,7 +512,7 @@ class TestLimitOffset:
|
||||||
content = self.get_paginated_content(queryset)
|
content = self.get_paginated_content(queryset)
|
||||||
next_limit = self.pagination.default_limit
|
next_limit = self.pagination.default_limit
|
||||||
next_offset = self.pagination.default_limit
|
next_offset = self.pagination.default_limit
|
||||||
next_url = 'http://testserver/?limit={0}&offset={1}'.format(next_limit, next_offset)
|
next_url = 'http://testserver/?limit={}&offset={}'.format(next_limit, next_offset)
|
||||||
assert queryset == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
assert queryset == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
assert content.get('next') == next_url
|
assert content.get('next') == next_url
|
||||||
|
|
||||||
|
@ -532,9 +528,9 @@ class TestLimitOffset:
|
||||||
max_limit = self.pagination.max_limit
|
max_limit = self.pagination.max_limit
|
||||||
next_offset = offset + max_limit
|
next_offset = offset + max_limit
|
||||||
prev_offset = offset - max_limit
|
prev_offset = offset - max_limit
|
||||||
base_url = 'http://testserver/?limit={0}'.format(max_limit)
|
base_url = 'http://testserver/?limit={}'.format(max_limit)
|
||||||
next_url = base_url + '&offset={0}'.format(next_offset)
|
next_url = base_url + '&offset={}'.format(next_offset)
|
||||||
prev_url = base_url + '&offset={0}'.format(prev_offset)
|
prev_url = base_url + '&offset={}'.format(prev_offset)
|
||||||
assert queryset == list(range(51, 66))
|
assert queryset == list(range(51, 66))
|
||||||
assert content.get('next') == next_url
|
assert content.get('next') == next_url
|
||||||
assert content.get('previous') == prev_url
|
assert content.get('previous') == prev_url
|
||||||
|
@ -632,7 +628,7 @@ class CursorPaginationTestsMixin:
|
||||||
assert current == [1, 1, 1, 1, 1]
|
assert current == [1, 1, 1, 1, 1]
|
||||||
assert next == [1, 2, 3, 4, 4]
|
assert next == [1, 2, 3, 4, 4]
|
||||||
|
|
||||||
assert isinstance(self.pagination.to_html(), six.text_type)
|
assert isinstance(self.pagination.to_html(), str)
|
||||||
|
|
||||||
def test_cursor_pagination_with_page_size(self):
|
def test_cursor_pagination_with_page_size(self):
|
||||||
(previous, current, next, previous_url, next_url) = self.get_pages('/?page_size=20')
|
(previous, current, next, previous_url, next_url) = self.get_pages('/?page_size=20')
|
||||||
|
@ -799,11 +795,11 @@ class TestCursorPagination(CursorPaginationTestsMixin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
class MockObject(object):
|
class MockObject:
|
||||||
def __init__(self, idx):
|
def __init__(self, idx):
|
||||||
self.created = idx
|
self.created = idx
|
||||||
|
|
||||||
class MockQuerySet(object):
|
class MockQuerySet:
|
||||||
def __init__(self, items):
|
def __init__(self, items):
|
||||||
self.items = items
|
self.items = items
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
@ -11,7 +8,6 @@ from django.core.files.uploadhandler import (
|
||||||
)
|
)
|
||||||
from django.http.request import RawPostDataException
|
from django.http.request import RawPostDataException
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.six import StringIO
|
|
||||||
|
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from rest_framework.parsers import (
|
from rest_framework.parsers import (
|
||||||
|
@ -34,7 +30,7 @@ class TestFormParser(TestCase):
|
||||||
""" Make sure the `QueryDict` works OK """
|
""" Make sure the `QueryDict` works OK """
|
||||||
parser = FormParser()
|
parser = FormParser()
|
||||||
|
|
||||||
stream = StringIO(self.string)
|
stream = io.StringIO(self.string)
|
||||||
data = parser.parse(stream)
|
data = parser.parse(stream)
|
||||||
|
|
||||||
assert Form(data).is_valid() is True
|
assert Form(data).is_valid() is True
|
||||||
|
@ -42,7 +38,7 @@ class TestFormParser(TestCase):
|
||||||
|
|
||||||
class TestFileUploadParser(TestCase):
|
class TestFileUploadParser(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class MockRequest(object):
|
class MockRequest:
|
||||||
pass
|
pass
|
||||||
self.stream = io.BytesIO(
|
self.stream = io.BytesIO(
|
||||||
"Test text file".encode('utf-8')
|
"Test text file".encode('utf-8')
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import unittest
|
import unittest
|
||||||
import warnings
|
import warnings
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
import django
|
import django
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -15,7 +14,7 @@ from rest_framework import (
|
||||||
HTTP_HEADER_ENCODING, RemovedInDRF310Warning, authentication, generics,
|
HTTP_HEADER_ENCODING, RemovedInDRF310Warning, authentication, generics,
|
||||||
permissions, serializers, status, views
|
permissions, serializers, status, views
|
||||||
)
|
)
|
||||||
from rest_framework.compat import PY36, is_guardian_installed, mock
|
from rest_framework.compat import PY36, is_guardian_installed
|
||||||
from rest_framework.filters import DjangoObjectPermissionsFilter
|
from rest_framework.filters import DjangoObjectPermissionsFilter
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from rest_framework.test import APIRequestFactory
|
from rest_framework.test import APIRequestFactory
|
||||||
|
@ -331,14 +330,14 @@ class ObjectPermissionsIntegrationTests(TestCase):
|
||||||
everyone = Group.objects.create(name='everyone')
|
everyone = Group.objects.create(name='everyone')
|
||||||
model_name = BasicPermModel._meta.model_name
|
model_name = BasicPermModel._meta.model_name
|
||||||
app_label = BasicPermModel._meta.app_label
|
app_label = BasicPermModel._meta.app_label
|
||||||
f = '{0}_{1}'.format
|
f = '{}_{}'.format
|
||||||
perms = {
|
perms = {
|
||||||
'view': f('view', model_name),
|
'view': f('view', model_name),
|
||||||
'change': f('change', model_name),
|
'change': f('change', model_name),
|
||||||
'delete': f('delete', model_name)
|
'delete': f('delete', model_name)
|
||||||
}
|
}
|
||||||
for perm in perms.values():
|
for perm in perms.values():
|
||||||
perm = '{0}.{1}'.format(app_label, perm)
|
perm = '{}.{}'.format(app_label, perm)
|
||||||
assign_perm(perm, everyone)
|
assign_perm(perm, everyone)
|
||||||
everyone.user_set.add(*users.values())
|
everyone.user_set.add(*users.values())
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class TestStringRelatedField(APISimpleTestCase):
|
||||||
assert representation == '<MockObject name=foo, pk=1>'
|
assert representation == '<MockObject name=foo, pk=1>'
|
||||||
|
|
||||||
|
|
||||||
class MockApiSettings(object):
|
class MockApiSettings:
|
||||||
def __init__(self, cutoff, cutoff_text):
|
def __init__(self, cutoff, cutoff_text):
|
||||||
self.HTML_SELECT_CUTOFF = cutoff
|
self.HTML_SELECT_CUTOFF = cutoff
|
||||||
self.HTML_SELECT_CUTOFF_TEXT = cutoff_text
|
self.HTML_SELECT_CUTOFF_TEXT = cutoff_text
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from tests.models import (
|
from tests.models import (
|
||||||
|
@ -263,7 +260,7 @@ class PKForeignKeyTests(TestCase):
|
||||||
instance = ForeignKeySource.objects.get(pk=1)
|
instance = ForeignKeySource.objects.get(pk=1)
|
||||||
serializer = ForeignKeySourceSerializer(instance, data=data)
|
serializer = ForeignKeySourceSerializer(instance, data=data)
|
||||||
assert not serializer.is_valid()
|
assert not serializer.is_valid()
|
||||||
assert serializer.errors == {'target': ['Incorrect type. Expected pk value, received %s.' % six.text_type.__name__]}
|
assert serializer.errors == {'target': ['Incorrect type. Expected pk value, received str.']}
|
||||||
|
|
||||||
def test_reverse_foreign_key_update(self):
|
def test_reverse_foreign_key_update(self):
|
||||||
data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]}
|
data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]}
|
||||||
|
@ -562,7 +559,7 @@ class OneToOnePrimaryKeyTests(TestCase):
|
||||||
# When: Trying to create a second object
|
# When: Trying to create a second object
|
||||||
second_source = OneToOnePKSourceSerializer(data=data)
|
second_source = OneToOnePKSourceSerializer(data=data)
|
||||||
self.assertFalse(second_source.is_valid())
|
self.assertFalse(second_source.is_valid())
|
||||||
expected = {'target': [u'one to one pk source with this target already exists.']}
|
expected = {'target': ['one to one pk source with this target already exists.']}
|
||||||
self.assertDictEqual(second_source.errors, expected)
|
self.assertDictEqual(second_source.errors, expected)
|
||||||
|
|
||||||
def test_one_to_one_when_primary_key_does_not_exist(self):
|
def test_one_to_one_when_primary_key_does_not_exist(self):
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
@ -11,7 +8,6 @@ from django.db import models
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
|
||||||
from django.utils.safestring import SafeText
|
from django.utils.safestring import SafeText
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -175,7 +171,7 @@ class RendererEndToEndTests(TestCase):
|
||||||
resp = self.client.head('/')
|
resp = self.client.head('/')
|
||||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||||
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
|
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
|
||||||
self.assertEqual(resp.content, six.b(''))
|
self.assertEqual(resp.content, b'')
|
||||||
|
|
||||||
def test_default_renderer_serializes_content_on_accept_any(self):
|
def test_default_renderer_serializes_content_on_accept_any(self):
|
||||||
"""If the Accept header is set to */* the default renderer should serialize the response."""
|
"""If the Accept header is set to */* the default renderer should serialize the response."""
|
||||||
|
@ -348,7 +344,7 @@ class JSONRendererTests(TestCase):
|
||||||
self.assertEqual(data, {'key': 'string value', '2': 3})
|
self.assertEqual(data, {'key': 'string value', '2': 3})
|
||||||
|
|
||||||
def test_render_obj_with_getitem(self):
|
def test_render_obj_with_getitem(self):
|
||||||
class DictLike(object):
|
class DictLike:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._dict = {}
|
self._dict = {}
|
||||||
|
|
||||||
|
@ -647,7 +643,7 @@ class BrowsableAPIRendererTests(URLPatternsTestCase):
|
||||||
assert self.renderer.get_description({}, status_code=403) == ''
|
assert self.renderer.get_description({}, status_code=403) == ''
|
||||||
|
|
||||||
def test_get_filter_form_returns_none_if_data_is_not_list_instance(self):
|
def test_get_filter_form_returns_none_if_data_is_not_list_instance(self):
|
||||||
class DummyView(object):
|
class DummyView:
|
||||||
get_queryset = None
|
get_queryset = None
|
||||||
filter_backends = None
|
filter_backends = None
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""
|
"""
|
||||||
Tests for content parsing, and form-overloaded content parsing.
|
Tests for content parsing, and form-overloaded content parsing.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
@ -15,7 +13,6 @@ from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
from django.http.request import RawPostDataException
|
from django.http.request import RawPostDataException
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
@ -82,7 +79,7 @@ class TestContentParsing(TestCase):
|
||||||
Ensure request.data returns content for POST request with
|
Ensure request.data returns content for POST request with
|
||||||
non-form content.
|
non-form content.
|
||||||
"""
|
"""
|
||||||
content = six.b('qwerty')
|
content = b'qwerty'
|
||||||
content_type = 'text/plain'
|
content_type = 'text/plain'
|
||||||
request = Request(factory.post('/', content, content_type=content_type))
|
request = Request(factory.post('/', content, content_type=content_type))
|
||||||
request.parsers = (PlainTextParser(),)
|
request.parsers = (PlainTextParser(),)
|
||||||
|
@ -121,7 +118,7 @@ class TestContentParsing(TestCase):
|
||||||
Ensure request.data returns content for PUT request with
|
Ensure request.data returns content for PUT request with
|
||||||
non-form content.
|
non-form content.
|
||||||
"""
|
"""
|
||||||
content = six.b('qwerty')
|
content = b'qwerty'
|
||||||
content_type = 'text/plain'
|
content_type = 'text/plain'
|
||||||
request = Request(factory.put('/', content, content_type=content_type))
|
request = Request(factory.put('/', content, content_type=content_type))
|
||||||
request.parsers = (PlainTextParser(), )
|
request.parsers = (PlainTextParser(), )
|
||||||
|
@ -235,7 +232,7 @@ class TestUserSetter(TestCase):
|
||||||
This proves that when an AttributeError is raised inside of the request.user
|
This proves that when an AttributeError is raised inside of the request.user
|
||||||
property, that we can handle this and report the true, underlying error.
|
property, that we can handle this and report the true, underlying error.
|
||||||
"""
|
"""
|
||||||
class AuthRaisesAttributeError(object):
|
class AuthRaisesAttributeError:
|
||||||
def authenticate(self, request):
|
def authenticate(self, request):
|
||||||
self.MISSPELLED_NAME_THAT_DOESNT_EXIST
|
self.MISSPELLED_NAME_THAT_DOESNT_EXIST
|
||||||
|
|
||||||
|
@ -249,10 +246,6 @@ class TestUserSetter(TestCase):
|
||||||
with pytest.raises(WrappedAttributeError, match=expected):
|
with pytest.raises(WrappedAttributeError, match=expected):
|
||||||
request.user
|
request.user
|
||||||
|
|
||||||
# python 2 hasattr fails for *any* exception, not just AttributeError
|
|
||||||
if six.PY2:
|
|
||||||
return
|
|
||||||
|
|
||||||
with pytest.raises(WrappedAttributeError, match=expected):
|
with pytest.raises(WrappedAttributeError, match=expected):
|
||||||
hasattr(request, 'user')
|
hasattr(request, 'user')
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import generics, routers, serializers, status, viewsets
|
from rest_framework import generics, routers, serializers, status, viewsets
|
||||||
from rest_framework.parsers import JSONParser
|
from rest_framework.parsers import JSONParser
|
||||||
|
@ -150,7 +147,7 @@ class RendererIntegrationTests(TestCase):
|
||||||
resp = self.client.head('/')
|
resp = self.client.head('/')
|
||||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||||
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
|
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
|
||||||
self.assertEqual(resp.content, six.b(''))
|
self.assertEqual(resp.content, b'')
|
||||||
|
|
||||||
def test_default_renderer_serializes_content_on_accept_any(self):
|
def test_default_renderer_serializes_content_on_accept_any(self):
|
||||||
"""If the Accept header is set to */* the default renderer should serialize the response."""
|
"""If the Accept header is set to */* the default renderer should serialize the response."""
|
||||||
|
@ -260,7 +257,7 @@ class Issue807Tests(TestCase):
|
||||||
"""
|
"""
|
||||||
headers = {"HTTP_ACCEPT": RendererA.media_type}
|
headers = {"HTTP_ACCEPT": RendererA.media_type}
|
||||||
resp = self.client.get('/', **headers)
|
resp = self.client.get('/', **headers)
|
||||||
expected = "{0}; charset={1}".format(RendererA.media_type, 'utf-8')
|
expected = "{}; charset={}".format(RendererA.media_type, 'utf-8')
|
||||||
self.assertEqual(expected, resp['Content-Type'])
|
self.assertEqual(expected, resp['Content-Type'])
|
||||||
|
|
||||||
def test_if_there_is_charset_specified_on_renderer_it_gets_appended(self):
|
def test_if_there_is_charset_specified_on_renderer_it_gets_appended(self):
|
||||||
|
@ -270,7 +267,7 @@ class Issue807Tests(TestCase):
|
||||||
"""
|
"""
|
||||||
headers = {"HTTP_ACCEPT": RendererC.media_type}
|
headers = {"HTTP_ACCEPT": RendererC.media_type}
|
||||||
resp = self.client.get('/', **headers)
|
resp = self.client.get('/', **headers)
|
||||||
expected = "{0}; charset={1}".format(RendererC.media_type, RendererC.charset)
|
expected = "{}; charset={}".format(RendererC.media_type, RendererC.charset)
|
||||||
self.assertEqual(expected, resp['Content-Type'])
|
self.assertEqual(expected, resp['Content-Type'])
|
||||||
|
|
||||||
def test_content_type_set_explicitly_on_response(self):
|
def test_content_type_set_explicitly_on_response(self):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.urls import NoReverseMatch
|
from django.urls import NoReverseMatch
|
||||||
|
@ -19,7 +17,7 @@ urlpatterns = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class MockVersioningScheme(object):
|
class MockVersioningScheme:
|
||||||
|
|
||||||
def __init__(self, raise_error=False):
|
def __init__(self, raise_error=False):
|
||||||
self.raise_error = raise_error
|
self.raise_error = raise_error
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ from .models import BasicModel, ForeignKeySource, ManyToManySource
|
||||||
factory = APIRequestFactory()
|
factory = APIRequestFactory()
|
||||||
|
|
||||||
|
|
||||||
class MockUser(object):
|
class MockUser:
|
||||||
def is_authenticated(self):
|
def is_authenticated(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ class ExampleViewSet(ModelViewSet):
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
assert self.request
|
assert self.request
|
||||||
assert self.action
|
assert self.action
|
||||||
return super(ExampleViewSet, self).get_serializer(*args, **kwargs)
|
return super().get_serializer(*args, **kwargs)
|
||||||
|
|
||||||
@action(methods=['get', 'post'], detail=False)
|
@action(methods=['get', 'post'], detail=False)
|
||||||
def documented_custom_action(self, request):
|
def documented_custom_action(self, request):
|
||||||
|
@ -1303,7 +1303,7 @@ def test_head_and_options_methods_are_excluded():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not coreapi, reason='coreapi is not installed')
|
@pytest.mark.skipif(not coreapi, reason='coreapi is not installed')
|
||||||
class TestAutoSchemaAllowsFilters(object):
|
class TestAutoSchemaAllowsFilters:
|
||||||
class MockAPIView(APIView):
|
class MockAPIView(APIView):
|
||||||
filter_backends = [filters.OrderingFilter]
|
filter_backends = [filters.OrderingFilter]
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
# coding: utf-8
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
import unittest
|
from collections import ChainMap
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from rest_framework import exceptions, fields, relations, serializers
|
from rest_framework import exceptions, fields, relations, serializers
|
||||||
from rest_framework.compat import Mapping, unicode_repr
|
from rest_framework.compat import Mapping
|
||||||
from rest_framework.fields import Field
|
from rest_framework.fields import Field
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -18,15 +15,9 @@ from .models import (
|
||||||
)
|
)
|
||||||
from .utils import MockObject
|
from .utils import MockObject
|
||||||
|
|
||||||
try:
|
|
||||||
from collections import ChainMap
|
|
||||||
except ImportError:
|
|
||||||
ChainMap = False
|
|
||||||
|
|
||||||
|
|
||||||
# Test serializer fields imports.
|
# Test serializer fields imports.
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
class TestFieldImports:
|
class TestFieldImports:
|
||||||
def is_field(self, name, value):
|
def is_field(self, name, value):
|
||||||
return (
|
return (
|
||||||
|
@ -130,7 +121,6 @@ class TestSerializer:
|
||||||
assert not serializer.is_valid()
|
assert not serializer.is_valid()
|
||||||
assert serializer.errors == {'non_field_errors': ['No data provided']}
|
assert serializer.errors == {'non_field_errors': ['No data provided']}
|
||||||
|
|
||||||
@unittest.skipUnless(ChainMap, 'requires python 3.3')
|
|
||||||
def test_serialize_chainmap(self):
|
def test_serialize_chainmap(self):
|
||||||
data = ChainMap({'char': 'abc'}, {'integer': 123})
|
data = ChainMap({'char': 'abc'}, {'integer': 123})
|
||||||
serializer = self.Serializer(data=data)
|
serializer = self.Serializer(data=data)
|
||||||
|
@ -160,7 +150,7 @@ class TestSerializer:
|
||||||
to_internal_value() is expected to return a dict, but subclasses may
|
to_internal_value() is expected to return a dict, but subclasses may
|
||||||
return application specific type.
|
return application specific type.
|
||||||
"""
|
"""
|
||||||
class Point(object):
|
class Point:
|
||||||
def __init__(self, srid, x, y):
|
def __init__(self, srid, x, y):
|
||||||
self.srid = srid
|
self.srid = srid
|
||||||
self.coords = (x, y)
|
self.coords = (x, y)
|
||||||
|
@ -171,7 +161,7 @@ class TestSerializer:
|
||||||
latitude = serializers.FloatField(source='y')
|
latitude = serializers.FloatField(source='y')
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
kwargs = super(NestedPointSerializer, self).to_internal_value(data)
|
kwargs = super().to_internal_value(data)
|
||||||
return Point(srid=4326, **kwargs)
|
return Point(srid=4326, **kwargs)
|
||||||
|
|
||||||
serializer = NestedPointSerializer(data={'longitude': 6.958307, 'latitude': 50.941357})
|
serializer = NestedPointSerializer(data={'longitude': 6.958307, 'latitude': 50.941357})
|
||||||
|
@ -201,7 +191,7 @@ class TestSerializer:
|
||||||
def raise_exception(value):
|
def raise_exception(value):
|
||||||
raise exceptions.ValidationError('Raised error')
|
raise exceptions.ValidationError('Raised error')
|
||||||
|
|
||||||
for validators in ([raise_exception], (raise_exception,), set([raise_exception])):
|
for validators in ([raise_exception], (raise_exception,), {raise_exception}):
|
||||||
class ExampleSerializer(serializers.Serializer):
|
class ExampleSerializer(serializers.Serializer):
|
||||||
char = serializers.CharField(validators=validators)
|
char = serializers.CharField(validators=validators)
|
||||||
integer = serializers.IntegerField()
|
integer = serializers.IntegerField()
|
||||||
|
@ -397,7 +387,7 @@ class TestIncorrectlyConfigured:
|
||||||
|
|
||||||
|
|
||||||
class TestUnicodeRepr:
|
class TestUnicodeRepr:
|
||||||
def test_unicode_repr(self):
|
def test_repr(self):
|
||||||
class ExampleSerializer(serializers.Serializer):
|
class ExampleSerializer(serializers.Serializer):
|
||||||
example = serializers.CharField()
|
example = serializers.CharField()
|
||||||
|
|
||||||
|
@ -406,7 +396,7 @@ class TestUnicodeRepr:
|
||||||
self.example = '한국'
|
self.example = '한국'
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return unicode_repr(self.example)
|
return repr(self.example)
|
||||||
|
|
||||||
instance = ExampleObject()
|
instance = ExampleObject()
|
||||||
serializer = ExampleSerializer(instance)
|
serializer = ExampleSerializer(instance)
|
||||||
|
@ -609,7 +599,7 @@ class Test2555Regression:
|
||||||
def test_serializer_context(self):
|
def test_serializer_context(self):
|
||||||
class NestedSerializer(serializers.Serializer):
|
class NestedSerializer(serializers.Serializer):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(NestedSerializer, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
# .context should not cache
|
# .context should not cache
|
||||||
self.context
|
self.context
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
"""
|
"""
|
||||||
Tests to cover bulk create and update using serializers.
|
Tests to cover bulk create and update using serializers.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
@ -87,8 +84,7 @@ class BulkCreateSerializerTests(TestCase):
|
||||||
serializer = self.BookSerializer(data=data, many=True)
|
serializer = self.BookSerializer(data=data, many=True)
|
||||||
assert serializer.is_valid() is False
|
assert serializer.is_valid() is False
|
||||||
|
|
||||||
text_type_string = six.text_type.__name__
|
message = 'Invalid data. Expected a dictionary, but got str.'
|
||||||
message = 'Invalid data. Expected a dictionary, but got %s.' % text_type_string
|
|
||||||
expected_errors = [
|
expected_errors = [
|
||||||
{'non_field_errors': [message]},
|
{'non_field_errors': [message]},
|
||||||
{'non_field_errors': [message]},
|
{'non_field_errors': [message]},
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
from rest_framework.settings import APISettings, api_settings
|
from rest_framework.settings import APISettings, api_settings
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from rest_framework.status import (
|
from rest_framework.status import (
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user