mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-05-21 04:56:11 +03:00
Merge branch 'encode:master' into master
This commit is contained in:
commit
68a5d5f176
|
@ -31,3 +31,9 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
exclude: locale|kickstarter-announcement.md|coreapi-0.1.1.js
|
exclude: locale|kickstarter-announcement.md|coreapi-0.1.1.js
|
||||||
|
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v3.19.1
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args: ["--py39-plus", "--keep-percent-format"]
|
||||||
|
|
|
@ -42,4 +42,4 @@ class Command(BaseCommand):
|
||||||
username)
|
username)
|
||||||
)
|
)
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
'Generated token {} for user {}'.format(token.key, username))
|
f'Generated token {token.key} for user {username}')
|
||||||
|
|
|
@ -111,7 +111,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 "{}"; original exception was: {}'.format(attr, exc))
|
raise ValueError(f'Exception raised in callable attribute "{attr}"; original exception was: {exc}')
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@ -1103,7 +1103,7 @@ class DecimalField(Field):
|
||||||
if self.localize:
|
if self.localize:
|
||||||
return localize_input(quantized)
|
return localize_input(quantized)
|
||||||
|
|
||||||
return '{:f}'.format(quantized)
|
return f'{quantized:f}'
|
||||||
|
|
||||||
def quantize(self, value):
|
def quantize(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -1861,7 +1861,7 @@ class SerializerMethodField(Field):
|
||||||
def bind(self, field_name, parent):
|
def bind(self, field_name, parent):
|
||||||
# The method name defaults to `get_{field_name}`.
|
# The method name defaults to `get_{field_name}`.
|
||||||
if self.method_name is None:
|
if self.method_name is None:
|
||||||
self.method_name = 'get_{field_name}'.format(field_name=field_name)
|
self.method_name = f'get_{field_name}'
|
||||||
|
|
||||||
super().bind(field_name, parent)
|
super().bind(field_name, parent)
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ class DefaultContentNegotiation(BaseContentNegotiation):
|
||||||
full_media_type = ';'.join(
|
full_media_type = ';'.join(
|
||||||
(renderer.media_type,) +
|
(renderer.media_type,) +
|
||||||
tuple(
|
tuple(
|
||||||
'{}={}'.format(key, value)
|
f'{key}={value}'
|
||||||
for key, value in media_type_wrapper.params.items()
|
for key, value in media_type_wrapper.params.items()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -225,7 +225,7 @@ class DjangoModelPermissions(BasePermission):
|
||||||
if hasattr(view, 'get_queryset'):
|
if hasattr(view, 'get_queryset'):
|
||||||
queryset = view.get_queryset()
|
queryset = view.get_queryset()
|
||||||
assert queryset is not None, (
|
assert queryset is not None, (
|
||||||
'{}.get_queryset() returned None'.format(view.__class__.__name__)
|
f'{view.__class__.__name__}.get_queryset() returned None'
|
||||||
)
|
)
|
||||||
return queryset
|
return queryset
|
||||||
return view.queryset
|
return view.queryset
|
||||||
|
|
|
@ -66,7 +66,7 @@ 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 = "{}; charset={}".format(media_type, charset)
|
content_type = f"{media_type}; charset={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
|
||||||
|
|
|
@ -68,7 +68,7 @@ class LinkNode(dict):
|
||||||
current_val = self.methods_counter[preferred_key]
|
current_val = self.methods_counter[preferred_key]
|
||||||
self.methods_counter[preferred_key] += 1
|
self.methods_counter[preferred_key] += 1
|
||||||
|
|
||||||
key = '{}_{}'.format(preferred_key, current_val)
|
key = f'{preferred_key}_{current_val}'
|
||||||
if key not in self:
|
if key not in self:
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ class SchemaGenerator(BaseSchemaGenerator):
|
||||||
continue
|
continue
|
||||||
if components_schemas[k] == components[k]:
|
if components_schemas[k] == components[k]:
|
||||||
continue
|
continue
|
||||||
warnings.warn('Schema component "{}" has been overridden with a different value.'.format(k))
|
warnings.warn(f'Schema component "{k}" has been overridden with a different value.')
|
||||||
|
|
||||||
components_schemas.update(components)
|
components_schemas.update(components)
|
||||||
|
|
||||||
|
@ -644,7 +644,7 @@ class AutoSchema(ViewInspector):
|
||||||
return self.get_serializer(path, method)
|
return self.get_serializer(path, method)
|
||||||
|
|
||||||
def get_reference(self, serializer):
|
def get_reference(self, serializer):
|
||||||
return {'$ref': '#/components/schemas/{}'.format(self.get_component_name(serializer))}
|
return {'$ref': f'#/components/schemas/{self.get_component_name(serializer)}'}
|
||||||
|
|
||||||
def get_request_body(self, path, method):
|
def get_request_body(self, path, method):
|
||||||
if method not in ('PUT', 'PATCH', 'POST'):
|
if method not in ('PUT', 'PATCH', 'POST'):
|
||||||
|
|
|
@ -66,7 +66,7 @@ def get_unique_validators(field_name, model_field):
|
||||||
"""
|
"""
|
||||||
Returns a list of UniqueValidators that should be applied to the field.
|
Returns a list of UniqueValidators that should be applied to the field.
|
||||||
"""
|
"""
|
||||||
field_set = set([field_name])
|
field_set = {field_name}
|
||||||
conditions = {
|
conditions = {
|
||||||
c.condition
|
c.condition
|
||||||
for c in model_field.model._meta.constraints
|
for c in model_field.model._meta.constraints
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
import sys
|
|
||||||
from collections.abc import Mapping, MutableMapping
|
from collections.abc import Mapping, MutableMapping
|
||||||
|
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
|
@ -29,21 +28,20 @@ class ReturnDict(dict):
|
||||||
# but preserve the raw data.
|
# but preserve the raw data.
|
||||||
return (dict, (dict(self),))
|
return (dict, (dict(self),))
|
||||||
|
|
||||||
if sys.version_info >= (3, 9):
|
# These are basically copied from OrderedDict, with `serializer` added.
|
||||||
# These are basically copied from OrderedDict, with `serializer` added.
|
def __or__(self, other):
|
||||||
def __or__(self, other):
|
if not isinstance(other, dict):
|
||||||
if not isinstance(other, dict):
|
return NotImplemented
|
||||||
return NotImplemented
|
new = self.__class__(self, serializer=self.serializer)
|
||||||
new = self.__class__(self, serializer=self.serializer)
|
new.update(other)
|
||||||
new.update(other)
|
return new
|
||||||
return new
|
|
||||||
|
|
||||||
def __ror__(self, other):
|
def __ror__(self, other):
|
||||||
if not isinstance(other, dict):
|
if not isinstance(other, dict):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
new = self.__class__(other, serializer=self.serializer)
|
new = self.__class__(other, serializer=self.serializer)
|
||||||
new.update(self)
|
new.update(self)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
|
||||||
class ReturnList(list):
|
class ReturnList(list):
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -81,7 +81,7 @@ setup(
|
||||||
author_email='tom@tomchristie.com', # SEE NOTE BELOW (*)
|
author_email='tom@tomchristie.com', # SEE NOTE BELOW (*)
|
||||||
packages=find_packages(exclude=['tests*']),
|
packages=find_packages(exclude=['tests*']),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=["django>=4.2", 'backports.zoneinfo;python_version<"3.9"'],
|
install_requires=["django>=4.2"],
|
||||||
python_requires=">=3.9",
|
python_requires=">=3.9",
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
|
|
@ -1234,7 +1234,7 @@ class TestURLNamingCollisions(TestCase):
|
||||||
|
|
||||||
for method, suffix in zip(methods, suffixes):
|
for method, suffix in zip(methods, suffixes):
|
||||||
if suffix is not None:
|
if suffix is not None:
|
||||||
key = '{}_{}'.format(method, suffix)
|
key = f'{method}_{suffix}'
|
||||||
else:
|
else:
|
||||||
key = method
|
key = method
|
||||||
assert loc[key].url == url
|
assert loc[key].url == url
|
||||||
|
|
|
@ -70,7 +70,7 @@ class GenerateSchemaTests(TestCase):
|
||||||
|
|
||||||
def test_accepts_custom_schema_generator(self):
|
def test_accepts_custom_schema_generator(self):
|
||||||
call_command('generateschema',
|
call_command('generateschema',
|
||||||
'--generator_class={}.{}'.format(__name__, CustomSchemaGenerator.__name__),
|
f'--generator_class={__name__}.{CustomSchemaGenerator.__name__}',
|
||||||
stdout=self.out)
|
stdout=self.out)
|
||||||
out_json = yaml.safe_load(self.out.getvalue())
|
out_json = yaml.safe_load(self.out.getvalue())
|
||||||
assert out_json == CustomSchemaGenerator.SCHEMA
|
assert out_json == CustomSchemaGenerator.SCHEMA
|
||||||
|
@ -78,7 +78,7 @@ class GenerateSchemaTests(TestCase):
|
||||||
def test_writes_schema_to_file_on_parameter(self):
|
def test_writes_schema_to_file_on_parameter(self):
|
||||||
fd, path = tempfile.mkstemp()
|
fd, path = tempfile.mkstemp()
|
||||||
try:
|
try:
|
||||||
call_command('generateschema', '--file={}'.format(path), stdout=self.out)
|
call_command('generateschema', f'--file={path}', stdout=self.out)
|
||||||
# nothing on stdout
|
# nothing on stdout
|
||||||
assert not self.out.getvalue()
|
assert not self.out.getvalue()
|
||||||
|
|
||||||
|
|
|
@ -43,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),
|
'message': f'Request was throttled. Expected available in {2} seconds.',
|
||||||
'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),
|
'message': f'Slow down! Expected available in {2} seconds.',
|
||||||
'code': 'throttled'}
|
'code': 'throttled'}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@ import datetime
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
from decimal import ROUND_DOWN, ROUND_UP, Decimal
|
from decimal import ROUND_DOWN, ROUND_UP, Decimal
|
||||||
from enum import auto
|
from enum import auto
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -30,11 +30,6 @@ from rest_framework.fields import (
|
||||||
)
|
)
|
||||||
from tests.models import UUIDForeignKeyTarget
|
from tests.models import UUIDForeignKeyTarget
|
||||||
|
|
||||||
if sys.version_info >= (3, 9):
|
|
||||||
from zoneinfo import ZoneInfo
|
|
||||||
else:
|
|
||||||
from backports.zoneinfo import ZoneInfo
|
|
||||||
|
|
||||||
utc = datetime.timezone.utc
|
utc = datetime.timezone.utc
|
||||||
|
|
||||||
# Tests for helper functions.
|
# Tests for helper functions.
|
||||||
|
@ -641,10 +636,6 @@ class Test5087Regression:
|
||||||
|
|
||||||
|
|
||||||
class TestTyping(TestCase):
|
class TestTyping(TestCase):
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7),
|
|
||||||
reason="subscriptable classes requires Python 3.7 or higher",
|
|
||||||
)
|
|
||||||
def test_field_is_subscriptable(self):
|
def test_field_is_subscriptable(self):
|
||||||
assert serializers.Field is serializers.Field["foo"]
|
assert serializers.Field is serializers.Field["foo"]
|
||||||
|
|
||||||
|
@ -671,7 +662,7 @@ class FieldValues:
|
||||||
"""
|
"""
|
||||||
for input_value, expected_output in get_items(self.valid_inputs):
|
for input_value, expected_output in get_items(self.valid_inputs):
|
||||||
assert self.field.run_validation(input_value) == expected_output, \
|
assert self.field.run_validation(input_value) == expected_output, \
|
||||||
'input value: {}'.format(repr(input_value))
|
f'input value: {repr(input_value)}'
|
||||||
|
|
||||||
def test_invalid_inputs(self, *args):
|
def test_invalid_inputs(self, *args):
|
||||||
"""
|
"""
|
||||||
|
@ -681,12 +672,12 @@ class FieldValues:
|
||||||
with pytest.raises(serializers.ValidationError) as exc_info:
|
with pytest.raises(serializers.ValidationError) as exc_info:
|
||||||
self.field.run_validation(input_value)
|
self.field.run_validation(input_value)
|
||||||
assert exc_info.value.detail == expected_failure, \
|
assert exc_info.value.detail == expected_failure, \
|
||||||
'input value: {}'.format(repr(input_value))
|
f'input value: {repr(input_value)}'
|
||||||
|
|
||||||
def test_outputs(self, *args):
|
def test_outputs(self, *args):
|
||||||
for output_value, expected_output in get_items(self.outputs):
|
for output_value, expected_output in get_items(self.outputs):
|
||||||
assert self.field.to_representation(output_value) == expected_output, \
|
assert self.field.to_representation(output_value) == expected_output, \
|
||||||
'output value: {}'.format(repr(output_value))
|
f'output value: {repr(output_value)}'
|
||||||
|
|
||||||
|
|
||||||
# Boolean types...
|
# Boolean types...
|
||||||
|
@ -1431,7 +1422,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',
|
||||||
str('2016-01-10'): '2016-01-10',
|
'2016-01-10': '2016-01-10',
|
||||||
None: None,
|
None: None,
|
||||||
'': None,
|
'': None,
|
||||||
}
|
}
|
||||||
|
@ -1498,7 +1489,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',
|
||||||
str('2016-01-10T00:00:00'): '2016-01-10T00:00:00',
|
'2016-01-10T00:00:00': '2016-01-10T00:00:00',
|
||||||
None: None,
|
None: None,
|
||||||
'': None,
|
'': None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import sys
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
@ -291,7 +289,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('/{}'.format(filtered_out_pk), data, format='json')
|
request = factory.put(f'/{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
|
||||||
|
|
||||||
|
@ -703,23 +701,11 @@ class TestSerializer(TestCase):
|
||||||
|
|
||||||
|
|
||||||
class TestTyping(TestCase):
|
class TestTyping(TestCase):
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7),
|
|
||||||
reason="subscriptable classes requires Python 3.7 or higher",
|
|
||||||
)
|
|
||||||
def test_genericview_is_subscriptable(self):
|
def test_genericview_is_subscriptable(self):
|
||||||
assert generics.GenericAPIView is generics.GenericAPIView["foo"]
|
assert generics.GenericAPIView is generics.GenericAPIView["foo"]
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7),
|
|
||||||
reason="subscriptable classes requires Python 3.7 or higher",
|
|
||||||
)
|
|
||||||
def test_listview_is_subscriptable(self):
|
def test_listview_is_subscriptable(self):
|
||||||
assert generics.ListAPIView is generics.ListAPIView["foo"]
|
assert generics.ListAPIView is generics.ListAPIView["foo"]
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7),
|
|
||||||
reason="subscriptable classes requires Python 3.7 or higher",
|
|
||||||
)
|
|
||||||
def test_instanceview_is_subscriptable(self):
|
def test_instanceview_is_subscriptable(self):
|
||||||
assert generics.RetrieveAPIView is generics.RetrieveAPIView["foo"]
|
assert generics.RetrieveAPIView is generics.RetrieveAPIView["foo"]
|
||||||
|
|
|
@ -9,7 +9,6 @@ import datetime
|
||||||
import decimal
|
import decimal
|
||||||
import json # noqa
|
import json # noqa
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -397,10 +396,6 @@ class TestDurationFieldMapping(TestCase):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
expected = dedent("""
|
expected = dedent("""
|
||||||
TestSerializer():
|
|
||||||
id = IntegerField(label='ID', read_only=True)
|
|
||||||
duration_field = DurationField(max_value=datetime.timedelta(3), min_value=datetime.timedelta(1))
|
|
||||||
""") if sys.version_info < (3, 7) else dedent("""
|
|
||||||
TestSerializer():
|
TestSerializer():
|
||||||
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))
|
||||||
|
|
|
@ -536,7 +536,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={}&offset={}'.format(next_limit, next_offset)
|
next_url = f'http://testserver/?limit={next_limit}&offset={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
|
||||||
|
|
||||||
|
@ -549,7 +549,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={}&offset={}'.format(next_limit, next_offset)
|
next_url = f'http://testserver/?limit={next_limit}&offset={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
|
||||||
|
|
||||||
|
@ -565,9 +565,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={}'.format(max_limit)
|
base_url = f'http://testserver/?limit={max_limit}'
|
||||||
next_url = base_url + '&offset={}'.format(next_offset)
|
next_url = base_url + f'&offset={next_offset}'
|
||||||
prev_url = base_url + '&offset={}'.format(prev_offset)
|
prev_url = base_url + f'&offset={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
|
||||||
|
|
|
@ -353,7 +353,7 @@ class ObjectPermissionsIntegrationTests(TestCase):
|
||||||
'delete': f('delete', model_name)
|
'delete': f('delete', model_name)
|
||||||
}
|
}
|
||||||
for perm in perms.values():
|
for perm in perms.values():
|
||||||
perm = '{}.{}'.format(app_label, perm)
|
perm = f'{app_label}.{perm}'
|
||||||
assign_perm(perm, everyone)
|
assign_perm(perm, everyone)
|
||||||
everyone.user_set.add(*users.values())
|
everyone.user_set.add(*users.values())
|
||||||
|
|
||||||
|
@ -718,7 +718,7 @@ class PermissionsCompositionTests(TestCase):
|
||||||
assert hasperm is False
|
assert hasperm is False
|
||||||
|
|
||||||
def test_operand_holder_is_hashable(self):
|
def test_operand_holder_is_hashable(self):
|
||||||
assert hash((permissions.IsAuthenticated & permissions.IsAdminUser))
|
assert hash(permissions.IsAuthenticated & permissions.IsAdminUser)
|
||||||
|
|
||||||
def test_operand_holder_hash_same_for_same_operands_and_operator(self):
|
def test_operand_holder_hash_same_for_same_operands_and_operator(self):
|
||||||
first_operand_holder = (
|
first_operand_holder = (
|
||||||
|
|
|
@ -586,7 +586,7 @@ class OneToOnePrimaryKeyTests(TestCase):
|
||||||
source = OneToOnePKSourceSerializer(data={'name': 'source-2', 'target': target_pk})
|
source = OneToOnePKSourceSerializer(data={'name': 'source-2', 'target': target_pk})
|
||||||
# Then: The source is valid with the serializer
|
# Then: The source is valid with the serializer
|
||||||
if not source.is_valid():
|
if not source.is_valid():
|
||||||
self.fail("Expected OneToOnePKTargetSerializer to be valid but had errors: {}".format(source.errors))
|
self.fail(f"Expected OneToOnePKTargetSerializer to be valid but had errors: {source.errors}")
|
||||||
# Then: Saving the serializer creates a new object
|
# Then: Saving the serializer creates a new object
|
||||||
new_source = source.save()
|
new_source = source.save()
|
||||||
# Then: The new object has the same pk as the target object
|
# Then: The new object has the same pk as the target object
|
||||||
|
|
|
@ -408,7 +408,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())
|
self.assertEqual(content, b'{"should_escape":"\\u2028\\u2029"}')
|
||||||
|
|
||||||
|
|
||||||
class AsciiJSONRendererTests(TestCase):
|
class AsciiJSONRendererTests(TestCase):
|
||||||
|
@ -421,7 +421,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())
|
self.assertEqual(content, b'{"countries":["United Kingdom","France","Espa\\u00f1a"]}')
|
||||||
|
|
||||||
|
|
||||||
# Tests for caching issue, #346
|
# Tests for caching issue, #346
|
||||||
|
|
|
@ -3,7 +3,6 @@ Tests for content parsing, and form-overloaded content parsing.
|
||||||
"""
|
"""
|
||||||
import copy
|
import copy
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -375,9 +374,5 @@ class TestDeepcopy(TestCase):
|
||||||
|
|
||||||
|
|
||||||
class TestTyping(TestCase):
|
class TestTyping(TestCase):
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7),
|
|
||||||
reason="subscriptable classes requires Python 3.7 or higher",
|
|
||||||
)
|
|
||||||
def test_request_is_subscriptable(self):
|
def test_request_is_subscriptable(self):
|
||||||
assert Request is Request["foo"]
|
assert Request is Request["foo"]
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import sys
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.urls import include, path, re_path
|
from django.urls import include, path, re_path
|
||||||
|
|
||||||
|
@ -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 = "{}; charset={}".format(RendererC.media_type, RendererC.charset)
|
expected = f"{RendererC.media_type}; charset={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):
|
||||||
|
@ -289,9 +286,5 @@ class Issue807Tests(TestCase):
|
||||||
|
|
||||||
|
|
||||||
class TestTyping(TestCase):
|
class TestTyping(TestCase):
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7),
|
|
||||||
reason="subscriptable classes requires Python 3.7 or higher",
|
|
||||||
)
|
|
||||||
def test_response_is_subscriptable(self):
|
def test_response_is_subscriptable(self):
|
||||||
assert Response is Response["foo"]
|
assert Response is Response["foo"]
|
||||||
|
|
|
@ -447,9 +447,9 @@ class TestDynamicListAndDetailRouter(TestCase):
|
||||||
url_path = endpoint.url_path
|
url_path = endpoint.url_path
|
||||||
|
|
||||||
if method_name.startswith('list_'):
|
if method_name.startswith('list_'):
|
||||||
assert route.url == '^{{prefix}}/{0}{{trailing_slash}}$'.format(url_path)
|
assert route.url == f'^{{prefix}}/{url_path}{{trailing_slash}}$'
|
||||||
else:
|
else:
|
||||||
assert route.url == '^{{prefix}}/{{lookup}}/{0}{{trailing_slash}}$'.format(url_path)
|
assert route.url == f'^{{prefix}}/{{lookup}}/{url_path}{{trailing_slash}}$'
|
||||||
# check method to function mapping
|
# check method to function mapping
|
||||||
if method_name.endswith('_post'):
|
if method_name.endswith('_post'):
|
||||||
method_map = 'post'
|
method_map = 'post'
|
||||||
|
@ -488,14 +488,14 @@ class TestRegexUrlPath(URLPatternsTestCase, TestCase):
|
||||||
|
|
||||||
def test_regex_url_path_list(self):
|
def test_regex_url_path_list(self):
|
||||||
kwarg = '1234'
|
kwarg = '1234'
|
||||||
response = self.client.get('/regex/list/{}/'.format(kwarg))
|
response = self.client.get(f'/regex/list/{kwarg}/')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.content.decode()) == {'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(f'/regex/{pk}/detail/{kwarg}/')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.content.decode()) == {'pk': pk, 'kwarg': kwarg}
|
assert json.loads(response.content.decode()) == {'pk': pk, 'kwarg': kwarg}
|
||||||
|
|
||||||
|
@ -557,14 +557,14 @@ class TestUrlPath(URLPatternsTestCase, TestCase):
|
||||||
|
|
||||||
def test_list_extra_action(self):
|
def test_list_extra_action(self):
|
||||||
kwarg = 1234
|
kwarg = 1234
|
||||||
response = self.client.get('/path/list/{}/'.format(kwarg))
|
response = self.client.get(f'/path/list/{kwarg}/')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.content.decode()) == {'kwarg': kwarg}
|
assert json.loads(response.content.decode()) == {'kwarg': kwarg}
|
||||||
|
|
||||||
def test_detail_extra_action(self):
|
def test_detail_extra_action(self):
|
||||||
pk = '1'
|
pk = '1'
|
||||||
kwarg = 1234
|
kwarg = 1234
|
||||||
response = self.client.get('/path/{}/detail/{}/'.format(pk, kwarg))
|
response = self.client.get(f'/path/{pk}/detail/{kwarg}/')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.content.decode()) == {'pk': pk, 'kwarg': kwarg}
|
assert json.loads(response.content.decode()) == {'pk': pk, 'kwarg': kwarg}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import inspect
|
import inspect
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from collections import ChainMap
|
from collections import ChainMap
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
|
|
||||||
|
@ -205,10 +204,6 @@ class TestSerializer:
|
||||||
exceptions.ErrorDetail(string='Raised error', code='invalid')
|
exceptions.ErrorDetail(string='Raised error', code='invalid')
|
||||||
]}
|
]}
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7),
|
|
||||||
reason="subscriptable classes requires Python 3.7 or higher",
|
|
||||||
)
|
|
||||||
def test_serializer_is_subscriptable(self):
|
def test_serializer_is_subscriptable(self):
|
||||||
assert serializers.Serializer is serializers.Serializer["foo"]
|
assert serializers.Serializer is serializers.Serializer["foo"]
|
||||||
|
|
||||||
|
@ -743,10 +738,6 @@ class TestDeclaredFieldInheritance:
|
||||||
|
|
||||||
|
|
||||||
class Test8301Regression:
|
class Test8301Regression:
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 9),
|
|
||||||
reason="dictionary union operator requires Python 3.9 or higher",
|
|
||||||
)
|
|
||||||
def test_ReturnDict_merging(self):
|
def test_ReturnDict_merging(self):
|
||||||
# Serializer.data returns ReturnDict, this is essentially a test for that.
|
# Serializer.data returns ReturnDict, this is essentially a test for that.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import sys
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
|
@ -60,10 +58,6 @@ class TestListSerializer:
|
||||||
assert serializer.is_valid()
|
assert serializer.is_valid()
|
||||||
assert serializer.validated_data == expected_output
|
assert serializer.validated_data == expected_output
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7),
|
|
||||||
reason="subscriptable classes requires Python 3.7 or higher",
|
|
||||||
)
|
|
||||||
def test_list_serializer_is_subscriptable(self):
|
def test_list_serializer_is_subscriptable(self):
|
||||||
assert serializers.ListSerializer is serializers.ListSerializer["foo"]
|
assert serializers.ListSerializer is serializers.ListSerializer["foo"]
|
||||||
|
|
||||||
|
|
|
@ -148,14 +148,14 @@ class TestMaxValueValidatorValidation(TestCase):
|
||||||
|
|
||||||
def test_max_value_validation_success(self):
|
def test_max_value_validation_success(self):
|
||||||
obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
|
obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
|
||||||
request = factory.patch('/{}'.format(obj.pk), {'number_value': 98}, format='json')
|
request = factory.patch(f'/{obj.pk}', {'number_value': 98}, format='json')
|
||||||
view = UpdateMaxValueValidationModel().as_view()
|
view = UpdateMaxValueValidationModel().as_view()
|
||||||
response = view(request, pk=obj.pk).render()
|
response = view(request, pk=obj.pk).render()
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
||||||
def test_max_value_validation_fail(self):
|
def test_max_value_validation_fail(self):
|
||||||
obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
|
obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
|
||||||
request = factory.patch('/{}'.format(obj.pk), {'number_value': 101}, format='json')
|
request = factory.patch(f'/{obj.pk}', {'number_value': 101}, format='json')
|
||||||
view = UpdateMaxValueValidationModel().as_view()
|
view = UpdateMaxValueValidationModel().as_view()
|
||||||
response = view(request, pk=obj.pk).render()
|
response = view(request, pk=obj.pk).render()
|
||||||
assert response.content == b'{"number_value":["Ensure this value is less than or equal to 100."]}'
|
assert response.content == b'{"number_value":["Ensure this value is less than or equal to 100."]}'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user