Fix OpenAPI Schema yaml rendering for timedelta (#9007)

* fix OpenAPIRenderer for timedelta

* added test for rendering openapi with timedelta

* fix OpenAPIRenderer for timedelta

* added test for rendering openapi with timedelta

* Removed usage of field.choices that triggered full table load (#8950)

Removed the `{{ field.choices|yesno:",disabled" }}` block because this triggers the loading of full database table worth of objects just to determine whether the multi-select widget should be set as disabled or not.

Since this "disabled" marking feature is not present in the normal select field, then I propose to remove it also from the multi-select.

* Added Deprecation Warnings for CoreAPI (#7519)

* Added Deprecation Warnings for CoreAPI

* Bumped removal to DRF315

* Update rest_framework/__init__.py

* Update rest_framework/filters.py

* Update rest_framework/filters.py

* Update tests/schemas/test_coreapi.py

* Update rest_framework/filters.py

* Update rest_framework/filters.py

* Update tests/schemas/test_coreapi.py

* Update tests/schemas/test_coreapi.py

* Update setup.cfg

* Update rest_framework/pagination.py

---------

Co-authored-by: Asif Saif Uddin <auvipy@gmail.com>

* Update copy right timeline

* Fix NamespaceVersioning ignoring DEFAULT_VERSION on non-None namespaces (#7278)

* Fix the case where if the namespace is not None and there's no match,
  NamespaceVersioning always raises NotFound even if DEFAULT_VERSION
  is set or None is in ALLOWED_VERSIONS

* Add test cases

* fix OpenAPIRenderer for timedelta

* added test for rendering openapi with timedelta

* added testcase for rendering yaml with minvalidator for duration field (timedelta)

---------

Co-authored-by: Rizwan Shaikh <rshaikh@ces-ltd.com>
Co-authored-by: Lenno Nagel <lenno@namespace.ee>
Co-authored-by: David Smith <39445562+smithdc1@users.noreply.github.com>
Co-authored-by: Asif Saif Uddin <auvipy@gmail.com>
Co-authored-by: Konstantin Kuchkov <konstantin.kuchkov@gmail.com>
This commit is contained in:
rizwanshaikh 2023-06-17 08:48:25 +05:30 committed by GitHub
parent 71f87a5864
commit 9cfa4bd7cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 0 deletions

View File

@ -9,6 +9,7 @@ REST framework also provides an HTML renderer that renders the browsable API.
import base64 import base64
import contextlib import contextlib
import datetime
from urllib import parse from urllib import parse
from django import forms from django import forms
@ -1062,6 +1063,7 @@ class OpenAPIRenderer(BaseRenderer):
def ignore_aliases(self, data): def ignore_aliases(self, data):
return True return True
Dumper.add_representer(SafeString, Dumper.represent_str) Dumper.add_representer(SafeString, Dumper.represent_str)
Dumper.add_representer(datetime.timedelta, encoders.CustomScalar.represent_timedelta)
return yaml.dump(data, default_flow_style=False, sort_keys=False, Dumper=Dumper).encode('utf-8') return yaml.dump(data, default_flow_style=False, sort_keys=False, Dumper=Dumper).encode('utf-8')

View File

@ -65,3 +65,14 @@ class JSONEncoder(json.JSONEncoder):
elif hasattr(obj, '__iter__'): elif hasattr(obj, '__iter__'):
return tuple(item for item in obj) return tuple(item for item in obj)
return super().default(obj) return super().default(obj)
class CustomScalar:
"""
CustomScalar that knows how to encode timedelta that renderer
can understand.
"""
@classmethod
def represent_timedelta(cls, dumper, data):
value = str(data.total_seconds())
return dumper.represent_scalar('tag:yaml.org,2002:str', value)

View File

@ -1162,6 +1162,31 @@ class TestGenerator(TestCase):
assert b'"openapi": "' in ret assert b'"openapi": "' in ret
assert b'"default": "0.0"' in ret assert b'"default": "0.0"' in ret
def test_schema_rendering_to_yaml(self):
patterns = [
path('example/', views.ExampleGenericAPIView.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
request = create_request('/')
schema = generator.get_schema(request=request)
ret = OpenAPIRenderer().render(schema)
assert b"openapi: " in ret
assert b"default: '0.0'" in ret
def test_schema_rendering_timedelta_to_yaml_with_validator(self):
patterns = [
path('example/', views.ExampleValidatedAPIView.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
request = create_request('/')
schema = generator.get_schema(request=request)
ret = OpenAPIRenderer().render(schema)
assert b"openapi: " in ret
assert b"duration:\n type: string\n minimum: \'10.0\'\n" in ret
def test_schema_with_no_paths(self): def test_schema_with_no_paths(self):
patterns = [] patterns = []
generator = SchemaGenerator(patterns=patterns) generator = SchemaGenerator(patterns=patterns)

View File

@ -134,6 +134,11 @@ class ExampleValidatedSerializer(serializers.Serializer):
ip4 = serializers.IPAddressField(protocol='ipv4') ip4 = serializers.IPAddressField(protocol='ipv4')
ip6 = serializers.IPAddressField(protocol='ipv6') ip6 = serializers.IPAddressField(protocol='ipv6')
ip = serializers.IPAddressField() ip = serializers.IPAddressField()
duration = serializers.DurationField(
validators=(
MinValueValidator(timedelta(seconds=10)),
)
)
class ExampleValidatedAPIView(generics.GenericAPIView): class ExampleValidatedAPIView(generics.GenericAPIView):