Skip to content

Commit 9cfa4bd

Browse files
rnshaikhRizwan Shaikhlnagelsmithdc1auvipy
authored
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 <[email protected]> * 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 <[email protected]> Co-authored-by: Lenno Nagel <[email protected]> Co-authored-by: David Smith <[email protected]> Co-authored-by: Asif Saif Uddin <[email protected]> Co-authored-by: Konstantin Kuchkov <[email protected]>
1 parent 71f87a5 commit 9cfa4bd

File tree

4 files changed

+43
-0
lines changed

4 files changed

+43
-0
lines changed

rest_framework/renderers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import base64
1111
import contextlib
12+
import datetime
1213
from urllib import parse
1314

1415
from django import forms
@@ -1062,6 +1063,7 @@ class Dumper(yaml.Dumper):
10621063
def ignore_aliases(self, data):
10631064
return True
10641065
Dumper.add_representer(SafeString, Dumper.represent_str)
1066+
Dumper.add_representer(datetime.timedelta, encoders.CustomScalar.represent_timedelta)
10651067
return yaml.dump(data, default_flow_style=False, sort_keys=False, Dumper=Dumper).encode('utf-8')
10661068

10671069

rest_framework/utils/encoders.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,14 @@ def default(self, obj):
6565
elif hasattr(obj, '__iter__'):
6666
return tuple(item for item in obj)
6767
return super().default(obj)
68+
69+
70+
class CustomScalar:
71+
"""
72+
CustomScalar that knows how to encode timedelta that renderer
73+
can understand.
74+
"""
75+
@classmethod
76+
def represent_timedelta(cls, dumper, data):
77+
value = str(data.total_seconds())
78+
return dumper.represent_scalar('tag:yaml.org,2002:str', value)

tests/schemas/test_openapi.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,31 @@ def test_schema_rendering_to_json(self):
11621162
assert b'"openapi": "' in ret
11631163
assert b'"default": "0.0"' in ret
11641164

1165+
def test_schema_rendering_to_yaml(self):
1166+
patterns = [
1167+
path('example/', views.ExampleGenericAPIView.as_view()),
1168+
]
1169+
generator = SchemaGenerator(patterns=patterns)
1170+
1171+
request = create_request('/')
1172+
schema = generator.get_schema(request=request)
1173+
ret = OpenAPIRenderer().render(schema)
1174+
assert b"openapi: " in ret
1175+
assert b"default: '0.0'" in ret
1176+
1177+
def test_schema_rendering_timedelta_to_yaml_with_validator(self):
1178+
1179+
patterns = [
1180+
path('example/', views.ExampleValidatedAPIView.as_view()),
1181+
]
1182+
generator = SchemaGenerator(patterns=patterns)
1183+
1184+
request = create_request('/')
1185+
schema = generator.get_schema(request=request)
1186+
ret = OpenAPIRenderer().render(schema)
1187+
assert b"openapi: " in ret
1188+
assert b"duration:\n type: string\n minimum: \'10.0\'\n" in ret
1189+
11651190
def test_schema_with_no_paths(self):
11661191
patterns = []
11671192
generator = SchemaGenerator(patterns=patterns)

tests/schemas/views.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ class ExampleValidatedSerializer(serializers.Serializer):
134134
ip4 = serializers.IPAddressField(protocol='ipv4')
135135
ip6 = serializers.IPAddressField(protocol='ipv6')
136136
ip = serializers.IPAddressField()
137+
duration = serializers.DurationField(
138+
validators=(
139+
MinValueValidator(timedelta(seconds=10)),
140+
)
141+
)
137142

138143

139144
class ExampleValidatedAPIView(generics.GenericAPIView):

0 commit comments

Comments
 (0)