mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-29 17:39:48 +03:00
Merge branch 'master' of https://github.com/encode/django-rest-framework into 34
This commit is contained in:
commit
b37eb5f2fa
|
@ -59,7 +59,7 @@ Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recom
|
||||||
To run the tests, clone the repository, and then:
|
To run the tests, clone the repository, and then:
|
||||||
|
|
||||||
# Setup the virtual environment
|
# Setup the virtual environment
|
||||||
virtualenv env
|
python3 -m venv env
|
||||||
source env/bin/activate
|
source env/bin/activate
|
||||||
pip install django
|
pip install django
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
@ -115,7 +115,7 @@ It's also useful to remember that if you have an outstanding pull request then p
|
||||||
|
|
||||||
GitHub's documentation for working on pull requests is [available here][pull-requests].
|
GitHub's documentation for working on pull requests is [available here][pull-requests].
|
||||||
|
|
||||||
Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible with both Python 2 and Python 3, and that they run properly on all supported versions of Django.
|
Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible on all supported versions of Python and Django.
|
||||||
|
|
||||||
Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.
|
Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.
|
||||||
|
|
||||||
|
|
|
@ -354,7 +354,7 @@ The following third party packages are also available.
|
||||||
|
|
||||||
## Django OAuth Toolkit
|
## Django OAuth Toolkit
|
||||||
|
|
||||||
The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support, and works with Python 2.7 and Python 3.3+. The package is maintained by [Evonove][evonove] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and well supported and is currently our **recommended package for OAuth 2.0 support**.
|
The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support and works with Python 3.4+. The package is maintained by [Evonove][evonove] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and well supported and is currently our **recommended package for OAuth 2.0 support**.
|
||||||
|
|
||||||
#### Installation & configuration
|
#### Installation & configuration
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,7 @@ If you want the date field to be entirely hidden from the user, then use `Hidden
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Note**: The `UniqueFor<Range>Validation` classes impose an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input.
|
**Note**: The `UniqueFor<Range>Validator` classes impose an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recom
|
||||||
To run the tests, clone the repository, and then:
|
To run the tests, clone the repository, and then:
|
||||||
|
|
||||||
# Setup the virtual environment
|
# Setup the virtual environment
|
||||||
virtualenv env
|
python3 -m venv env
|
||||||
source env/bin/activate
|
source env/bin/activate
|
||||||
pip install django
|
pip install django
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
@ -121,7 +121,7 @@ It's also useful to remember that if you have an outstanding pull request then p
|
||||||
|
|
||||||
GitHub's documentation for working on pull requests is [available here][pull-requests].
|
GitHub's documentation for working on pull requests is [available here][pull-requests].
|
||||||
|
|
||||||
Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible with both Python 2 and Python 3, and that they run properly on all supported versions of Django.
|
Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible on all supported versions of Python and Django.
|
||||||
|
|
||||||
Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.
|
Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ continued development by **[signing up for a paid plan][funding]**.
|
||||||
|
|
||||||
REST framework requires the following:
|
REST framework requires the following:
|
||||||
|
|
||||||
* 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
|
||||||
|
|
|
@ -14,18 +14,18 @@ The tutorial is fairly in-depth, so you should probably get a cookie and a cup o
|
||||||
|
|
||||||
## Setting up a new environment
|
## Setting up a new environment
|
||||||
|
|
||||||
Before we do anything else we'll create a new virtual environment, using [virtualenv]. This will make sure our package configuration is kept nicely isolated from any other projects we're working on.
|
Before we do anything else we'll create a new virtual environment, using [venv]. This will make sure our package configuration is kept nicely isolated from any other projects we're working on.
|
||||||
|
|
||||||
virtualenv env
|
python3 -m venv env
|
||||||
source env/bin/activate
|
source env/bin/activate
|
||||||
|
|
||||||
Now that we're inside a virtualenv environment, we can install our package requirements.
|
Now that we're inside a virtual environment, we can install our package requirements.
|
||||||
|
|
||||||
pip install django
|
pip install django
|
||||||
pip install djangorestframework
|
pip install djangorestframework
|
||||||
pip install pygments # We'll be using this for the code highlighting
|
pip install pygments # We'll be using this for the code highlighting
|
||||||
|
|
||||||
**Note:** To exit the virtualenv environment at any time, just type `deactivate`. For more information see the [virtualenv documentation][virtualenv].
|
**Note:** To exit the virtual environment at any time, just type `deactivate`. For more information see the [venv documentation][venv].
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
|
@ -372,7 +372,7 @@ We'll see how we can start to improve things in [part 2 of the tutorial][tut-2].
|
||||||
[quickstart]: quickstart.md
|
[quickstart]: quickstart.md
|
||||||
[repo]: https://github.com/encode/rest-framework-tutorial
|
[repo]: https://github.com/encode/rest-framework-tutorial
|
||||||
[sandbox]: https://restframework.herokuapp.com/
|
[sandbox]: https://restframework.herokuapp.com/
|
||||||
[virtualenv]: http://www.virtualenv.org/en/latest/index.html
|
[venv]: https://docs.python.org/3/library/venv.html
|
||||||
[tut-2]: 2-requests-and-responses.md
|
[tut-2]: 2-requests-and-responses.md
|
||||||
[httpie]: https://github.com/jakubroztocil/httpie#installation
|
[httpie]: https://github.com/jakubroztocil/httpie#installation
|
||||||
[curl]: https://curl.haxx.se/
|
[curl]: https://curl.haxx.se/
|
||||||
|
|
|
@ -10,11 +10,11 @@ Create a new Django project named `tutorial`, then start a new app called `quick
|
||||||
mkdir tutorial
|
mkdir tutorial
|
||||||
cd tutorial
|
cd tutorial
|
||||||
|
|
||||||
# Create a virtualenv to isolate our package dependencies locally
|
# Create a virtual environment to isolate our package dependencies locally
|
||||||
virtualenv env
|
python3 -m venv env
|
||||||
source env/bin/activate # On Windows use `env\Scripts\activate`
|
source env/bin/activate # On Windows use `env\Scripts\activate`
|
||||||
|
|
||||||
# Install Django and Django REST framework into the virtualenv
|
# Install Django and Django REST framework into the virtual environment
|
||||||
pip install django
|
pip install django
|
||||||
pip install djangorestframework
|
pip install djangorestframework
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ 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.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class AuthTokenConfig(AppConfig):
|
class AuthTokenConfig(AppConfig):
|
||||||
|
|
|
@ -3,7 +3,7 @@ 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.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class Token(models.Model):
|
class Token(models.Model):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ 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.
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
|
|
@ -8,8 +8,8 @@ import math
|
||||||
|
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
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 gettext_lazy as _
|
||||||
from django.utils.translation import ungettext
|
from django.utils.translation import ngettext
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
|
from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
|
||||||
|
@ -230,9 +230,9 @@ class Throttled(APIException):
|
||||||
wait = math.ceil(wait)
|
wait = math.ceil(wait)
|
||||||
detail = ' '.join((
|
detail = ' '.join((
|
||||||
detail,
|
detail,
|
||||||
force_text(ungettext(self.extra_detail_singular.format(wait=wait),
|
force_text(ngettext(self.extra_detail_singular.format(wait=wait),
|
||||||
self.extra_detail_plural.format(wait=wait),
|
self.extra_detail_plural.format(wait=wait),
|
||||||
wait))))
|
wait))))
|
||||||
self.wait = wait
|
self.wait = wait
|
||||||
super().__init__(detail, code)
|
super().__init__(detail, code)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import inspect
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from collections.abc import Mapping
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
@ -25,12 +26,12 @@ from django.utils.formats import localize_input, sanitize_separators
|
||||||
from django.utils.functional import lazy
|
from django.utils.functional import lazy
|
||||||
from django.utils.ipv6 import clean_ipv6_address
|
from django.utils.ipv6 import clean_ipv6_address
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from pytz.exceptions import InvalidTimeError
|
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,
|
MaxLengthValidator, MaxValueValidator, MinLengthValidator,
|
||||||
MinValueValidator, ProhibitNullCharactersValidator
|
MinValueValidator, ProhibitNullCharactersValidator
|
||||||
)
|
)
|
||||||
from rest_framework.exceptions import ErrorDetail, ValidationError
|
from rest_framework.exceptions import ErrorDetail, ValidationError
|
||||||
|
@ -1753,7 +1754,7 @@ class JSONField(Field):
|
||||||
try:
|
try:
|
||||||
if self.binary or getattr(data, 'is_json_string', False):
|
if self.binary or getattr(data, 'is_json_string', False):
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
data = data.decode('utf-8')
|
data = data.decode()
|
||||||
return json.loads(data)
|
return json.loads(data)
|
||||||
else:
|
else:
|
||||||
json.dumps(data)
|
json.dumps(data)
|
||||||
|
@ -1764,10 +1765,7 @@ class JSONField(Field):
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
if self.binary:
|
if self.binary:
|
||||||
value = json.dumps(value)
|
value = json.dumps(value)
|
||||||
# On python 2.x the return type for json.dumps() is underspecified.
|
value = value.encode()
|
||||||
# On python 3.x json.dumps() returns unicode strings.
|
|
||||||
if isinstance(value, str):
|
|
||||||
value = bytes(value.encode('utf-8'))
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ 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.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import RemovedInDRF310Warning
|
from rest_framework import RemovedInDRF310Warning
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
|
|
|
@ -29,7 +29,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
renderer = self.get_renderer(options['format'])
|
renderer = self.get_renderer(options['format'])
|
||||||
output = renderer.render(schema, renderer_context={})
|
output = renderer.render(schema, renderer_context={})
|
||||||
self.stdout.write(output.decode('utf-8'))
|
self.stdout.write(output.decode())
|
||||||
|
|
||||||
def get_renderer(self, format):
|
def get_renderer(self, format):
|
||||||
renderer_cls = {
|
renderer_cls = {
|
||||||
|
|
|
@ -10,7 +10,7 @@ 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.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.compat import coreapi, coreschema
|
from rest_framework.compat import coreapi, coreschema
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
|
|
|
@ -80,8 +80,7 @@ class FormParser(BaseParser):
|
||||||
"""
|
"""
|
||||||
parser_context = parser_context or {}
|
parser_context = parser_context or {}
|
||||||
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
|
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
|
||||||
data = QueryDict(stream.read(), encoding=encoding)
|
return QueryDict(stream.read(), encoding=encoding)
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class MultiPartParser(BaseParser):
|
class MultiPartParser(BaseParser):
|
||||||
|
@ -202,7 +201,7 @@ class FileUploadParser(BaseParser):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
meta = parser_context['request'].META
|
meta = parser_context['request'].META
|
||||||
disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
|
disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode())
|
||||||
filename_parm = disposition[1]
|
filename_parm = disposition[1]
|
||||||
if 'filename*' in filename_parm:
|
if 'filename*' in filename_parm:
|
||||||
return self.get_encoded_filename(filename_parm)
|
return self.get_encoded_filename(filename_parm)
|
||||||
|
|
|
@ -7,7 +7,7 @@ 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.encoding import smart_text, uri_to_iri
|
from django.utils.encoding import smart_text, uri_to_iri
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.fields import (
|
from rest_framework.fields import (
|
||||||
Field, empty, get_attribute, is_simple_callable, iter_options
|
Field, empty, get_attribute, is_simple_callable, iter_options
|
||||||
|
|
|
@ -104,18 +104,11 @@ class JSONRenderer(BaseRenderer):
|
||||||
allow_nan=not self.strict, separators=separators
|
allow_nan=not self.strict, separators=separators
|
||||||
)
|
)
|
||||||
|
|
||||||
# On python 2.x json.dumps() returns bytestrings if ensure_ascii=True,
|
# We always fully escape \u2028 and \u2029 to ensure we output JSON
|
||||||
# but if ensure_ascii=False, the return type is underspecified,
|
# that is a strict javascript subset.
|
||||||
# and may (or may not) be unicode.
|
# See: http://timelessrepo.com/json-isnt-a-javascript-subset
|
||||||
# On python 3.x json.dumps() returns unicode strings.
|
ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029')
|
||||||
if isinstance(ret, str):
|
return ret.encode()
|
||||||
# We always fully escape \u2028 and \u2029 to ensure we output JSON
|
|
||||||
# that is a strict javascript subset. If bytes were returned
|
|
||||||
# by json.dumps() then we don't have these characters in any case.
|
|
||||||
# See: http://timelessrepo.com/json-isnt-a-javascript-subset
|
|
||||||
ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029')
|
|
||||||
return bytes(ret.encode('utf-8'))
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateHTMLRenderer(BaseRenderer):
|
class TemplateHTMLRenderer(BaseRenderer):
|
||||||
|
@ -574,7 +567,7 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
data.pop(name, None)
|
data.pop(name, None)
|
||||||
content = renderer.render(data, accepted, context)
|
content = renderer.render(data, accepted, context)
|
||||||
# Renders returns bytes, but CharField expects a str.
|
# Renders returns bytes, but CharField expects a str.
|
||||||
content = content.decode('utf-8')
|
content = content.decode()
|
||||||
else:
|
else:
|
||||||
content = None
|
content = None
|
||||||
|
|
||||||
|
@ -681,7 +674,7 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
csrf_header_name = csrf_header_name[5:]
|
csrf_header_name = csrf_header_name[5:]
|
||||||
csrf_header_name = csrf_header_name.replace('_', '-')
|
csrf_header_name = csrf_header_name.replace('_', '-')
|
||||||
|
|
||||||
context = {
|
return {
|
||||||
'content': self.get_content(renderer, data, accepted_media_type, renderer_context),
|
'content': self.get_content(renderer, data, accepted_media_type, renderer_context),
|
||||||
'code_style': pygments_css(self.code_style),
|
'code_style': pygments_css(self.code_style),
|
||||||
'view': view,
|
'view': view,
|
||||||
|
@ -717,7 +710,6 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
'csrf_cookie_name': csrf_cookie_name,
|
'csrf_cookie_name': csrf_cookie_name,
|
||||||
'csrf_header_name': csrf_header_name
|
'csrf_header_name': csrf_header_name
|
||||||
}
|
}
|
||||||
return context
|
|
||||||
|
|
||||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||||
"""
|
"""
|
||||||
|
@ -1032,7 +1024,7 @@ class OpenAPIRenderer(_BaseOpenAPIRenderer):
|
||||||
|
|
||||||
def render(self, data, media_type=None, renderer_context=None):
|
def render(self, data, media_type=None, renderer_context=None):
|
||||||
structure = self.get_structure(data)
|
structure = self.get_structure(data)
|
||||||
return yaml.dump(structure, default_flow_style=False).encode('utf-8')
|
return yaml.dump(structure, default_flow_style=False).encode()
|
||||||
|
|
||||||
|
|
||||||
class JSONOpenAPIRenderer(_BaseOpenAPIRenderer):
|
class JSONOpenAPIRenderer(_BaseOpenAPIRenderer):
|
||||||
|
@ -1045,4 +1037,4 @@ class JSONOpenAPIRenderer(_BaseOpenAPIRenderer):
|
||||||
|
|
||||||
def render(self, data, media_type=None, renderer_context=None):
|
def render(self, data, media_type=None, renderer_context=None):
|
||||||
structure = self.get_structure(data)
|
structure = self.get_structure(data)
|
||||||
return json.dumps(structure, indent=4).encode('utf-8')
|
return json.dumps(structure, indent=4).encode()
|
||||||
|
|
|
@ -184,9 +184,7 @@ class EndpointEnumerator:
|
||||||
)
|
)
|
||||||
api_endpoints.extend(nested_endpoints)
|
api_endpoints.extend(nested_endpoints)
|
||||||
|
|
||||||
api_endpoints = sorted(api_endpoints, key=endpoint_ordering)
|
return sorted(api_endpoints, key=endpoint_ordering)
|
||||||
|
|
||||||
return api_endpoints
|
|
||||||
|
|
||||||
def get_path_from_regex(self, path_regex):
|
def get_path_from_regex(self, path_regex):
|
||||||
"""
|
"""
|
||||||
|
@ -195,8 +193,7 @@ class EndpointEnumerator:
|
||||||
path = simplify_regex(path_regex)
|
path = simplify_regex(path_regex)
|
||||||
|
|
||||||
# Strip Django 2.0 convertors as they are incompatible with uritemplate format
|
# Strip Django 2.0 convertors as they are incompatible with uritemplate format
|
||||||
path = re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g<parameter>}', path)
|
return re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g<parameter>}', path)
|
||||||
return path
|
|
||||||
|
|
||||||
def should_include_endpoint(self, path, callback):
|
def should_include_endpoint(self, path, callback):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -11,7 +11,7 @@ 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.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import exceptions, serializers
|
from rest_framework import exceptions, serializers
|
||||||
from rest_framework.compat import coreapi, coreschema, uritemplate
|
from rest_framework.compat import coreapi, coreschema, uritemplate
|
||||||
|
@ -435,8 +435,7 @@ class AutoSchema(ViewInspector):
|
||||||
by_name = OrderedDict((f.name, f) for f in fields)
|
by_name = OrderedDict((f.name, f) for f in fields)
|
||||||
for f in update_with:
|
for f in update_with:
|
||||||
by_name[f.name] = f
|
by_name[f.name] = f
|
||||||
fields = list(by_name.values())
|
return list(by_name.values())
|
||||||
return fields
|
|
||||||
|
|
||||||
def get_encoding(self, path, method):
|
def get_encoding(self, path, method):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -14,6 +14,7 @@ import copy
|
||||||
import inspect
|
import inspect
|
||||||
import traceback
|
import traceback
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from collections.abc import Mapping
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
|
@ -23,9 +24,9 @@ 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 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 gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.compat import Mapping, postgres_fields
|
from rest_framework.compat import 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
|
||||||
|
|
|
@ -18,10 +18,9 @@ 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 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.module_loading import import_string
|
||||||
|
|
||||||
from rest_framework import ISO_8601
|
from rest_framework import ISO_8601
|
||||||
|
|
||||||
|
@ -175,11 +174,8 @@ def import_from_string(val, setting_name):
|
||||||
Attempt to import a class from a string representation.
|
Attempt to import a class from a string representation.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Nod to tastypie's use of importlib.
|
return import_string(val)
|
||||||
module_path, class_name = val.rsplit('.', 1)
|
except ImportError as e:
|
||||||
module = import_module(module_path)
|
|
||||||
return getattr(module, class_name)
|
|
||||||
except (ImportError, AttributeError) as e:
|
|
||||||
msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
|
msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
|
||||||
raise ImportError(msg)
|
raise ImportError(msg)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<h4 class="text-center">You are logged in as {{ user.username }}.</h4>
|
<h4 class="text-center">You are logged in as {{ user.get_username }}.</h4>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
|
|
@ -47,7 +47,7 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
return tuple(obj)
|
return tuple(obj)
|
||||||
elif isinstance(obj, bytes):
|
elif isinstance(obj, bytes):
|
||||||
# Best-effort for binary blobs. See #4187.
|
# Best-effort for binary blobs. See #4187.
|
||||||
return obj.decode('utf-8')
|
return obj.decode()
|
||||||
elif hasattr(obj, 'tolist'):
|
elif hasattr(obj, 'tolist'):
|
||||||
# Numpy arrays and array scalars.
|
# Numpy arrays and array scalars.
|
||||||
return obj.tolist()
|
return obj.tolist()
|
||||||
|
|
|
@ -41,9 +41,7 @@ def smart_repr(value):
|
||||||
# <django.core.validators.RegexValidator object at 0x1047af050>
|
# <django.core.validators.RegexValidator object at 0x1047af050>
|
||||||
# Should be presented as
|
# Should be presented as
|
||||||
# <django.core.validators.RegexValidator object>
|
# <django.core.validators.RegexValidator object>
|
||||||
value = re.sub(' at 0x[0-9A-Fa-f]{4,32}>', '>', value)
|
return re.sub(' at 0x[0-9A-Fa-f]{4,32}>', '>', value)
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def field_repr(field, force_many=False):
|
def field_repr(field, force_many=False):
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from collections.abc import MutableMapping
|
||||||
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
from rest_framework.compat import MutableMapping
|
|
||||||
from rest_framework.utils import json
|
from rest_framework.utils import json
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ 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 django.db import DataError
|
from django.db import DataError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
from rest_framework.compat import unicode_http_header
|
from rest_framework.compat import unicode_http_header
|
||||||
|
|
|
@ -11,7 +11,7 @@ PYTEST_ARGS = {
|
||||||
|
|
||||||
FLAKE8_ARGS = ['rest_framework', 'tests']
|
FLAKE8_ARGS = ['rest_framework', 'tests']
|
||||||
|
|
||||||
ISORT_ARGS = ['--recursive', '--check-only', '--diff', '-o' 'uritemplate', '-p', 'tests', 'rest_framework', 'tests']
|
ISORT_ARGS = ['--recursive', '--check-only', '--diff', 'rest_framework', 'tests']
|
||||||
|
|
||||||
|
|
||||||
def exit_on_failure(ret, message=None):
|
def exit_on_failure(ret, message=None):
|
||||||
|
|
|
@ -14,8 +14,8 @@ skip=.tox
|
||||||
atomic=true
|
atomic=true
|
||||||
multi_line_output=5
|
multi_line_output=5
|
||||||
known_standard_library=types
|
known_standard_library=types
|
||||||
known_third_party=pytest,_pytest,django,pytz
|
known_third_party=pytest,_pytest,django,pytz,uritemplate
|
||||||
known_first_party=rest_framework
|
known_first_party=rest_framework,tests
|
||||||
|
|
||||||
[coverage:run]
|
[coverage:run]
|
||||||
# NOTE: source is ignored with pytest-cov (but uses the same).
|
# NOTE: source is ignored with pytest-cov (but uses the same).
|
||||||
|
|
|
@ -183,7 +183,7 @@ class SessionAuthTests(TestCase):
|
||||||
cf. [#1810](https://github.com/encode/django-rest-framework/pull/1810)
|
cf. [#1810](https://github.com/encode/django-rest-framework/pull/1810)
|
||||||
"""
|
"""
|
||||||
response = self.csrf_client.get('/auth/login/')
|
response = self.csrf_client.get('/auth/login/')
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode()
|
||||||
assert '<label for="id_username">Username:</label>' in content
|
assert '<label for="id_username">Username:</label>' in content
|
||||||
|
|
||||||
def test_post_form_session_auth_failing_csrf(self):
|
def test_post_form_session_auth_failing_csrf(self):
|
||||||
|
|
|
@ -24,18 +24,18 @@ class DropdownWithAuthTests(TestCase):
|
||||||
def test_name_shown_when_logged_in(self):
|
def test_name_shown_when_logged_in(self):
|
||||||
self.client.login(username=self.username, password=self.password)
|
self.client.login(username=self.username, password=self.password)
|
||||||
response = self.client.get('/')
|
response = self.client.get('/')
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode()
|
||||||
assert 'john' in content
|
assert 'john' in content
|
||||||
|
|
||||||
def test_logout_shown_when_logged_in(self):
|
def test_logout_shown_when_logged_in(self):
|
||||||
self.client.login(username=self.username, password=self.password)
|
self.client.login(username=self.username, password=self.password)
|
||||||
response = self.client.get('/')
|
response = self.client.get('/')
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode()
|
||||||
assert '>Log out<' in content
|
assert '>Log out<' in content
|
||||||
|
|
||||||
def test_login_shown_when_logged_out(self):
|
def test_login_shown_when_logged_out(self):
|
||||||
response = self.client.get('/')
|
response = self.client.get('/')
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode()
|
||||||
assert '>Log in<' in content
|
assert '>Log in<' in content
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,16 +59,16 @@ class NoDropdownWithoutAuthTests(TestCase):
|
||||||
def test_name_shown_when_logged_in(self):
|
def test_name_shown_when_logged_in(self):
|
||||||
self.client.login(username=self.username, password=self.password)
|
self.client.login(username=self.username, password=self.password)
|
||||||
response = self.client.get('/')
|
response = self.client.get('/')
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode()
|
||||||
assert 'john' in content
|
assert 'john' in content
|
||||||
|
|
||||||
def test_dropdown_not_shown_when_logged_in(self):
|
def test_dropdown_not_shown_when_logged_in(self):
|
||||||
self.client.login(username=self.username, password=self.password)
|
self.client.login(username=self.username, password=self.password)
|
||||||
response = self.client.get('/')
|
response = self.client.get('/')
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode()
|
||||||
assert '<li class="dropdown">' not in content
|
assert '<li class="dropdown">' not in content
|
||||||
|
|
||||||
def test_dropdown_not_shown_when_logged_out(self):
|
def test_dropdown_not_shown_when_logged_out(self):
|
||||||
response = self.client.get('/')
|
response = self.client.get('/')
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode()
|
||||||
assert '<li class="dropdown">' not in content
|
assert '<li class="dropdown">' not in content
|
||||||
|
|
|
@ -34,7 +34,7 @@ class DropdownWithAuthTests(TestCase):
|
||||||
def test_login(self):
|
def test_login(self):
|
||||||
response = self.client.get('/api/')
|
response = self.client.get('/api/')
|
||||||
assert 200 == response.status_code
|
assert 200 == response.status_code
|
||||||
content = response.content.decode('utf-8')
|
content = response.content.decode()
|
||||||
assert 'form action="/api/"' in content
|
assert 'form action="/api/"' in content
|
||||||
assert 'input name="nested.one"' in content
|
assert 'input name="nested.one"' in content
|
||||||
assert 'input name="nested.two"' in content
|
assert 'input name="nested.two"' in content
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class RESTFrameworkModel(models.Model):
|
class RESTFrameworkModel(models.Model):
|
||||||
|
|
|
@ -202,8 +202,7 @@ class ActionDecoratorTestCase(TestCase):
|
||||||
def method():
|
def method():
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
# Python 2.x compatibility - cast __name__ to str
|
method.__name__ = name
|
||||||
method.__name__ = str(name)
|
|
||||||
getattr(test_action.mapping, name)(method)
|
getattr(test_action.mapping, name)(method)
|
||||||
|
|
||||||
# ensure the mapping returns the correct method name
|
# ensure the mapping returns the correct method name
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.test import RequestFactory, TestCase
|
from django.test import RequestFactory, TestCase
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.exceptions import (
|
from rest_framework.exceptions import (
|
||||||
APIException, ErrorDetail, Throttled, _get_error_details, bad_request,
|
APIException, ErrorDetail, Throttled, _get_error_details, bad_request,
|
||||||
|
|
|
@ -164,7 +164,7 @@ class TestRootView(TestCase):
|
||||||
request = factory.post('/', data, HTTP_ACCEPT='text/html')
|
request = factory.post('/', data, HTTP_ACCEPT='text/html')
|
||||||
response = self.view(request).render()
|
response = self.view(request).render()
|
||||||
expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>'
|
expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>'
|
||||||
assert expected_error in response.rendered_content.decode('utf-8')
|
assert expected_error in response.rendered_content.decode()
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_QUERIES_FOR_PUT = 2
|
EXPECTED_QUERIES_FOR_PUT = 2
|
||||||
|
@ -311,7 +311,7 @@ class TestInstanceView(TestCase):
|
||||||
request = factory.put('/', data, HTTP_ACCEPT='text/html')
|
request = factory.put('/', data, HTTP_ACCEPT='text/html')
|
||||||
response = self.view(request, pk=1).render()
|
response = self.view(request, pk=1).render()
|
||||||
expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>'
|
expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>'
|
||||||
assert expected_error in response.rendered_content.decode('utf-8')
|
assert expected_error in response.rendered_content.decode()
|
||||||
|
|
||||||
|
|
||||||
class TestFKInstanceView(TestCase):
|
class TestFKInstanceView(TestCase):
|
||||||
|
@ -538,7 +538,7 @@ class TestFilterBackendAppliedToViews(TestCase):
|
||||||
view = DynamicSerializerView.as_view()
|
view = DynamicSerializerView.as_view()
|
||||||
request = factory.get('/')
|
request = factory.get('/')
|
||||||
response = view(request).render()
|
response = view(request).render()
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode()
|
||||||
assert 'field_b' in content
|
assert 'field_b' in content
|
||||||
assert 'field_a' not in content
|
assert 'field_a' not in content
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,7 @@ class TestFileUploadParser(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class MockRequest:
|
class MockRequest:
|
||||||
pass
|
pass
|
||||||
self.stream = io.BytesIO(
|
self.stream = io.BytesIO(b"Test text file")
|
||||||
"Test text file".encode('utf-8')
|
|
||||||
)
|
|
||||||
request = MockRequest()
|
request = MockRequest()
|
||||||
request.upload_handlers = (MemoryFileUploadHandler(),)
|
request.upload_handlers = (MemoryFileUploadHandler(),)
|
||||||
request.META = {
|
request.META = {
|
||||||
|
@ -128,7 +126,7 @@ class TestFileUploadParser(TestCase):
|
||||||
|
|
||||||
class TestJSONParser(TestCase):
|
class TestJSONParser(TestCase):
|
||||||
def bytes(self, value):
|
def bytes(self, value):
|
||||||
return io.BytesIO(value.encode('utf-8'))
|
return io.BytesIO(value.encode())
|
||||||
|
|
||||||
def test_float_strictness(self):
|
def test_float_strictness(self):
|
||||||
parser = JSONParser()
|
parser = JSONParser()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from collections.abc import MutableMapping
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
|
@ -9,10 +10,10 @@ 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.safestring import SafeText
|
from django.utils.safestring import SafeText
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import permissions, serializers, status
|
from rest_framework import permissions, serializers, status
|
||||||
from rest_framework.compat import MutableMapping, coreapi
|
from rest_framework.compat import coreapi
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.renderers import (
|
from rest_framework.renderers import (
|
||||||
AdminRenderer, BaseRenderer, BrowsableAPIRenderer, DocumentationRenderer,
|
AdminRenderer, BaseRenderer, BrowsableAPIRenderer, DocumentationRenderer,
|
||||||
|
@ -75,8 +76,7 @@ class MockView(APIView):
|
||||||
renderer_classes = (RendererA, RendererB)
|
renderer_classes = (RendererA, RendererB)
|
||||||
|
|
||||||
def get(self, request, **kwargs):
|
def get(self, request, **kwargs):
|
||||||
response = Response(DUMMYCONTENT, status=DUMMYSTATUS)
|
return Response(DUMMYCONTENT, status=DUMMYSTATUS)
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
class MockGETView(APIView):
|
class MockGETView(APIView):
|
||||||
|
@ -303,14 +303,14 @@ class JSONRendererTests(TestCase):
|
||||||
o = DummyTestModel.objects.create(name='dummy')
|
o = DummyTestModel.objects.create(name='dummy')
|
||||||
qs = DummyTestModel.objects.values('id', 'name')
|
qs = DummyTestModel.objects.values('id', 'name')
|
||||||
ret = JSONRenderer().render(qs)
|
ret = JSONRenderer().render(qs)
|
||||||
data = json.loads(ret.decode('utf-8'))
|
data = json.loads(ret.decode())
|
||||||
self.assertEqual(data, [{'id': o.id, 'name': o.name}])
|
self.assertEqual(data, [{'id': o.id, 'name': o.name}])
|
||||||
|
|
||||||
def test_render_queryset_values_list(self):
|
def test_render_queryset_values_list(self):
|
||||||
o = DummyTestModel.objects.create(name='dummy')
|
o = DummyTestModel.objects.create(name='dummy')
|
||||||
qs = DummyTestModel.objects.values_list('id', 'name')
|
qs = DummyTestModel.objects.values_list('id', 'name')
|
||||||
ret = JSONRenderer().render(qs)
|
ret = JSONRenderer().render(qs)
|
||||||
data = json.loads(ret.decode('utf-8'))
|
data = json.loads(ret.decode())
|
||||||
self.assertEqual(data, [[o.id, o.name]])
|
self.assertEqual(data, [[o.id, o.name]])
|
||||||
|
|
||||||
def test_render_dict_abc_obj(self):
|
def test_render_dict_abc_obj(self):
|
||||||
|
@ -340,7 +340,7 @@ class JSONRendererTests(TestCase):
|
||||||
x['key'] = 'string value'
|
x['key'] = 'string value'
|
||||||
x[2] = 3
|
x[2] = 3
|
||||||
ret = JSONRenderer().render(x)
|
ret = JSONRenderer().render(x)
|
||||||
data = json.loads(ret.decode('utf-8'))
|
data = json.loads(ret.decode())
|
||||||
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):
|
||||||
|
@ -380,7 +380,7 @@ class JSONRendererTests(TestCase):
|
||||||
renderer = JSONRenderer()
|
renderer = JSONRenderer()
|
||||||
content = renderer.render(obj, 'application/json')
|
content = renderer.render(obj, 'application/json')
|
||||||
# Fix failing test case which depends on version of JSON library.
|
# Fix failing test case which depends on version of JSON library.
|
||||||
self.assertEqual(content.decode('utf-8'), _flat_repr)
|
self.assertEqual(content.decode(), _flat_repr)
|
||||||
|
|
||||||
def test_with_content_type_args(self):
|
def test_with_content_type_args(self):
|
||||||
"""
|
"""
|
||||||
|
@ -389,7 +389,7 @@ class JSONRendererTests(TestCase):
|
||||||
obj = {'foo': ['bar', 'baz']}
|
obj = {'foo': ['bar', 'baz']}
|
||||||
renderer = JSONRenderer()
|
renderer = JSONRenderer()
|
||||||
content = renderer.render(obj, 'application/json; indent=2')
|
content = renderer.render(obj, 'application/json; indent=2')
|
||||||
self.assertEqual(strip_trailing_whitespace(content.decode('utf-8')), _indented_repr)
|
self.assertEqual(strip_trailing_whitespace(content.decode()), _indented_repr)
|
||||||
|
|
||||||
|
|
||||||
class UnicodeJSONRendererTests(TestCase):
|
class UnicodeJSONRendererTests(TestCase):
|
||||||
|
@ -400,7 +400,7 @@ class UnicodeJSONRendererTests(TestCase):
|
||||||
obj = {'countries': ['United Kingdom', 'France', 'España']}
|
obj = {'countries': ['United Kingdom', 'France', 'España']}
|
||||||
renderer = JSONRenderer()
|
renderer = JSONRenderer()
|
||||||
content = renderer.render(obj, 'application/json')
|
content = renderer.render(obj, 'application/json')
|
||||||
self.assertEqual(content, '{"countries":["United Kingdom","France","España"]}'.encode('utf-8'))
|
self.assertEqual(content, '{"countries":["United Kingdom","France","España"]}'.encode())
|
||||||
|
|
||||||
def test_u2028_u2029(self):
|
def test_u2028_u2029(self):
|
||||||
# The \u2028 and \u2029 characters should be escaped,
|
# The \u2028 and \u2029 characters should be escaped,
|
||||||
|
@ -409,7 +409,7 @@ class UnicodeJSONRendererTests(TestCase):
|
||||||
obj = {'should_escape': '\u2028\u2029'}
|
obj = {'should_escape': '\u2028\u2029'}
|
||||||
renderer = JSONRenderer()
|
renderer = JSONRenderer()
|
||||||
content = renderer.render(obj, 'application/json')
|
content = renderer.render(obj, 'application/json')
|
||||||
self.assertEqual(content, '{"should_escape":"\\u2028\\u2029"}'.encode('utf-8'))
|
self.assertEqual(content, '{"should_escape":"\\u2028\\u2029"}'.encode())
|
||||||
|
|
||||||
|
|
||||||
class AsciiJSONRendererTests(TestCase):
|
class AsciiJSONRendererTests(TestCase):
|
||||||
|
@ -422,7 +422,7 @@ class AsciiJSONRendererTests(TestCase):
|
||||||
obj = {'countries': ['United Kingdom', 'France', 'España']}
|
obj = {'countries': ['United Kingdom', 'France', 'España']}
|
||||||
renderer = AsciiJSONRenderer()
|
renderer = AsciiJSONRenderer()
|
||||||
content = renderer.render(obj, 'application/json')
|
content = renderer.render(obj, 'application/json')
|
||||||
self.assertEqual(content, '{"countries":["United Kingdom","France","Espa\\u00f1a"]}'.encode('utf-8'))
|
self.assertEqual(content, '{"countries":["United Kingdom","France","Espa\\u00f1a"]}'.encode())
|
||||||
|
|
||||||
|
|
||||||
# Tests for caching issue, #346
|
# Tests for caching issue, #346
|
||||||
|
@ -653,9 +653,9 @@ class BrowsableAPIRendererTests(URLPatternsTestCase):
|
||||||
|
|
||||||
def test_extra_actions_dropdown(self):
|
def test_extra_actions_dropdown(self):
|
||||||
resp = self.client.get('/api/examples/', HTTP_ACCEPT='text/html')
|
resp = self.client.get('/api/examples/', HTTP_ACCEPT='text/html')
|
||||||
assert 'id="extra-actions-menu"' in resp.content.decode('utf-8')
|
assert 'id="extra-actions-menu"' in resp.content.decode()
|
||||||
assert '/api/examples/list_action/' in resp.content.decode('utf-8')
|
assert '/api/examples/list_action/' in resp.content.decode()
|
||||||
assert '>Extra list action<' in resp.content.decode('utf-8')
|
assert '>Extra list action<' in resp.content.decode()
|
||||||
|
|
||||||
|
|
||||||
class AdminRendererTests(TestCase):
|
class AdminRendererTests(TestCase):
|
||||||
|
|
|
@ -438,13 +438,13 @@ class TestEmptyPrefix(URLPatternsTestCase, TestCase):
|
||||||
def test_empty_prefix_list(self):
|
def test_empty_prefix_list(self):
|
||||||
response = self.client.get('/empty-prefix/')
|
response = self.client.get('/empty-prefix/')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.content.decode('utf-8')) == [{'uuid': '111', 'text': 'First'},
|
assert json.loads(response.content.decode()) == [{'uuid': '111', 'text': 'First'},
|
||||||
{'uuid': '222', 'text': 'Second'}]
|
{'uuid': '222', 'text': 'Second'}]
|
||||||
|
|
||||||
def test_empty_prefix_detail(self):
|
def test_empty_prefix_detail(self):
|
||||||
response = self.client.get('/empty-prefix/1/')
|
response = self.client.get('/empty-prefix/1/')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.content.decode('utf-8')) == {'uuid': '111', 'text': 'First'}
|
assert json.loads(response.content.decode()) == {'uuid': '111', 'text': 'First'}
|
||||||
|
|
||||||
|
|
||||||
class TestRegexUrlPath(URLPatternsTestCase, TestCase):
|
class TestRegexUrlPath(URLPatternsTestCase, TestCase):
|
||||||
|
@ -456,14 +456,14 @@ class TestRegexUrlPath(URLPatternsTestCase, TestCase):
|
||||||
kwarg = '1234'
|
kwarg = '1234'
|
||||||
response = self.client.get('/regex/list/{}/'.format(kwarg))
|
response = self.client.get('/regex/list/{}/'.format(kwarg))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.content.decode('utf-8')) == {'kwarg': kwarg}
|
assert json.loads(response.content.decode()) == {'kwarg': kwarg}
|
||||||
|
|
||||||
def test_regex_url_path_detail(self):
|
def test_regex_url_path_detail(self):
|
||||||
pk = '1'
|
pk = '1'
|
||||||
kwarg = '1234'
|
kwarg = '1234'
|
||||||
response = self.client.get('/regex/{}/detail/{}/'.format(pk, kwarg))
|
response = self.client.get('/regex/{}/detail/{}/'.format(pk, kwarg))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.content.decode('utf-8')) == {'pk': pk, 'kwarg': kwarg}
|
assert json.loads(response.content.decode()) == {'pk': pk, 'kwarg': kwarg}
|
||||||
|
|
||||||
|
|
||||||
class TestViewInitkwargs(URLPatternsTestCase, TestCase):
|
class TestViewInitkwargs(URLPatternsTestCase, TestCase):
|
||||||
|
|
|
@ -1358,4 +1358,4 @@ def test_schema_handles_exception():
|
||||||
response = schema_view(request)
|
response = schema_view(request)
|
||||||
response.render()
|
response.render()
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
assert "You do not have permission to perform this action." in str(response.content)
|
assert b"You do not have permission to perform this action." in response.content
|
||||||
|
|
|
@ -2,12 +2,12 @@ import inspect
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
from collections import ChainMap
|
from collections import ChainMap
|
||||||
|
from collections.abc import Mapping
|
||||||
|
|
||||||
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
|
|
||||||
from rest_framework.fields import Field
|
from rest_framework.fields import Field
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -386,23 +386,6 @@ class TestIncorrectlyConfigured:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestUnicodeRepr:
|
|
||||||
def test_repr(self):
|
|
||||||
class ExampleSerializer(serializers.Serializer):
|
|
||||||
example = serializers.CharField()
|
|
||||||
|
|
||||||
class ExampleObject:
|
|
||||||
def __init__(self):
|
|
||||||
self.example = '한국'
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return repr(self.example)
|
|
||||||
|
|
||||||
instance = ExampleObject()
|
|
||||||
serializer = ExampleSerializer(instance)
|
|
||||||
repr(serializer) # Should not error.
|
|
||||||
|
|
||||||
|
|
||||||
class TestNotRequiredOutput:
|
class TestNotRequiredOutput:
|
||||||
def test_not_required_output_for_dict(self):
|
def test_not_required_output_for_dict(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.shortcuts import render
|
||||||
def test_base_template_with_context():
|
def test_base_template_with_context():
|
||||||
context = {'request': True, 'csrf_token': 'TOKEN'}
|
context = {'request': True, 'csrf_token': 'TOKEN'}
|
||||||
result = render({}, 'rest_framework/base.html', context=context)
|
result = render({}, 'rest_framework/base.html', context=context)
|
||||||
assert re.search(r'\bcsrfToken: "TOKEN"', result.content.decode('utf-8'))
|
assert re.search(r'\bcsrfToken: "TOKEN"', result.content.decode())
|
||||||
|
|
||||||
|
|
||||||
def test_base_template_with_no_context():
|
def test_base_template_with_no_context():
|
||||||
|
@ -14,4 +14,4 @@ def test_base_template_with_no_context():
|
||||||
# so it can be easily extended.
|
# so it can be easily extended.
|
||||||
result = render({}, 'rest_framework/base.html')
|
result = render({}, 'rest_framework/base.html')
|
||||||
# note that this response will not include a valid CSRF token
|
# note that this response will not include a valid CSRF token
|
||||||
assert re.search(r'\bcsrfToken: ""', result.content.decode('utf-8'))
|
assert re.search(r'\bcsrfToken: ""', result.content.decode())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user