From bcf196d0ac14a1b6a8d28eda2e8c26711f717d99 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 29 Jul 2019 15:10:57 -0700 Subject: [PATCH 01/79] Remove outdated note about read_only+default (#6847) --- docs/api-guide/validators.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index 93cb1ffa55..87417b7f15 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -152,8 +152,6 @@ If you want the date field to be visible, but not editable by the user, then set published = serializers.DateTimeField(read_only=True, default=timezone.now) -The field will not be writable to the user, but the default value will still be passed through to the `validated_data`. - #### Using with a hidden date field. If you want the date field to be entirely hidden from the user, then use `HiddenField`. This field type does not accept user input, but instead always returns its default value to the `validated_data` in the serializer. From 0cc09f0c0dbe4a6552b1a5bbaa4f7f921270698a Mon Sep 17 00:00:00 2001 From: Greg Schmit <14255284+gregschmit@users.noreply.github.com> Date: Tue, 30 Jul 2019 14:37:49 -0500 Subject: [PATCH 02/79] Add drf-action-sreializer to third party packages (#6845) --- docs/community/third-party-packages.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index 54dec7f5e0..0f8bdc4a48 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -210,6 +210,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque Enables black/whitelisting fields, and conditionally expanding child serializers on a per-view/request basis. * [djangorestframework-queryfields][djangorestframework-queryfields] - Serializer mixin allowing clients to control which fields will be sent in the API response. * [drf-flex-fields][drf-flex-fields] - Serializer providing dynamic field expansion and sparse field sets via URL parameters. +* [drf-action-serializer][drf-action-serializer] - Serializer providing per-action fields config for use with ViewSets to prevent having to write multiple serializers. ### Serializer fields @@ -346,5 +347,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [django-rest-witchcraft]: https://github.com/shosca/django-rest-witchcraft [drf-access-policy]: https://github.com/rsinger86/drf-access-policy [drf-flex-fields]: https://github.com/rsinger86/drf-flex-fields +[drf-action-serializer]: https://github.com/gregschmit/drf-action-serializer [djangorestframework-mvt]: https://github.com/corteva/djangorestframework-mvt [django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian From 7e1c4be7ced1fe242540c796d046d665f2a40350 Mon Sep 17 00:00:00 2001 From: zeroohub Date: Tue, 6 Aug 2019 18:31:46 +0800 Subject: [PATCH 03/79] Avoided double call for remote url in FileField.to_representation. (#6841) --- rest_framework/fields.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f5f0b632e5..0be6a7c125 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1546,16 +1546,16 @@ def to_representation(self, value): return None use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL) - if use_url: - if not getattr(value, 'url', None): - # If the file has not been saved it may not have a URL. + try: + url = value.url + except AttributeError: return None - url = value.url request = self.context.get('request', None) if request is not None: return request.build_absolute_uri(url) return url + return value.name From a3f244d85e9057aab5537832f70bf17f5ea4e5d6 Mon Sep 17 00:00:00 2001 From: Reupen Shah Date: Tue, 30 Jul 2019 20:42:23 +0100 Subject: [PATCH 04/79] Move AutoSchema serializer instantiation to common method --- rest_framework/schemas/openapi.py | 44 +++++++++++++------------------ 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 4050bc2600..32076eaa6b 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -419,23 +419,26 @@ def _map_field_validators(self, validators, schema): schema['maximum'] = int(digits * '9') + 1 schema['minimum'] = -schema['maximum'] - def _get_request_body(self, path, method): + def _get_serializer(self, method, path): view = self.view - if method not in ('PUT', 'PATCH', 'POST'): - return {} - if not hasattr(view, 'get_serializer'): - return {} + return None try: - serializer = view.get_serializer() + return view.get_serializer() except exceptions.APIException: - serializer = None warnings.warn('{}.get_serializer() raised an exception during ' 'schema generation. Serializer fields will not be ' 'generated for {} {}.' .format(view.__class__.__name__, method, path)) + return None + + def _get_request_body(self, path, method): + if method not in ('PUT', 'PATCH', 'POST'): + return {} + + serializer = self._get_serializer(path, method) if not isinstance(serializer, serializers.Serializer): return {} @@ -459,24 +462,15 @@ def _get_request_body(self, path, method): def _get_responses(self, path, method): # TODO: Handle multiple codes. content = {} - view = self.view - if hasattr(view, 'get_serializer'): - try: - serializer = view.get_serializer() - except exceptions.APIException: - serializer = None - warnings.warn('{}.get_serializer() raised an exception during ' - 'schema generation. Serializer fields will not be ' - 'generated for {} {}.' - .format(view.__class__.__name__, method, path)) - - if isinstance(serializer, serializers.Serializer): - content = self._map_serializer(serializer) - # No write_only fields for response. - for name, schema in content['properties'].copy().items(): - if 'writeOnly' in schema: - del content['properties'][name] - content['required'] = [f for f in content['required'] if f != name] + serializer = self._get_serializer(path, method) + + if isinstance(serializer, serializers.Serializer): + content = self._map_serializer(serializer) + # No write_only fields for response. + for name, schema in content['properties'].copy().items(): + if 'writeOnly' in schema: + del content['properties'][name] + content['required'] = [f for f in content['required'] if f != name] return { '200': { From b45ff072947d01917132ef6993bf1b7f46d9f2c8 Mon Sep 17 00:00:00 2001 From: Reupen Shah Date: Tue, 30 Jul 2019 21:19:43 +0100 Subject: [PATCH 05/79] Use an array type for list view response schemas This is the first part of #6846. Previously, the response schema for list views was an object representing a single item. However, list views return a list of items, and hence it should be an array. Further work will need to be done to support how pagination classes modify list responses. There should be no change for views not determined to be list views. --- rest_framework/schemas/openapi.py | 22 +++-- tests/schemas/test_openapi.py | 156 +++++++++++++++++++++++------- 2 files changed, 138 insertions(+), 40 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 32076eaa6b..ac828935f5 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -460,22 +460,30 @@ def _get_request_body(self, path, method): } def _get_responses(self, path, method): - # TODO: Handle multiple codes. - content = {} + # TODO: Handle multiple codes and pagination classes. + item_schema = {} serializer = self._get_serializer(path, method) if isinstance(serializer, serializers.Serializer): - content = self._map_serializer(serializer) + item_schema = self._map_serializer(serializer) # No write_only fields for response. - for name, schema in content['properties'].copy().items(): + for name, schema in item_schema['properties'].copy().items(): if 'writeOnly' in schema: - del content['properties'][name] - content['required'] = [f for f in content['required'] if f != name] + del item_schema['properties'][name] + item_schema['required'] = [f for f in item_schema['required'] if f != name] + + if is_list_view(path, method, self.view): + response_schema = { + 'type': 'array', + 'items': item_schema, + } + else: + response_schema = item_schema return { '200': { 'content': { - ct: {'schema': content} + ct: {'schema': response_schema} for ct in self.content_types } } diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index b5727400ba..9930621073 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -82,7 +82,18 @@ def test_path_without_parameters(self): assert operation == { 'operationId': 'ListExamples', 'parameters': [], - 'responses': {'200': {'content': {'application/json': {'schema': {}}}}}, + 'responses': { + '200': { + 'content': { + 'application/json': { + 'schema': { + 'type': 'array', + 'items': {}, + }, + }, + }, + }, + }, } def test_path_with_id_parameter(self): @@ -184,6 +195,83 @@ class View(generics.GenericAPIView): assert list(schema['properties']['nested']['properties'].keys()) == ['number'] assert schema['properties']['nested']['required'] == ['number'] + def test_list_response_body_generation(self): + """Test that an array schema is returned for list views.""" + path = '/' + method = 'GET' + + class ItemSerializer(serializers.Serializer): + text = serializers.CharField() + + class View(generics.GenericAPIView): + serializer_class = ItemSerializer + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + responses = inspector._get_responses(path, method) + assert responses == { + '200': { + 'content': { + 'application/json': { + 'schema': { + 'type': 'array', + 'items': { + 'properties': { + 'text': { + 'type': 'string', + }, + }, + 'required': ['text'], + }, + }, + }, + }, + }, + } + + def test_retrieve_response_body_generation(self): + """Test that a list of properties is returned for retrieve item views.""" + path = '/{id}/' + method = 'GET' + + class ItemSerializer(serializers.Serializer): + text = serializers.CharField() + + class View(generics.GenericAPIView): + serializer_class = ItemSerializer + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + responses = inspector._get_responses(path, method) + assert responses == { + '200': { + 'content': { + 'application/json': { + 'schema': { + 'properties': { + 'text': { + 'type': 'string', + }, + }, + 'required': ['text'], + }, + }, + }, + }, + } + def test_operation_id_generation(self): path = '/' method = 'GET' @@ -226,10 +314,11 @@ def test_serializer_datefield(self): inspector.view = view responses = inspector._get_responses(path, method) - response_schema = responses['200']['content']['application/json']['schema']['properties'] - assert response_schema['date']['type'] == response_schema['datetime']['type'] == 'string' - assert response_schema['date']['format'] == 'date' - assert response_schema['datetime']['format'] == 'date-time' + response_schema = responses['200']['content']['application/json']['schema'] + properties = response_schema['items']['properties'] + assert properties['date']['type'] == properties['datetime']['type'] == 'string' + assert properties['date']['format'] == 'date' + assert properties['datetime']['format'] == 'date-time' def test_serializer_validators(self): path = '/' @@ -243,45 +332,46 @@ def test_serializer_validators(self): inspector.view = view responses = inspector._get_responses(path, method) - response_schema = responses['200']['content']['application/json']['schema']['properties'] + response_schema = responses['200']['content']['application/json']['schema'] + properties = response_schema['items']['properties'] - assert response_schema['integer']['type'] == 'integer' - assert response_schema['integer']['maximum'] == 99 - assert response_schema['integer']['minimum'] == -11 + assert properties['integer']['type'] == 'integer' + assert properties['integer']['maximum'] == 99 + assert properties['integer']['minimum'] == -11 - assert response_schema['string']['minLength'] == 2 - assert response_schema['string']['maxLength'] == 10 + assert properties['string']['minLength'] == 2 + assert properties['string']['maxLength'] == 10 - assert response_schema['regex']['pattern'] == r'[ABC]12{3}' - assert response_schema['regex']['description'] == 'must have an A, B, or C followed by 1222' + assert properties['regex']['pattern'] == r'[ABC]12{3}' + assert properties['regex']['description'] == 'must have an A, B, or C followed by 1222' - assert response_schema['decimal1']['type'] == 'number' - assert response_schema['decimal1']['multipleOf'] == .01 - assert response_schema['decimal1']['maximum'] == 10000 - assert response_schema['decimal1']['minimum'] == -10000 + assert properties['decimal1']['type'] == 'number' + assert properties['decimal1']['multipleOf'] == .01 + assert properties['decimal1']['maximum'] == 10000 + assert properties['decimal1']['minimum'] == -10000 - assert response_schema['decimal2']['type'] == 'number' - assert response_schema['decimal2']['multipleOf'] == .0001 + assert properties['decimal2']['type'] == 'number' + assert properties['decimal2']['multipleOf'] == .0001 - assert response_schema['email']['type'] == 'string' - assert response_schema['email']['format'] == 'email' - assert response_schema['email']['default'] == 'foo@bar.com' + assert properties['email']['type'] == 'string' + assert properties['email']['format'] == 'email' + assert properties['email']['default'] == 'foo@bar.com' - assert response_schema['url']['type'] == 'string' - assert response_schema['url']['nullable'] is True - assert response_schema['url']['default'] == 'http://www.example.com' + assert properties['url']['type'] == 'string' + assert properties['url']['nullable'] is True + assert properties['url']['default'] == 'http://www.example.com' - assert response_schema['uuid']['type'] == 'string' - assert response_schema['uuid']['format'] == 'uuid' + assert properties['uuid']['type'] == 'string' + assert properties['uuid']['format'] == 'uuid' - assert response_schema['ip4']['type'] == 'string' - assert response_schema['ip4']['format'] == 'ipv4' + assert properties['ip4']['type'] == 'string' + assert properties['ip4']['format'] == 'ipv4' - assert response_schema['ip6']['type'] == 'string' - assert response_schema['ip6']['format'] == 'ipv6' + assert properties['ip6']['type'] == 'string' + assert properties['ip6']['format'] == 'ipv6' - assert response_schema['ip']['type'] == 'string' - assert 'format' not in response_schema['ip'] + assert properties['ip']['type'] == 'string' + assert 'format' not in properties['ip'] @pytest.mark.skipif(uritemplate is None, reason='uritemplate not installed.') From 335054a5d36b352a58286b303b608b6bf48152f8 Mon Sep 17 00:00:00 2001 From: "Gregory N. Schmit" Date: Mon, 29 Jul 2019 19:02:43 -0500 Subject: [PATCH 06/79] replace force_text with force_str --- rest_framework/exceptions.py | 16 ++++++------ rest_framework/filters.py | 14 +++++----- rest_framework/metadata.py | 6 ++--- rest_framework/pagination.py | 26 +++++++++---------- rest_framework/parsers.py | 8 +++--- rest_framework/schemas/coreapi.py | 10 +++---- rest_framework/schemas/openapi.py | 4 +-- rest_framework/templatetags/rest_framework.py | 4 +-- rest_framework/utils/encoders.py | 4 +-- rest_framework/utils/formatting.py | 4 +-- rest_framework/utils/representation.py | 4 +-- rest_framework/utils/serializer_helpers.py | 4 +-- 12 files changed, 52 insertions(+), 52 deletions(-) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index ba0c9458ab..345a405248 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -7,7 +7,7 @@ import math from django.http import JsonResponse -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ from django.utils.translation import ngettext @@ -36,7 +36,7 @@ def _get_error_details(data, default_code=None): return ReturnDict(ret, serializer=data.serializer) return ret - text = force_text(data) + text = force_str(data) code = getattr(data, 'code', default_code) return ErrorDetail(text, code) @@ -191,7 +191,7 @@ class MethodNotAllowed(APIException): def __init__(self, method, detail=None, code=None): if detail is None: - detail = force_text(self.default_detail).format(method=method) + detail = force_str(self.default_detail).format(method=method) super().__init__(detail, code) @@ -212,7 +212,7 @@ class UnsupportedMediaType(APIException): def __init__(self, media_type, detail=None, code=None): if detail is None: - detail = force_text(self.default_detail).format(media_type=media_type) + detail = force_str(self.default_detail).format(media_type=media_type) super().__init__(detail, code) @@ -225,14 +225,14 @@ class Throttled(APIException): def __init__(self, wait=None, detail=None, code=None): if detail is None: - detail = force_text(self.default_detail) + detail = force_str(self.default_detail) if wait is not None: wait = math.ceil(wait) detail = ' '.join(( detail, - force_text(ngettext(self.extra_detail_singular.format(wait=wait), - self.extra_detail_plural.format(wait=wait), - wait)))) + force_str(ngettext(self.extra_detail_singular.format(wait=wait), + self.extra_detail_plural.format(wait=wait), + wait)))) self.wait = wait super().__init__(detail, code) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index c0c708d26d..c15723ec3f 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -10,7 +10,7 @@ from django.db.models.constants import LOOKUP_SEP from django.db.models.sql.constants import ORDER_PATTERN from django.template import loader -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ from rest_framework.compat import coreapi, coreschema, distinct @@ -151,8 +151,8 @@ def get_schema_fields(self, view): required=False, location='query', schema=coreschema.String( - title=force_text(self.search_title), - description=force_text(self.search_description) + title=force_str(self.search_title), + description=force_str(self.search_description) ) ) ] @@ -163,7 +163,7 @@ def get_schema_operation_parameters(self, view): 'name': self.search_param, 'required': False, 'in': 'query', - 'description': force_text(self.search_description), + 'description': force_str(self.search_description), 'schema': { 'type': 'string', }, @@ -295,8 +295,8 @@ def get_schema_fields(self, view): required=False, location='query', schema=coreschema.String( - title=force_text(self.ordering_title), - description=force_text(self.ordering_description) + title=force_str(self.ordering_title), + description=force_str(self.ordering_description) ) ) ] @@ -307,7 +307,7 @@ def get_schema_operation_parameters(self, view): 'name': self.ordering_param, 'required': False, 'in': 'query', - 'description': force_text(self.ordering_description), + 'description': force_str(self.ordering_description), 'schema': { 'type': 'string', }, diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index 42442f91cb..8a44f2aad3 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -10,7 +10,7 @@ from django.core.exceptions import PermissionDenied from django.http import Http404 -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework import exceptions, serializers from rest_framework.request import clone_request @@ -130,7 +130,7 @@ def get_field_info(self, field): for attr in attrs: value = getattr(field, attr, None) if value is not None and value != '': - field_info[attr] = force_text(value, strings_only=True) + field_info[attr] = force_str(value, strings_only=True) if getattr(field, 'child', None): field_info['child'] = self.get_field_info(field.child) @@ -143,7 +143,7 @@ def get_field_info(self, field): field_info['choices'] = [ { 'value': choice_value, - 'display_name': force_text(choice_name, strings_only=True) + 'display_name': force_str(choice_name, strings_only=True) } for choice_value, choice_name in field.choices.items() ] diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 4d65d080a3..a68e8ab128 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -9,7 +9,7 @@ from django.core.paginator import InvalidPage from django.core.paginator import Paginator as DjangoPaginator from django.template import loader -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ from rest_framework.compat import coreapi, coreschema @@ -286,7 +286,7 @@ def get_schema_fields(self, view): location='query', schema=coreschema.Integer( title='Page', - description=force_text(self.page_query_description) + description=force_str(self.page_query_description) ) ) ] @@ -298,7 +298,7 @@ def get_schema_fields(self, view): location='query', schema=coreschema.Integer( title='Page size', - description=force_text(self.page_size_query_description) + description=force_str(self.page_size_query_description) ) ) ) @@ -310,7 +310,7 @@ def get_schema_operation_parameters(self, view): 'name': self.page_query_param, 'required': False, 'in': 'query', - 'description': force_text(self.page_query_description), + 'description': force_str(self.page_query_description), 'schema': { 'type': 'integer', }, @@ -322,7 +322,7 @@ def get_schema_operation_parameters(self, view): 'name': self.page_size_query_param, 'required': False, 'in': 'query', - 'description': force_text(self.page_size_query_description), + 'description': force_str(self.page_size_query_description), 'schema': { 'type': 'integer', }, @@ -478,7 +478,7 @@ def get_schema_fields(self, view): location='query', schema=coreschema.Integer( title='Limit', - description=force_text(self.limit_query_description) + description=force_str(self.limit_query_description) ) ), coreapi.Field( @@ -487,7 +487,7 @@ def get_schema_fields(self, view): location='query', schema=coreschema.Integer( title='Offset', - description=force_text(self.offset_query_description) + description=force_str(self.offset_query_description) ) ) ] @@ -498,7 +498,7 @@ def get_schema_operation_parameters(self, view): 'name': self.limit_query_param, 'required': False, 'in': 'query', - 'description': force_text(self.limit_query_description), + 'description': force_str(self.limit_query_description), 'schema': { 'type': 'integer', }, @@ -507,7 +507,7 @@ def get_schema_operation_parameters(self, view): 'name': self.offset_query_param, 'required': False, 'in': 'query', - 'description': force_text(self.offset_query_description), + 'description': force_str(self.offset_query_description), 'schema': { 'type': 'integer', }, @@ -861,7 +861,7 @@ def get_schema_fields(self, view): location='query', schema=coreschema.String( title='Cursor', - description=force_text(self.cursor_query_description) + description=force_str(self.cursor_query_description) ) ) ] @@ -873,7 +873,7 @@ def get_schema_fields(self, view): location='query', schema=coreschema.Integer( title='Page size', - description=force_text(self.page_size_query_description) + description=force_str(self.page_size_query_description) ) ) ) @@ -885,7 +885,7 @@ def get_schema_operation_parameters(self, view): 'name': self.cursor_query_param, 'required': False, 'in': 'query', - 'description': force_text(self.cursor_query_description), + 'description': force_str(self.cursor_query_description), 'schema': { 'type': 'integer', }, @@ -897,7 +897,7 @@ def get_schema_operation_parameters(self, view): 'name': self.page_size_query_param, 'required': False, 'in': 'query', - 'description': force_text(self.page_size_query_description), + 'description': force_str(self.page_size_query_description), 'schema': { 'type': 'integer', }, diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 978576a719..fc4eb14283 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -14,7 +14,7 @@ from django.http.multipartparser import \ MultiPartParser as DjangoMultiPartParser from django.http.multipartparser import MultiPartParserError, parse_header -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework import renderers from rest_framework.exceptions import ParseError @@ -205,7 +205,7 @@ def get_filename(self, stream, media_type, parser_context): filename_parm = disposition[1] if 'filename*' in filename_parm: return self.get_encoded_filename(filename_parm) - return force_text(filename_parm['filename']) + return force_str(filename_parm['filename']) except (AttributeError, KeyError, ValueError): pass @@ -214,10 +214,10 @@ def get_encoded_filename(self, filename_parm): Handle encoded filenames per RFC6266. See also: https://tools.ietf.org/html/rfc2231#section-4 """ - encoded_filename = force_text(filename_parm['filename*']) + encoded_filename = force_str(filename_parm['filename*']) try: charset, lang, filename = encoded_filename.split('\'', 2) filename = parse.unquote(filename) except (ValueError, LookupError): - filename = force_text(filename_parm['filename']) + filename = force_str(filename_parm['filename']) return filename diff --git a/rest_framework/schemas/coreapi.py b/rest_framework/schemas/coreapi.py index a2252ff792..8d9099f2a9 100644 --- a/rest_framework/schemas/coreapi.py +++ b/rest_framework/schemas/coreapi.py @@ -4,7 +4,7 @@ from urllib import parse from django.db import models -from django.utils.encoding import force_text, smart_text +from django.utils.encoding import force_str, smart_text from rest_framework import exceptions, serializers from rest_framework.compat import coreapi, coreschema, uritemplate @@ -255,8 +255,8 @@ def determine_path_prefix(self, paths): def field_to_schema(field): - title = force_text(field.label) if field.label else '' - description = force_text(field.help_text) if field.help_text else '' + title = force_str(field.label) if field.label else '' + description = force_str(field.help_text) if field.help_text else '' if isinstance(field, (serializers.ListSerializer, serializers.ListField)): child_schema = field_to_schema(field.child) @@ -457,10 +457,10 @@ def get_path_fields(self, path, method): model_field = None if model_field is not None and model_field.verbose_name: - title = force_text(model_field.verbose_name) + title = force_str(model_field.verbose_name) if model_field is not None and model_field.help_text: - description = force_text(model_field.help_text) + description = force_str(model_field.help_text) elif model_field is not None and model_field.primary_key: description = get_pk_description(model, model_field) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index ac828935f5..db5712efb4 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -6,7 +6,7 @@ MinLengthValidator, MinValueValidator, RegexValidator, URLValidator ) from django.db import models -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework import exceptions, serializers from rest_framework.compat import uritemplate @@ -162,7 +162,7 @@ def _get_path_parameters(self, path, method): model_field = None if model_field is not None and model_field.help_text: - description = force_text(model_field.help_text) + description = force_str(model_field.help_text) elif model_field is not None and model_field.primary_key: description = get_pk_description(model, model_field) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index e754c76c05..79dd953fff 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -4,7 +4,7 @@ from django import template from django.template import loader from django.urls import NoReverseMatch, reverse -from django.utils.encoding import force_text, iri_to_uri +from django.utils.encoding import force_str, iri_to_uri from django.utils.html import escape, format_html, smart_urlquote from django.utils.safestring import SafeData, mark_safe @@ -339,7 +339,7 @@ def trim_url(x, limit=trim_url_limit): def conditional_escape(text): return escape(text) if autoescape and not safe_input else text - words = word_split_re.split(force_text(text)) + words = word_split_re.split(force_str(text)) for i, word in enumerate(words): if '.' in word or '@' in word or ':' in word: # Deal with punctuation. diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index dfd205d40c..27293b7252 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -8,7 +8,7 @@ from django.db.models.query import QuerySet from django.utils import timezone -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.functional import Promise from rest_framework.compat import coreapi @@ -23,7 +23,7 @@ def default(self, obj): # For Date Time string spec, see ECMA 262 # https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 if isinstance(obj, Promise): - return force_text(obj) + return force_str(obj) elif isinstance(obj, datetime.datetime): representation = obj.isoformat() if representation.endswith('+00:00'): diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index e96cc6719b..c5917fd415 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -3,7 +3,7 @@ """ import re -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.html import escape from django.utils.safestring import mark_safe @@ -29,7 +29,7 @@ def dedent(content): as it fails to dedent multiline docstrings that include unindented text on the initial line. """ - content = force_text(content) + content = force_str(content) lines = [line for line in content.splitlines()[1:] if line.lstrip()] # unindent the content if needed diff --git a/rest_framework/utils/representation.py b/rest_framework/utils/representation.py index 3916eb6864..6f2efee164 100644 --- a/rest_framework/utils/representation.py +++ b/rest_framework/utils/representation.py @@ -5,7 +5,7 @@ import re from django.db import models -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.functional import Promise @@ -28,7 +28,7 @@ def smart_repr(value): return manager_repr(value) if isinstance(value, Promise) and value._delegate_text: - value = force_text(value) + value = force_str(value) value = repr(value) diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 80aea27d35..b18fbe0df9 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -1,7 +1,7 @@ from collections import OrderedDict from collections.abc import MutableMapping -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework.utils import json @@ -123,7 +123,7 @@ def as_form_field(self): if isinstance(value, (list, dict)): values[key] = value else: - values[key] = '' if (value is None or value is False) else force_text(value) + values[key] = '' if (value is None or value is False) else force_str(value) return self.__class__(self._field, values, self.errors, self._prefix) From 0ebfbfdf81b71a5ba3600e6edbd666c5b732fb23 Mon Sep 17 00:00:00 2001 From: Chris Vigelius Date: Wed, 7 Aug 2019 21:40:21 +0200 Subject: [PATCH 07/79] OpenAPI: only include non-empty required property. (#6851) Closes #6834 --- rest_framework/schemas/openapi.py | 13 +++++++++---- tests/schemas/test_openapi.py | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index db5712efb4..1ad685271e 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -381,10 +381,14 @@ def _map_serializer(self, serializer): self._map_field_validators(field.validators, schema) properties[field.field_name] = schema - return { - 'required': required, - 'properties': properties, + + result = { + 'properties': properties } + if len(required) > 0: + result['required'] = required + + return result def _map_field_validators(self, validators, schema): """ @@ -470,7 +474,8 @@ def _get_responses(self, path, method): for name, schema in item_schema['properties'].copy().items(): if 'writeOnly' in schema: del item_schema['properties'][name] - item_schema['required'] = [f for f in item_schema['required'] if f != name] + if 'required' in item_schema: + item_schema['required'] = [f for f in item_schema['required'] if f != name] if is_list_view(path, method, self.view): response_schema = { diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 9930621073..5df60efe61 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -142,6 +142,32 @@ class View(generics.GenericAPIView): assert request_body['content']['application/json']['schema']['required'] == ['text'] assert list(request_body['content']['application/json']['schema']['properties'].keys()) == ['text'] + def test_empty_required(self): + path = '/' + method = 'POST' + + class Serializer(serializers.Serializer): + read_only = serializers.CharField(read_only=True) + write_only = serializers.CharField(write_only=True, required=False) + + class View(generics.GenericAPIView): + serializer_class = Serializer + + view = create_view( + View, + method, + create_request(path) + ) + inspector = AutoSchema() + inspector.view = view + + request_body = inspector._get_request_body(path, method) + # there should be no empty 'required' property, see #6834 + assert 'required' not in request_body['content']['application/json']['schema'] + + for response in inspector._get_responses(path, method).values(): + assert 'required' not in response['content']['application/json']['schema'] + def test_response_body_generation(self): path = '/' method = 'POST' From f7dc6b56569790d6cea0ab19485832064f1c8069 Mon Sep 17 00:00:00 2001 From: Chris Vigelius Date: Wed, 7 Aug 2019 22:09:27 +0200 Subject: [PATCH 08/79] OpenAPI: Added required description to responses. (#6850) --- rest_framework/schemas/openapi.py | 6 +++++- tests/schemas/test_openapi.py | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 1ad685271e..d37148ea2d 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -490,6 +490,10 @@ def _get_responses(self, path, method): 'content': { ct: {'schema': response_schema} for ct in self.content_types - } + }, + # description is a mandatory property, + # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject + # TODO: put something meaningful into it + 'description': "" } } diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 5df60efe61..de8b0f247c 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -84,6 +84,7 @@ def test_path_without_parameters(self): 'parameters': [], 'responses': { '200': { + 'description': '', 'content': { 'application/json': { 'schema': { @@ -190,6 +191,7 @@ class View(generics.GenericAPIView): responses = inspector._get_responses(path, method) assert responses['200']['content']['application/json']['schema']['required'] == ['text'] assert list(responses['200']['content']['application/json']['schema']['properties'].keys()) == ['text'] + assert 'description' in responses['200'] def test_response_body_nested_serializer(self): path = '/' @@ -243,6 +245,7 @@ class View(generics.GenericAPIView): responses = inspector._get_responses(path, method) assert responses == { '200': { + 'description': '', 'content': { 'application/json': { 'schema': { @@ -283,6 +286,7 @@ class View(generics.GenericAPIView): responses = inspector._get_responses(path, method) assert responses == { '200': { + 'description': '', 'content': { 'application/json': { 'schema': { From a1424675861e6add5a05710fc3a88922e529c340 Mon Sep 17 00:00:00 2001 From: Dima Knivets Date: Fri, 9 Aug 2019 16:02:41 +0300 Subject: [PATCH 09/79] Fixed incorrect OpenAPI response schema generation for a DELETE method in generic views (#6860) --- rest_framework/schemas/openapi.py | 7 +++++++ tests/schemas/test_openapi.py | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index d37148ea2d..0af7510cde 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -465,6 +465,13 @@ def _get_request_body(self, path, method): def _get_responses(self, path, method): # TODO: Handle multiple codes and pagination classes. + if method == 'DELETE': + return { + '204': { + 'description': '' + } + } + item_schema = {} serializer = self._get_serializer(path, method) diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index de8b0f247c..78a5609dac 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -264,6 +264,29 @@ class View(generics.GenericAPIView): }, } + def test_delete_response_body_generation(self): + """Test that a view's delete method generates a proper response body schema.""" + path = '/{id}/' + method = 'DELETE' + + class View(generics.DestroyAPIView): + serializer_class = views.ExampleSerializer + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + responses = inspector._get_responses(path, method) + assert responses == { + '204': { + 'description': '', + }, + } + def test_retrieve_response_body_generation(self): """Test that a list of properties is returned for retrieve item views.""" path = '/{id}/' From 5a8736ae450c14ab1236386c5b6065b355541d5e Mon Sep 17 00:00:00 2001 From: Christopher Grebs Date: Mon, 12 Aug 2019 20:36:05 +0200 Subject: [PATCH 10/79] Handle 'None' return value of wait() properly during throttling. (#6837) --- rest_framework/views.py | 10 +++++++++- tests/test_throttling.py | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/rest_framework/views.py b/rest_framework/views.py index 832f172330..bec10560a9 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -356,7 +356,15 @@ def check_throttles(self, request): throttle_durations.append(throttle.wait()) if throttle_durations: - self.throttled(request, max(throttle_durations)) + # Filter out `None` values which may happen in case of config / rate + # changes, see #1438 + durations = [ + duration for duration in throttle_durations + if duration is not None + ] + + duration = max(durations, default=None) + self.throttled(request, duration) def determine_version(self, request, *args, **kwargs): """ diff --git a/tests/test_throttling.py b/tests/test_throttling.py index 3c172e2636..d5a61232d9 100644 --- a/tests/test_throttling.py +++ b/tests/test_throttling.py @@ -159,6 +159,27 @@ def test_request_throttling_multiple_throttles(self): assert response.status_code == 429 assert int(response['retry-after']) == 58 + def test_throttle_rate_change_negative(self): + self.set_throttle_timer(MockView_DoubleThrottling, 0) + request = self.factory.get('/') + for dummy in range(24): + response = MockView_DoubleThrottling.as_view()(request) + assert response.status_code == 429 + assert int(response['retry-after']) == 60 + + previous_rate = User3SecRateThrottle.rate + try: + User3SecRateThrottle.rate = '1/sec' + + for dummy in range(24): + response = MockView_DoubleThrottling.as_view()(request) + + assert response.status_code == 429 + assert int(response['retry-after']) == 60 + finally: + # reset + User3SecRateThrottle.rate = previous_rate + def ensure_response_header_contains_proper_throttle_field(self, view, expected_headers): """ Ensure the response returns an Retry-After field with status and next attributes From ec1b14174f8717337fc4402c7cea43f647e2586e Mon Sep 17 00:00:00 2001 From: Min ho Kim Date: Thu, 15 Aug 2019 07:39:45 +1000 Subject: [PATCH 11/79] Fixed typos (#6872) --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0b41cd50bd..b4e5ff5797 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,8 +2,8 @@ # just Django, but for the purposes of development and testing # there are a number of packages that are useful to install. -# Laying these out as seperate requirements files, allows us to -# only included the relevent sets when running tox, and ensures +# Laying these out as separate requirements files, allows us to +# only included the relevant sets when running tox, and ensures # we are only ever declaring our dependencies in one place. -r requirements/requirements-optionals.txt From f8c16441fa69850fd581c23807a8a0e38f3239d4 Mon Sep 17 00:00:00 2001 From: Reupen Shah Date: Tue, 3 Sep 2019 14:25:44 +0100 Subject: [PATCH 12/79] Add support for pagination in OpenAPI response schemas (#6867) Refs #6846 This provides a way for pagination classes to add pagination properties (`count`, `next`, `results` etc.) to OpenAPI response schemas. A new method `get_paginated_response_schema()` has been added to `BasePagination`. This method is intended to mirror `get_paginated_response()` (which takes a `list` and wraps it in a `dict`). Hence, `get_paginated_response_schema()` takes an unpaginated response schema (of type `array`) and wraps that with a schema object of type `object` containing the relevant properties that the pagination class adds to responses. The default implementation of `BasePagination.get_paginated_response_schema()` simply passes the schema through unmodified, for backwards compatibility. --- rest_framework/pagination.py | 59 ++++++++++++++++++++ rest_framework/schemas/openapi.py | 15 ++++-- tests/schemas/test_openapi.py | 66 ++++++++++++++++++++++- tests/test_pagination.py | 89 +++++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 4 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index a68e8ab128..1a1ba2f657 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -138,6 +138,9 @@ def paginate_queryset(self, queryset, request, view=None): # pragma: no cover def get_paginated_response(self, data): # pragma: no cover raise NotImplementedError('get_paginated_response() must be implemented.') + def get_paginated_response_schema(self, schema): + return schema + def to_html(self): # pragma: no cover raise NotImplementedError('to_html() must be implemented to display page controls.') @@ -222,6 +225,26 @@ def get_paginated_response(self, data): ('results', data) ])) + def get_paginated_response_schema(self, schema): + return { + 'type': 'object', + 'properties': { + 'count': { + 'type': 'integer', + 'example': 123, + }, + 'next': { + 'type': 'string', + 'nullable': True, + }, + 'previous': { + 'type': 'string', + 'nullable': True, + }, + 'results': schema, + }, + } + def get_page_size(self, request): if self.page_size_query_param: try: @@ -369,6 +392,26 @@ def get_paginated_response(self, data): ('results', data) ])) + def get_paginated_response_schema(self, schema): + return { + 'type': 'object', + 'properties': { + 'count': { + 'type': 'integer', + 'example': 123, + }, + 'next': { + 'type': 'string', + 'nullable': True, + }, + 'previous': { + 'type': 'string', + 'nullable': True, + }, + 'results': schema, + }, + } + def get_limit(self, request): if self.limit_query_param: try: @@ -840,6 +883,22 @@ def get_paginated_response(self, data): ('results', data) ])) + def get_paginated_response_schema(self, schema): + return { + 'type': 'object', + 'properties': { + 'next': { + 'type': 'string', + 'nullable': True, + }, + 'previous': { + 'type': 'string', + 'nullable': True, + }, + 'results': schema, + }, + } + def get_html_context(self): return { 'previous_url': self.get_previous_link(), diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 0af7510cde..343d0b6247 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -206,11 +206,10 @@ def _get_pagination_parameters(self, path, method): if not is_list_view(path, method, view): return [] - pagination = getattr(view, 'pagination_class', None) - if not pagination: + paginator = self._get_pagninator() + if not paginator: return [] - paginator = view.pagination_class() return paginator.get_schema_operation_parameters(view) def _map_field(self, field): @@ -423,6 +422,13 @@ def _map_field_validators(self, validators, schema): schema['maximum'] = int(digits * '9') + 1 schema['minimum'] = -schema['maximum'] + def _get_pagninator(self): + pagination_class = getattr(self.view, 'pagination_class', None) + if pagination_class: + return pagination_class() + + return None + def _get_serializer(self, method, path): view = self.view @@ -489,6 +495,9 @@ def _get_responses(self, path, method): 'type': 'array', 'items': item_schema, } + paginator = self._get_pagninator() + if paginator: + response_schema = paginator.get_paginated_response_schema(response_schema) else: response_schema = item_schema diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 78a5609dac..e0fe3c9abe 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -264,6 +264,58 @@ class View(generics.GenericAPIView): }, } + def test_paginated_list_response_body_generation(self): + """Test that pagination properties are added for a paginated list view.""" + path = '/' + method = 'GET' + + class Pagination(pagination.BasePagination): + def get_paginated_response_schema(self, schema): + return { + 'type': 'object', + 'item': schema, + } + + class ItemSerializer(serializers.Serializer): + text = serializers.CharField() + + class View(generics.GenericAPIView): + serializer_class = ItemSerializer + pagination_class = Pagination + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + responses = inspector._get_responses(path, method) + assert responses == { + '200': { + 'description': '', + 'content': { + 'application/json': { + 'schema': { + 'type': 'object', + 'item': { + 'type': 'array', + 'items': { + 'properties': { + 'text': { + 'type': 'string', + }, + }, + 'required': ['text'], + }, + }, + }, + }, + }, + }, + } + def test_delete_response_body_generation(self): """Test that a view's delete method generates a proper response body schema.""" path = '/{id}/' @@ -288,15 +340,27 @@ class View(generics.DestroyAPIView): } def test_retrieve_response_body_generation(self): - """Test that a list of properties is returned for retrieve item views.""" + """ + Test that a list of properties is returned for retrieve item views. + + Pagination properties should not be added as the view represents a single item. + """ path = '/{id}/' method = 'GET' + class Pagination(pagination.BasePagination): + def get_paginated_response_schema(self, schema): + return { + 'type': 'object', + 'item': schema, + } + class ItemSerializer(serializers.Serializer): text = serializers.CharField() class View(generics.GenericAPIView): serializer_class = ItemSerializer + pagination_class = Pagination view = create_view( View, diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 11fd6844df..cd84c81516 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -259,6 +259,37 @@ def test_invalid_page(self): with pytest.raises(exceptions.NotFound): self.paginate_queryset(request) + def test_get_paginated_response_schema(self): + unpaginated_schema = { + 'type': 'object', + 'item': { + 'properties': { + 'test-property': { + 'type': 'integer', + }, + }, + }, + } + + assert self.pagination.get_paginated_response_schema(unpaginated_schema) == { + 'type': 'object', + 'properties': { + 'count': { + 'type': 'integer', + 'example': 123, + }, + 'next': { + 'type': 'string', + 'nullable': True, + }, + 'previous': { + 'type': 'string', + 'nullable': True, + }, + 'results': unpaginated_schema, + }, + } + class TestPageNumberPaginationOverride: """ @@ -535,6 +566,37 @@ def test_max_limit(self): assert content.get('next') == next_url assert content.get('previous') == prev_url + def test_get_paginated_response_schema(self): + unpaginated_schema = { + 'type': 'object', + 'item': { + 'properties': { + 'test-property': { + 'type': 'integer', + }, + }, + }, + } + + assert self.pagination.get_paginated_response_schema(unpaginated_schema) == { + 'type': 'object', + 'properties': { + 'count': { + 'type': 'integer', + 'example': 123, + }, + 'next': { + 'type': 'string', + 'nullable': True, + }, + 'previous': { + 'type': 'string', + 'nullable': True, + }, + 'results': unpaginated_schema, + }, + } + class CursorPaginationTestsMixin: @@ -834,6 +896,33 @@ def test_cursor_pagination_with_page_size_negative(self): assert current == [1, 1, 1, 1, 1] assert next == [1, 2, 3, 4, 4] + def test_get_paginated_response_schema(self): + unpaginated_schema = { + 'type': 'object', + 'item': { + 'properties': { + 'test-property': { + 'type': 'integer', + }, + }, + }, + } + + assert self.pagination.get_paginated_response_schema(unpaginated_schema) == { + 'type': 'object', + 'properties': { + 'next': { + 'type': 'string', + 'nullable': True, + }, + 'previous': { + 'type': 'string', + 'nullable': True, + }, + 'results': unpaginated_schema, + }, + } + class TestCursorPagination(CursorPaginationTestsMixin): """ From 1cc4be47b40a0d0c062fccf28853ca448a8522ab Mon Sep 17 00:00:00 2001 From: Dima Knivets Date: Tue, 3 Sep 2019 16:43:54 +0300 Subject: [PATCH 13/79] Fixed min/max attributes for serializers.ListField (#6866) --- rest_framework/schemas/openapi.py | 18 +++++++++++------- tests/schemas/test_openapi.py | 3 +++ tests/schemas/views.py | 6 ++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 343d0b6247..4fee95439b 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -377,7 +377,7 @@ def _map_serializer(self, serializer): schema['default'] = field.default if field.help_text: schema['description'] = str(field.help_text) - self._map_field_validators(field.validators, schema) + self._map_field_validators(field, schema) properties[field.field_name] = schema @@ -389,13 +389,11 @@ def _map_serializer(self, serializer): return result - def _map_field_validators(self, validators, schema): + def _map_field_validators(self, field, schema): """ map field validators - :param list:validators: list of field validators - :param dict:schema: schema that the validators get added to """ - for v in validators: + for v in field.validators: # "Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification." # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types if isinstance(v, EmailValidator): @@ -405,9 +403,15 @@ def _map_field_validators(self, validators, schema): if isinstance(v, RegexValidator): schema['pattern'] = v.regex.pattern elif isinstance(v, MaxLengthValidator): - schema['maxLength'] = v.limit_value + attr_name = 'maxLength' + if isinstance(field, serializers.ListField): + attr_name = 'maxItems' + schema[attr_name] = v.limit_value elif isinstance(v, MinLengthValidator): - schema['minLength'] = v.limit_value + attr_name = 'minLength' + if isinstance(field, serializers.ListField): + attr_name = 'minItems' + schema[attr_name] = v.limit_value elif isinstance(v, MaxValueValidator): schema['maximum'] = v.limit_value elif isinstance(v, MinValueValidator): diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index e0fe3c9abe..508e7dba8f 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -459,6 +459,9 @@ def test_serializer_validators(self): assert properties['string']['minLength'] == 2 assert properties['string']['maxLength'] == 10 + assert properties['lst']['minItems'] == 2 + assert properties['lst']['maxItems'] == 10 + assert properties['regex']['pattern'] == r'[ABC]12{3}' assert properties['regex']['description'] == 'must have an A, B, or C followed by 1222' diff --git a/tests/schemas/views.py b/tests/schemas/views.py index 273f1d30a1..d1fc75eb81 100644 --- a/tests/schemas/views.py +++ b/tests/schemas/views.py @@ -85,6 +85,12 @@ class ExampleValidatedSerializer(serializers.Serializer): ), help_text='must have an A, B, or C followed by 1222' ) + lst = serializers.ListField( + validators=( + MaxLengthValidator(limit_value=10), + MinLengthValidator(limit_value=2), + ) + ) decimal1 = serializers.DecimalField(max_digits=6, decimal_places=2) decimal2 = serializers.DecimalField(max_digits=5, decimal_places=0, validators=(DecimalValidator(max_digits=17, decimal_places=4),)) From b3f032fb8f93dae5291e19a8117dd45e01b1c2e8 Mon Sep 17 00:00:00 2001 From: "Peter J. Farrell" Date: Tue, 3 Sep 2019 09:05:43 -0500 Subject: [PATCH 14/79] Fixed #6875 -- Made OpenAPI Schema operationId casing consistent. (#6876) --- rest_framework/schemas/openapi.py | 9 ++++++--- tests/schemas/test_openapi.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 4fee95439b..b9ab104a08 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -111,7 +111,7 @@ def _get_operation_id(self, path, method): """ method_name = getattr(self.view, 'action', method.lower()) if is_list_view(path, method, self.view): - action = 'List' + action = 'list' elif method_name not in self.method_mapping: action = method_name else: @@ -135,10 +135,13 @@ def _get_operation_id(self, path, method): name = name[:-7] elif name.endswith('View'): name = name[:-4] - if name.endswith(action): # ListView, UpdateAPIView, ThingDelete ... + + # Due to camel-casing of classes and `action` being lowercase, apply title in order to find if action truly + # comes at the end of the name + if name.endswith(action.title()): # ListView, UpdateAPIView, ThingDelete ... name = name[:-len(action)] - if action == 'List' and not name.endswith('s'): # ListThings instead of ListThing + if action == 'list' and not name.endswith('s'): # listThings instead of listThing name += 's' return action + name diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 508e7dba8f..187672ca28 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -80,7 +80,7 @@ def test_path_without_parameters(self): operation = inspector.get_operation(path, method) assert operation == { - 'operationId': 'ListExamples', + 'operationId': 'listExamples', 'parameters': [], 'responses': { '200': { @@ -402,7 +402,7 @@ def test_operation_id_generation(self): inspector.view = view operationId = inspector._get_operation_id(path, method) - assert operationId == 'ListExamples' + assert operationId == 'listExamples' def test_repeat_operation_ids(self): router = routers.SimpleRouter() From e57c1505fc4ed957332ae547a35c8713acfdf30c Mon Sep 17 00:00:00 2001 From: Yann Savary Date: Tue, 3 Sep 2019 16:07:30 +0200 Subject: [PATCH 15/79] Replaced 'TODO' hardcoded version info by a parameter with default '0.1.0' (#6899) --- docs/api-guide/schemas.md | 5 ++++- rest_framework/schemas/__init__.py | 5 +++-- rest_framework/schemas/coreapi.py | 2 +- rest_framework/schemas/generators.py | 3 ++- rest_framework/schemas/openapi.py | 2 +- tests/schemas/test_openapi.py | 14 ++++++++++++++ 6 files changed, 25 insertions(+), 6 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 040c2ed14b..e1ac16a229 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -60,7 +60,8 @@ urlpatterns = [ # * Provide view name for use with `reverse()`. path('openapi', get_schema_view( title="Your Project", - description="API for all things …" + description="API for all things …", + version="1.0.0" ), name='openapi-schema'), # ... ] @@ -72,6 +73,7 @@ The `get_schema_view()` helper takes the following keyword arguments: * `title`: May be used to provide a descriptive title for the schema definition. * `description`: Longer descriptive text. +* `version`: The version of the API. Defaults to `0.1.0`. * `url`: May be used to pass a canonical base URL for the schema. schema_view = get_schema_view( @@ -137,6 +139,7 @@ Arguments: * `title` **required**: The name of the API. * `description`: Longer descriptive text. +* `version`: The version of the API. Defaults to `0.1.0`. * `url`: The root URL of the API schema. This option is not required unless the schema is included under path prefix. * `patterns`: A list of URLs to inspect when generating the schema. Defaults to the project's URL conf. * `urlconf`: A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`. diff --git a/rest_framework/schemas/__init__.py b/rest_framework/schemas/__init__.py index 8fdb2d86a6..588680362d 100644 --- a/rest_framework/schemas/__init__.py +++ b/rest_framework/schemas/__init__.py @@ -31,7 +31,8 @@ def get_schema_view( title=None, url=None, description=None, urlconf=None, renderer_classes=None, public=False, patterns=None, generator_class=None, authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, - permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES): + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES, + version=None): """ Return a schema view. """ @@ -43,7 +44,7 @@ def get_schema_view( generator = generator_class( title=title, url=url, description=description, - urlconf=urlconf, patterns=patterns, + urlconf=urlconf, patterns=patterns, version=version ) # Avoid import cycle on APIView diff --git a/rest_framework/schemas/coreapi.py b/rest_framework/schemas/coreapi.py index 8d9099f2a9..d811da2d83 100644 --- a/rest_framework/schemas/coreapi.py +++ b/rest_framework/schemas/coreapi.py @@ -124,7 +124,7 @@ class SchemaGenerator(BaseSchemaGenerator): # Set by 'SCHEMA_COERCE_METHOD_NAMES'. coerce_method_names = None - def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None): + def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version=None): assert coreapi, '`coreapi` must be installed for schema support.' assert coreschema, '`coreschema` must be installed for schema support.' diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index a656c3ba5a..0b789fc79c 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -151,7 +151,7 @@ class BaseSchemaGenerator(object): # Set by 'SCHEMA_COERCE_PATH_PK'. coerce_path_pk = None - def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None): + def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version='0.1.0'): if url and not url.endswith('/'): url += '/' @@ -161,6 +161,7 @@ def __init__(self, title=None, url=None, description=None, patterns=None, urlcon self.urlconf = urlconf self.title = title self.description = description + self.version = version self.url = url self.endpoints = None diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index b9ab104a08..ac846bf801 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -24,7 +24,7 @@ class SchemaGenerator(BaseSchemaGenerator): def get_info(self): info = { 'title': self.title, - 'version': 'TODO', + 'version': self.version, } if self.description is not None: diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 187672ca28..d9375585bd 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -556,3 +556,17 @@ def test_schema_construction(self): assert 'openapi' in schema assert 'paths' in schema + + def test_schema_information(self): + """Construction of the top level dictionary.""" + patterns = [ + url(r'^example/?$', views.ExampleListView.as_view()), + ] + generator = SchemaGenerator(patterns=patterns, title='My title', version='1.2.3', description='My description') + + request = create_request('/') + schema = generator.get_schema(request=request) + + assert schema['info']['title'] == 'My title' + assert schema['info']['version'] == '1.2.3' + assert schema['info']['description'] == 'My description' From c0cf37e35dd51a1c5fb6e98d0bc3dcff0f60d412 Mon Sep 17 00:00:00 2001 From: Pramod Pujara Date: Wed, 4 Sep 2019 00:06:53 +0545 Subject: [PATCH 16/79] Update tutorial links (#6890) --- docs/community/tutorials-and-resources.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/community/tutorials-and-resources.md b/docs/community/tutorials-and-resources.md index a03d63a3ca..7993f54fb1 100644 --- a/docs/community/tutorials-and-resources.md +++ b/docs/community/tutorials-and-resources.md @@ -85,11 +85,11 @@ Want your Django REST Framework talk/tutorial/article to be added to our website [beginners-guide-to-the-django-rest-framework]: https://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786 -[getting-started-with-django-rest-framework-and-angularjs]: https://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html +[getting-started-with-django-rest-framework-and-angularjs]: https://blog.kevinastone.com/django-rest-framework-and-angular-js [end-to-end-web-app-with-django-rest-framework-angularjs]: https://mourafiq.com/2013/07/01/end-to-end-web-app-with-django-angular-1.html -[start-your-api-django-rest-framework-part-1]: https://godjango.com/41-start-your-api-django-rest-framework-part-1/ -[permissions-authentication-django-rest-framework-part-2]: https://godjango.com/43-permissions-authentication-django-rest-framework-part-2/ -[viewsets-and-routers-django-rest-framework-part-3]: https://godjango.com/45-viewsets-and-routers-django-rest-framework-part-3/ +[start-your-api-django-rest-framework-part-1]: https://www.youtube.com/watch?v=hqo2kk91WpE +[permissions-authentication-django-rest-framework-part-2]: https://www.youtube.com/watch?v=R3xvUDUZxGU +[viewsets-and-routers-django-rest-framework-part-3]: https://www.youtube.com/watch?v=2d6w4DGQ4OU [django-rest-framework-user-endpoint]: https://richardtier.com/2014/02/25/django-rest-framework-user-endpoint/ [check-credentials-using-django-rest-framework]: https://richardtier.com/2014/03/06/110/ [ember-and-django-part 1-video]: http://www.neckbeardrepublic.com/screencasts/ember-and-django-part-1 From 4b30b320144897965bb12fa07745ba02a0452884 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 4 Sep 2019 12:53:50 +0100 Subject: [PATCH 17/79] Default OpenAPI version to the empty string (#6907) --- rest_framework/schemas/generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 0b789fc79c..77e92eeb8c 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -151,7 +151,7 @@ class BaseSchemaGenerator(object): # Set by 'SCHEMA_COERCE_PATH_PK'. coerce_path_pk = None - def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version='0.1.0'): + def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version=''): if url and not url.endswith('/'): url += '/' From 89ac0a1c7ef6153312890376ac142674b28b04f1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 4 Sep 2019 14:28:25 +0100 Subject: [PATCH 18/79] Version 3.10.3 (#6908) * Version 3.10.3 * Version 3.10.3 release notes --- docs/community/release-notes.md | 29 +++++++++++++++++++++++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index e3f3820d3a..cdaa350447 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -40,6 +40,35 @@ You can determine your currently installed version using `pip show`: ## 3.10.x series +### 3.10.3 + +* Include API version in OpenAPI schema generation, defaulting to empty string. +* Add pagination properties to OpenAPI response schemas. +* Add missing "description" property to OpenAPI response schemas. +* Only include "required" for non-empty cases in OpenAPI schemas. +* Fix response schemas for "DELETE" case in OpenAPI schemas. +* Use an array type for list view response schemas. +* Use consistent `lowerInitialCamelCase` style in OpenAPI operation IDs. +* Fix `minLength`/`maxLength`/`minItems`/`maxItems` properties in OpenAPI schemas. +* Only call `FileField.url` once in serialization, for improved performance. +* Fix an edge case where throttling calcualtions could error after a configuration change. + +* TODO + +### 3.10.2 + +**Date**: 29th July 2019 + +* Various `OpenAPI` schema fixes. +* Ability to specify urlconf in include_docs_urls. + +### 3.10.1 + +**Date**: 17th July 2019 + +* Don't include autocomplete fields on TokenAuth admin, since it forces constraints on custom user models & admin. +* Require `uritemplate` for OpenAPI schema generation, but not `coreapi`. + ### 3.10.0 **Date**: [15th July 2019][3.10.0-milestone] diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 6a64c8b18b..4d4225c964 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.10.1' +__version__ = '3.10.3' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' From 7a3b7d2001d3d6d147dc472d971552505057700c Mon Sep 17 00:00:00 2001 From: napsterv Date: Mon, 16 Sep 2019 23:17:29 +0530 Subject: [PATCH 19/79] Fixed method signature in docs (#6931) --- docs/api-guide/validators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index 87417b7f15..49685838a2 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -218,7 +218,7 @@ in the `.validate()` method, or else in the view. For example: class BillingRecordSerializer(serializers.ModelSerializer): - def validate(self, data): + def validate(self, attrs): # Apply custom validation either here, or in the view. class Meta: From 2664e650197862c1017435379bc7ce7ec8cd7c9c Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 18 Sep 2019 20:21:32 +0200 Subject: [PATCH 20/79] Add djangorestframework-dataclasses to 3rd party package docs (#6939) --- docs/community/third-party-packages.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index 0f8bdc4a48..674a8973d5 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -211,6 +211,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [djangorestframework-queryfields][djangorestframework-queryfields] - Serializer mixin allowing clients to control which fields will be sent in the API response. * [drf-flex-fields][drf-flex-fields] - Serializer providing dynamic field expansion and sparse field sets via URL parameters. * [drf-action-serializer][drf-action-serializer] - Serializer providing per-action fields config for use with ViewSets to prevent having to write multiple serializers. +* [djangorestframework-dataclasses][djangorestframework-dataclasses] - Serializer providing automatic field generation for Python dataclasses, like the built-in ModelSerializer does for models. ### Serializer fields @@ -348,5 +349,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [drf-access-policy]: https://github.com/rsinger86/drf-access-policy [drf-flex-fields]: https://github.com/rsinger86/drf-flex-fields [drf-action-serializer]: https://github.com/gregschmit/drf-action-serializer +[djangorestframework-dataclasses]: https://github.com/oxan/djangorestframework-dataclasses [djangorestframework-mvt]: https://github.com/corteva/djangorestframework-mvt [django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian From 5b990d40920fb1a0fc74ecd262e1cc1ac5e5394f Mon Sep 17 00:00:00 2001 From: Yezy Ilomo Date: Tue, 24 Sep 2019 10:22:25 +0300 Subject: [PATCH 21/79] Add django-restql to 3rd party packages in docs (#6947) --- docs/community/third-party-packages.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index 674a8973d5..9d7d09d9bc 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -212,6 +212,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [drf-flex-fields][drf-flex-fields] - Serializer providing dynamic field expansion and sparse field sets via URL parameters. * [drf-action-serializer][drf-action-serializer] - Serializer providing per-action fields config for use with ViewSets to prevent having to write multiple serializers. * [djangorestframework-dataclasses][djangorestframework-dataclasses] - Serializer providing automatic field generation for Python dataclasses, like the built-in ModelSerializer does for models. +* [django-restql][django-restql] - Turn your REST API into a GraphQL like API(It allows clients to control which fields will be sent in a response, uses GraphQL like syntax, supports read and write on both flat and nested fields). ### Serializer fields @@ -350,5 +351,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [drf-flex-fields]: https://github.com/rsinger86/drf-flex-fields [drf-action-serializer]: https://github.com/gregschmit/drf-action-serializer [djangorestframework-dataclasses]: https://github.com/oxan/djangorestframework-dataclasses +[django-restql]: https://github.com/yezyilomo/django-restql [djangorestframework-mvt]: https://github.com/corteva/djangorestframework-mvt [django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian From 60f9c129003139fc3dc5fc43d595afcbc3d83f1e Mon Sep 17 00:00:00 2001 From: Ted Stoychev Date: Fri, 27 Sep 2019 19:47:51 +0300 Subject: [PATCH 22/79] Fixed broken link in documentation (#6937) --- docs/api-guide/schemas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index e1ac16a229..bdbb952dc5 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -1,6 +1,6 @@ --- source: - - schemas.py + - schemas --- # Schema From 75afe48b6c69d0c2e9a3f0854a6369e8faba6b4f Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Sat, 28 Sep 2019 20:52:22 +0200 Subject: [PATCH 23/79] Fixed import of FieldDoesNotExist exception. --- rest_framework/serializers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 01f34298b4..6b73e64980 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -16,12 +16,11 @@ from collections import OrderedDict from collections.abc import Mapping -from django.core.exceptions import ImproperlyConfigured +from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured from django.core.exceptions import ValidationError as DjangoValidationError from django.db import models from django.db.models import DurationField as ModelDurationField from django.db.models.fields import Field as DjangoModelField -from django.db.models.fields import FieldDoesNotExist from django.utils import timezone from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ From 30e56f62ba1115ce23851995c6ed6bff45defbb7 Mon Sep 17 00:00:00 2001 From: Konstantinos Tselepakis Date: Thu, 3 Oct 2019 19:33:37 +0300 Subject: [PATCH 24/79] Fix nested write of non-relational fields (#6916) --- rest_framework/serializers.py | 13 +++++++++ tests/test_serializer_nested.py | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 6b73e64980..f5d9a50658 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -790,6 +790,8 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data): * Silently ignore the nested part of the update. * Automatically create a profile instance. """ + ModelClass = serializer.Meta.model + model_field_info = model_meta.get_field_info(ModelClass) # Ensure we don't have a writable nested field. For example: # @@ -799,6 +801,7 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data): assert not any( isinstance(field, BaseSerializer) and (field.source in validated_data) and + (field.source in model_field_info.relations) and isinstance(validated_data[field.source], (list, dict)) for field in serializer._writable_fields ), ( @@ -817,9 +820,19 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data): # class UserSerializer(ModelSerializer): # ... # address = serializer.CharField('profile.address') + # + # Though, non-relational fields (e.g., JSONField) are acceptable. For example: + # + # class NonRelationalPersonModel(models.Model): + # profile = JSONField() + # + # class UserSerializer(ModelSerializer): + # ... + # address = serializer.CharField('profile.address') assert not any( len(field.source_attrs) > 1 and (field.source_attrs[0] in validated_data) and + (field.source_attrs[0] in model_field_info.relations) and isinstance(validated_data[field.source_attrs[0]], (list, dict)) for field in serializer._writable_fields ), ( diff --git a/tests/test_serializer_nested.py b/tests/test_serializer_nested.py index ab30fad223..a614e05d13 100644 --- a/tests/test_serializer_nested.py +++ b/tests/test_serializer_nested.py @@ -4,6 +4,8 @@ from django.test import TestCase from rest_framework import serializers +from rest_framework.compat import postgres_fields +from rest_framework.serializers import raise_errors_on_nested_writes class TestNestedSerializer: @@ -302,3 +304,50 @@ class Meta: 'serializer `tests.test_serializer_nested.DottedAddressSerializer`, ' 'or set `read_only=True` on dotted-source serializer fields.' ) + + +if postgres_fields: + class NonRelationalPersonModel(models.Model): + """Model declaring a postgres JSONField""" + data = postgres_fields.JSONField() + + +@pytest.mark.skipif(not postgres_fields, reason='psycopg2 is not installed') +class TestNestedNonRelationalFieldWrite: + """ + Test that raise_errors_on_nested_writes does not raise `AssertionError` when the + model field is not a relation. + """ + + def test_nested_serializer_create_and_update(self): + + class NonRelationalPersonDataSerializer(serializers.Serializer): + occupation = serializers.CharField() + + class NonRelationalPersonSerializer(serializers.ModelSerializer): + data = NonRelationalPersonDataSerializer() + + class Meta: + model = NonRelationalPersonModel + fields = ['data'] + + serializer = NonRelationalPersonSerializer(data={'data': {'occupation': 'developer'}}) + assert serializer.is_valid() + assert serializer.validated_data == {'data': {'occupation': 'developer'}} + raise_errors_on_nested_writes('create', serializer, serializer.validated_data) + raise_errors_on_nested_writes('update', serializer, serializer.validated_data) + + def test_dotted_source_field_create_and_update(self): + + class DottedNonRelationalPersonSerializer(serializers.ModelSerializer): + occupation = serializers.CharField(source='data.occupation') + + class Meta: + model = NonRelationalPersonModel + fields = ['occupation'] + + serializer = DottedNonRelationalPersonSerializer(data={'occupation': 'developer'}) + assert serializer.is_valid() + assert serializer.validated_data == {'data': {'occupation': 'developer'}} + raise_errors_on_nested_writes('create', serializer, serializer.validated_data) + raise_errors_on_nested_writes('update', serializer, serializer.validated_data) From b4db2dfacf283aa5a1f51ccf895222fbcf3709ec Mon Sep 17 00:00:00 2001 From: mehrab Date: Fri, 4 Oct 2019 23:10:09 +0330 Subject: [PATCH 25/79] Fixed no-cls-argument on staticmethod `__new__` (#6960) --- rest_framework/fields.py | 4 ++-- rest_framework/relations.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 0be6a7c125..c0ceebe3b7 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1764,8 +1764,8 @@ def get_value(self, dictionary): # When HTML form input is used, mark up the input # as being a JSON string, rather than a JSON primitive. class JSONString(str): - def __new__(self, value): - ret = str.__new__(self, value) + def __new__(cls, value): + ret = str.__new__(cls, value) ret.is_json_string = True return ret return JSONString(dictionary[self.field_name]) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 3c2132c5be..3387768847 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -46,8 +46,8 @@ class Hyperlink(str): We use this for hyperlinked URLs that may render as a named link in some contexts, or render as a plain URL in others. """ - def __new__(self, url, obj): - ret = str.__new__(self, url) + def __new__(cls, url, obj): + ret = str.__new__(cls, url) ret.obj = obj return ret From 37dcd553705421201f00fb7f6a75a6214afb9d60 Mon Sep 17 00:00:00 2001 From: Francisco Couzo Date: Fri, 4 Oct 2019 17:50:19 -0300 Subject: [PATCH 26/79] Fix bug in escape_curly_brackets (#6909) --- rest_framework/routers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rest_framework/routers.py b/rest_framework/routers.py index ee5760e81b..d8e19a2d7b 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -38,9 +38,7 @@ def escape_curly_brackets(url_path): """ Double brackets in regex of url_path for escape string formatting """ - if ('{' and '}') in url_path: - url_path = url_path.replace('{', '{{').replace('}', '}}') - return url_path + return url_path.replace('{', '{{').replace('}', '}}') def flatten(list_of_lists): From f36ff9b08b4c6f0f2a8c69f425c241694ddfa792 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 7 Oct 2019 14:21:58 +0100 Subject: [PATCH 27/79] Add Retool as a new premium sponsor (#6977) --- README.md | 5 ++++- docs/img/premium/retool-readme.png | Bin 0 -> 9086 bytes docs/index.md | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 docs/img/premium/retool-readme.png diff --git a/README.md b/README.md index 13ad47aef0..179e2c3577 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ The initial aim is to provide a single full-time position on REST framework. [![][kloudless-img]][kloudless-url] [![][esg-img]][esg-url] [![][lightson-img]][lightson-url] +[![][retool-img]][retool-url] -Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [Cadre][cadre-url], [Kloudless][kloudless-url], [ESG][esg-url], and [Lights On Software][lightson-url]. +Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [Cadre][cadre-url], [Kloudless][kloudless-url], [ESG][esg-url], [Lights On Software][lightson-url], and [Retool][retool-url]. --- @@ -199,6 +200,7 @@ Please see the [security policy][security-policy]. [kloudless-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/kloudless-readme.png [esg-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/esg-readme.png [lightson-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/lightson-readme.png +[retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/retool-readme.png [sentry-url]: https://getsentry.com/welcome/ [stream-url]: https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf @@ -207,6 +209,7 @@ Please see the [security policy][security-policy]. [kloudless-url]: https://hubs.ly/H0f30Lf0 [esg-url]: https://software.esg-usa.com/ [lightson-url]: https://lightsonsoftware.com +[lightson-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship [oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit diff --git a/docs/img/premium/retool-readme.png b/docs/img/premium/retool-readme.png new file mode 100644 index 0000000000000000000000000000000000000000..45058b4b3874aaf2adc40c9fa235080d0ab3cde7 GIT binary patch literal 9086 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 Q{US!~mKLl`$vpgj0C85IdH?_b literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index 6e55c10bf2..e06b21dffd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -73,10 +73,11 @@ continued development by **[signing up for a paid plan][funding]**.
  • Cadre
  • Kloudless
  • Lights On Software
  • +
  • Retool
  • -*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), and [Lights On Software](https://lightsonsoftware.com).* +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), and [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship).* --- From 1f3505931cde9764823482a0f7a90e3bfe7fd8bf Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 7 Oct 2019 14:22:40 +0100 Subject: [PATCH 28/79] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 179e2c3577..8774bc854a 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ Please see the [security policy][security-policy]. [kloudless-url]: https://hubs.ly/H0f30Lf0 [esg-url]: https://software.esg-usa.com/ [lightson-url]: https://lightsonsoftware.com -[lightson-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship +[retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship [oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit From f9cc190177e0ad7bd3d747b0852747aa3819ae77 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 7 Oct 2019 14:31:16 +0100 Subject: [PATCH 29/79] Crop width on premium sponsor images --- docs/img/premium/cadre-readme.png | Bin 7021 -> 6989 bytes docs/img/premium/esg-readme.png | Bin 13650 -> 13590 bytes docs/img/premium/kloudless-readme.png | Bin 14607 -> 14582 bytes docs/img/premium/lightson-readme.png | Bin 16507 -> 16420 bytes docs/img/premium/release-history.png | Bin 18009 -> 17960 bytes docs/img/premium/retool-readme.png | Bin 9086 -> 9053 bytes docs/img/premium/rollbar-readme.png | Bin 17370 -> 17335 bytes docs/img/premium/sentry-readme.png | Bin 24556 -> 24549 bytes docs/img/premium/stream-readme.png | Bin 19311 -> 19281 bytes 9 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/img/premium/cadre-readme.png b/docs/img/premium/cadre-readme.png index 08290b72727f04bdd77573b17051f14994def17f..8144c7bd041f724fd80d5b171a7da59db0d91de4 100644 GIT binary patch literal 6989 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 literal 7021 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 diff --git a/docs/img/premium/esg-readme.png b/docs/img/premium/esg-readme.png index 5aeb93fd2b36b8e2e1ba47fab8ce31957b8969a9..50aec5f1f77da7e7f4bd046adbc812f6d589ab76 100644 GIT binary patch delta 12640 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 F{{SO#rE~xQ delta 12679 zcmXAwWmsEXvxaeNks!q>5VW|vLn!WA9E#K8#RC)x?!`5@yIXN78mv&DxVvk=yyyH$ za$S4xB{P$mwbpa*ANI5M>=Ej!^4M5pSO^FR*a{zIH4zXHY2m*gG0@@GnF+ zO?hdA>Pd=&I<841Bq7cuMC1)zHcoyvPF`&;E@4i7VNQXh3uF;Q-Xt~@c@ox5PaFgU z&OrrPDQz#r6JyLY3Y{kL7N4YO6f&@R3^g@18j74GAtAs(&J%!vgv7I&9l{#O6txnL z!NfkbLsK77;CV+!!#e7|ewUl!&Yhl~b8yHCtQZoI9+$O%dlW4-GolcnHCCm2Ftx?j+F|@GJ&J;!ashmBdB_wbGrwaU`Cj#xp zT(&%uu*;Q%Ql;>WuBsYXvh|dlqeJ1T(v5}W0l<^jCMIj+#0u(;=};fuMJ_9!3`5Qk zJ$yVT$Ourz-19m-^>et0u#rCDZ%;t4EHA1jhMvgea2G~3wl{%awvO@SVKwS(Jh_a{WRga3DZ z-JzkW>~D5&9f!A7&876QrEI_cge1NozXz7MitS=_6-MYz=*_zmfnc$=-T93WAkwA% zJNb&{a;@&p_hlb^q{T&OeyGiCc&T(0=x7ufcjDp66LR=ASF7YVl5pjp^+^>PGS0e> z1DW*_|9Ze`2Wx`Bof(5u#S;$69G9*!QPa*oS25U)@o5Q&cu*B-3XR5-J>y5e2mR}i z?x37$?Xv+w1Zq|kQ%2oTF7<4##iw31in)-u4p-!Hh zY8oFhSiwiiG*p`^ePp!y`k$tUW>|g0E1uAAxxZoXAerOT22|-U-Q$<*{;(Xn{sXb^ zwU7wQ(Gc(f^+eX<&^`)cK!9ZEStF*0pK=Riix9-bNxY4DUns@ezl^!B5_=oV25Df7 z;Kymi4{Q(Ei(yCU)dca+e3N#>m*eQ~S&26TJET#Jq`SftzQ?*-` z4ADh4fqxxi;|+OxsW7CMcjCPZeNMQ}9&ir!(C_tO(Zq1H!ocXb>+pA~rpGv&(^d$0 z=?e}B`OS{kK<}pO`{vT{YltDrWV!XjCd;|@TJ&e~vj)X#>X4ahwS9j)&8am2ipa(W zF48RU5`FP+=`D6Ani9WmV-bNxGSNx!{G)(Ibtk9bbZC+t-2Lew-j6%YV{1GV*6AI1(5cd2ux;=XKJGA8YHWE!!+PDRmvoUaWj5+c~7m7B4Sb`W>EV zgnRYf3?0HRk(Dlv0=TR)tYeHc03oU0nI3UeRM?^3hl8b%)i17*!1~{t{=!lbK~OIF zeI$sTU4UICA_YTcF?neQSTxdSzM+3|Oiu|f-mHl}j6^Fs_AU+S^#i4GD@F&slXsX*T=Se6~Vt0zUud(eEPSr8Juo#iEeA8w%?@#xq=r>VNU zgzJRdM>Y#kS8~pS@B4I&1M~m>v1nmurrxDq&eU<>nYy^gNhK05xxawx=<|$)hj=~d z*#HZ8_^L!Fq?1($94lv{Cv4}eeGSV~zo0}^QOnzFmzae-&wVp-nv`VVMRP%}WbMXR z-S@1;XCy>fEB0dYA^LcuWiIJcTg+J{Bkv>Y5y<8Fh$EKGkMm3cwfxErf>B9NnX^EV z7*N5l2#+U0XZk>Sjs|&A%S6Hi{S!__Mm74zaEq@C%0GGIB>ztn@@5fFW~ z_!wTuF!3IS$<<)eMEY};=Wig#&zU@z*L?f|+dX6-%~INdnF^sId z_?>DiLlnWhO~((ZcDIWbdNbyi&v)C8Esx`QVr5alOV}yd#!4%pivL{7@s|&KZVX@# zENMAYM3f5!L(}<&O$*J;Yj`D6jpv%pmknxW~~VIZrBtrrq^cg!GQaItxf57wAb>^XU- zib2TD?1)nqCm^i^8Nh)jpE{m0Fjwf8pXxW#&`f;m_U`X#GY3O-gwl0V@Mv>Xw$lXo zv-xmF`Ts5!N*b8%wu187RS8bx%M+rrWXwsyiXfPj_KDj}hQH_9oI!~xu7sQ?zVNE~ zv>ed5OLi4dUuH%j%kRPLWArNAIYTx^ssyhoR%PbF#7en{jD(Iz|J=cshTGPLZ;dN{ z9d+G=VZ>*8Cu2%kK6ho!hB3u`iYNJL}cHrvs9{v z{#i<=NTr2G!n2Y325x%W0 zBGsW~F5e#}R*B`bb+*kGZu#Y$HuXr*~S2H~Id-HWPKZ`V3;IpwuZQ5@FOZ}1qNBz+`|7iFwCGlW0 zxiS1_&=*ML2(697fA?UwFg0!J)w*%2F=eo#<8>riMU1NKJ1P^8z%a~DdOf-l%@T9+lLSi%c`6m@QGHevJJK$thN&I@ zj?xpXy?~AscdwfSVFg$lHKYStsxka0{2_VmEJ8Ow3oS;#w?M-o>%b3Vh4dsmXGX%I zWSd74<-Tn)^_FX5rW2?+uu}C0BDJ_v97^Pa4W@B^Yckxus6G+#p^C%{-KlK5j{kKt zqCD2u_(+su_=ANHgQCyw*{BN+LRmegt29&!W#KJdi}<1gnd6Kz-&HKrhU03Ws)ts$ zpGkk>M>%8+vz&Nl#yL|z4jmR2uJMPedmb*o?hd7|H(}Y>Oo{_sz@j_!Dlln2qbvjH zFIcV*mmW-5<`^-G z4+qXI0Zc$+*0@DW=O!>Yx;VYpO_c1^&FJOcOQ+L_=L`Z;awFV{`WQ$*D1lOIFP7Q< z2jiuCwgO@jNHEL6C;gQUAm`h5Z7dGt05?1TN$NzeFaFG1g?dcp#C`qznDK!DLmtIr zUUMuZ9zh9xphhDEXo|b?V%{WNV)jV0{pGdFi6>!XS zCIsWkJfIi^s~wH{ICHJv zqZCh=rJMd^?_ADfl=Ve<`-h6%boXmPE7UH6KCf*JrLDQ>r+h=)@9w2#>3Q7R!B1D&vc6ISWV3`^%J0OgNB zslA$DHN4U_pK1U06lwOl$E_MNaCMriEly3dWJGDj;YzTrGfD>L4iD^Rf?eXez6ECt zafEL{jQBW@AHBR$t7ds(9Ej9{$@6suG`+9MH8@%}ut^@fXIf_JtV6Z0-;KDE*BLR4 zssFNNrUN9}& zwJ-{;)=$VfC-FcjwO-B+4_IB$THE711B6SO_s$gm`T4@_BcZ;sEnS+q2<;CW9EP^4 z{FK3XQ(Ta=<#?S>qPvB7*!~$mZ9>PF%62r>eLm2BI>Y7|>O*Js*NV~kl?UPi=Bt^N z_8Lz`@o<{nDM|_OGewxbqqFRNUCG^ap5gb_nQ7%UJjA-_8z4sKB)H`GIF~%3g7aLX zv-}U1p|v$XIlx|-mgC!wmA6Xruj?KSX-Y&*?kQWr98t;tYmbIlgaYur*CC^^UD*WI zyaq7*_uhkU*J9|pCgu+?^b)CDx9usLS9%IetDt9Ma%Ek>s{ArEm^AlQB|b_tCg0n# zrEMx3b!=%1Lh=?Y_cR;z=?j1JyHTXTPlSjLCBirbs0|a(!F!5npP+Agzu8A!hQOkC zJoevl^G%HmHbl0E_v?d^Xc|Bygu%w0?lj3uU`B9T*S&suXu7*Rg7 zzqEil@q@{3Z({U{gtvFB3fx66hzdmOiM1;<`0LHdp!lzM*coPQ8LU0R+x}O15pZN# zP-sI`mXU>SbL~&CO)K*~mocCkV&atf%Tt&ZG-`Ym()5uN+&=@xXs77J81v|Fu8(fyl*=L&xgaT8<5)uCd zHq|c7-@ZBW0Pc)s-S+!#!YW4Va>)``1uv|m;*yC}Gzxa0|u7d9fcF;3N%+D3kaK0}_U z#lzBIL9$rekC(W!KZqyTqAA|Qm`#brn%RZD`{gx+u_ApyC;E|?G@l5SHqshs?f%Bv z%yOYIsL#`xfh}0Y>xawrc;wBQ=fwpJtk^uHU>nkEb4?}c$O4qe7=l{jaARd;RHm`` zZmDcomY1!VOo}Dh%RA-^Hgbh_0P_RBGAB@N>z#Cp}HV+2egl> zQphh1vSKJwl)RLm0&sCsywp%b&A#~WJ6L5jB(=X!Cz>JP<`?$lrAkxEOri;E^0o!% zx{Ra-h6nW_9*>q;Izv~#1{ylk-FrmmzKN0_J`4h=$nzFXoRVO-{t$#3@s4$k$U=JE zOVYE(LN_DSpZuOxqQV}#l|}`#aBbWR0jf{sdq%#1pI-n>p_k}o!XsMzo^gSVP}%5u zpvC7~YMU#k#)9WVjf~&U(i<%iZ`8mUT1oK+Gt>L}-OrypWBss`zquZa_mCk)52H2~ zgxpx6MRri2csBUY0*@*x{P%yF3n}v|%~1k+icb`XSpR-9@Pm3VOk>cvQDtTCBPdQi zhXWoii@pZ#mQDNC1AH!k+}MbYzj(+-NHk(jhHhb^Z@-wEiy4?|a@47}hsl8>yMY!u zt7!>_4vz2&y^&eF$$gwAoVxq%2V

    MyPmpufy=5vlC<9VPQmh%Y#Rua2dNi!V`6L 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 diff --git a/docs/img/premium/kloudless-readme.png b/docs/img/premium/kloudless-readme.png index e2f05831daf0142ff71218f1ad1826cba51bc09e..2ee1c4874a1c444dd03e5cbce7201f4d00b0aea1 100644 GIT binary patch literal 14582 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¬j1!_(Cu4B;gq*s{oTq< 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 Fe*>lf5}p76 diff --git a/docs/img/premium/release-history.png b/docs/img/premium/release-history.png index b732b1ca23b8ce74f758ce03e90b4e0674ad425a..8bc9b20f6cfe8ef8d1e3c7d26f483ece5a0e5837 100644 GIT binary patch delta 17046 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 Q@Pr8lPgg&ebxsLQ0BZ@afB*mh literal 18009 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 diff --git a/docs/img/premium/retool-readme.png b/docs/img/premium/retool-readme.png index 45058b4b3874aaf2adc40c9fa235080d0ab3cde7..b5dc3aee746f12e183a52eb067a8edd031ac9435 100644 GIT binary patch literal 9053 zcmeHNcUY5Ivxfiz(m^^>1r;Rp4hg+Cr3gre5PC}xkS5Ypy3#u=Qltn5q)8DFq}QcL z6Y0_korK)r?)R;CpS}Oyzc$a4QRvg=IOad2?RG}M(J;Nali z#E#F2u37HMG=ZDw zXiM99xB+eKJ!~OBKQ~V-Hx7=hpEP#p27%kK`?IDc|TW5-uy0Z#UxEN~Y& zP7@t{b|nvQ2)h_i6e!3Ey3WqdF6(XYApJmD^-nnVN{-VJ4)>H65b*W&1^S8rJ-nd; zLQ+yv0)oN%$4HUSV5fB0j3j7lc;^*{#z^-QgPnf;kf35QL@pk=blf9h) z#1-NOafidOIH7+OhxP4$f&WWMKO4_Kg#C2lAGXO}X)gT$0`qY7xpIM#yAvEFEc?@- zzmxy+$R8*r4>wP52n>dWfdv1A{7U;fzsX->Kob9v@hkD~06lLftfe+rdVz%g6!0tU z@BF6!B;Z%#FMuo8OY1xNL0nCgov;r5X@#()n5@7*&ioyz z-?@LvG5tl37}j5Z$@yjG7o@$NG|b1=8DaC+PEbwXuwTmhBS5IYmYhLX&?Oyqr!3|hn?qlFc^ZuaKi4V?i4fmv7T&Ya zg^ToDG8jw_F{3vW8L&HM6cDiM4xZBsaX6RB_Dyvt@a$DhyL0z-;#2&Iq&6UP{zR>X z3(c@4fI2~*7+0k@JjIKfJ=7WSQddloCgCZ*Cea;f?wS}rTw+5SefV3djHmdw^cCet z?&d?xiPiHmo)#Fy4&&cq52eOJi20W^;HtR}$ivO1Qj`Z(DI`RPOI~;lINyEaGSa~|~ zBc-a2|El<}7ysRhe}s#!6}A7{dd%5SNRj4`ak#E;<5>222!;!nxLhFg&C9{tp#2xC zTx@6R$eD%94^k^h20U-xujK@YPd3dTWiZe0MGkD}wT|2Bn32=GZgaR@)n`aBOPQ!Z zn-4*Lr*EzI@0OV#aj1K4+!KPiScseqC1jnF{Z19{_;|*xuJomYG<8B7hG^?Om9x#g zHyV4ND1xh`R@3avIMUkIMubDwmDrqtPe!7}qgbD;JIb~uJ;gUxvR=gNsqqHwg;I#t zxYO}VvKK~#?V*;~FeYU8Vc5`DCBA14zdWvM6>B{k@Gu);JhCqk@SXFST-6IXH$ZlS zFTDzG=%@my(_Xc;t3>7JBrfLM?elgvTkbxMQvgt#fB{;JW2UXPy9}%xIf*L-a6A8z zA0q1PRNpL_TyXBW5GjvnJhUfUXhp3E)FFbQk{ygMKPq{eh?FO#7 z9M1>ru42952g@h1aCEYm<1CK-s1LZE8|aN3F9y|~9lZ<54r1U?rwEwp$mla)6(%_f z>!i(&;EIFpowQ<_&lV{{rn2^9prqWd8q+^=DMO|DOf#a*mpKP&-vw>2KE)$<91NI8 zMvngO*v-`@w};3@m!ECug=7S+qb?RO?z*!3oo{YvkCZKqYl(&)oea6-DLWd=O1J3Fw!O=X z-39Y)=Z7;zVj5bz0wHHwNbJt5KUTU8xjCwSV0#ap@zf26h? zz8R6cYq7vh$(^^Z+_P={n8jT+`Z;h%Umk8EuR7V!9;!m652sUkOg7g1dxiV&-V(ixr zs4~fXovBd>5Rr`C>!(@&>SfW2LGRX$?UWbibE|yqH?iD%M(=tOu#Ff3!8}&vDNQpw z0uG>G=v$!KhA(#05$6l7e zR&+bX2VOYZT6i^UO%~eZv13c$WYUxjNeo^N%sfCuM3YMZ)RJ4)k+slj=6FBWbp75yP&7W>I`9uIEz^%ftM{O&F{`myTS9T@%plh#CmC)X5Wxd5HkI^ ze+L`pGH$8V0SrD$yo9!o7fGz@`MVVeZ;oeeny>v(CBd+bpq6igrvndvV{@x38LrJ< zA-q_t#0FuV8d7&ftsV!rpXyq_Qz{lNKf9sp>)f3Zd5~v>YFa>7(lYPR zTPk^-Wf>C_ex@oZx3e?uDIN;ifepA=A~RM47A`C=sQ3|wnqKF-h3GIgRGvcK{86Q^ zSY7kBTc#4ZRN>o|u=VES+IRfNVJY03Z%rcG=Nlf)AEX!sANDn_x6%WpXO(AJlq3cV zNb0{F-ld@>w}Gr|A##q&UF#r;prj%lKFF3CJLi__E+Ow+pVj3-WT`Je=h7eG&!PL# z@v|&8?RZR*%bk9r7+h7#AR-IyM<#Hh-F=4}Vi$}WvprfYHQX^ypgIQ zoF{L{AffmUROB7(bPEceLH&5x%H*oR4s_+%01H{2G)~s)L3qEKXaPnv-wBbIwu5&4 zhe)zxBvT&m$n>g^L<^OeLcUB>1gV;lqChKm_0nCs;zLiM;EU;PhVk*>;gy2uR<10v z%5~DY`Iq z4)zm5)0s(-Tf`kQz1Q161AShRqsQ=j_-l0xv5&lDCYky!0r})Rem&jYNyJsy(4>A0WzM&#inVvG)Xg2Ua@XPnGK<( zH4QHn3Dj<|?mZ=}LEJ|7e)*bGhTVhPtB)FQ^rH^>NRHtePA{tNrnUKv!g7#V#zgAK zhM^$$M~UYjI#uqZHQT&npiOz|S=a_8B9FnIy08{#^uOY6Sgz^xJd}vKF7`E06X5dSd*S~n$$c(n@6?M zX8Y7%S70l7z8iefG*hXh`Akzm<$F&|0wrbU4q4XsB41tty~zsZ&Dc?GN(+METSC5e zc4{DOfN(%6x)+=wJPM(2^%Q15uBHr4Qx9&F}pgzYXTlLYE=?{w{U z*=6B<8X%_I|JDi63ywTDn`|_mJRHZo#=HUgl&;>7e_{w*;j?PkR^?C*0(#o9rygmc z$q?`W6Eh(m$$Le&D8i-b?{oxTY)`gSdPeZo%-yMcWVN*)mcnQ;`Do#M0-KC4r;lGX zd#A#!EdjHhL?jg%hqhjfdXL@J-3=|#%!o`7tpG6*AhDeo^T=tIp15VLv4=*}nGTP&8;cLs230H{uq&2yQHy!@Sop!CUh;Ck1#n~B!+V9#`Eqp zGu@9bI!;_Wn|~O+%g|nZ(j8fynZ>rOQs!+DpH~|oHpx9Nn@AZe+5-C)N+^C+#?8n* zKvN%-QAsVfv*z0lP=x)4yf$jf3rgilPJXFLQX#`5;_!k0E3W1uRCqYshNW2a z8#wH<)oDBZ2ay$-##-zu)MaRyf2ZC)*(xrW5nCnPcB!08Q zvciYt_6Okc;7L(3XrMd^(M4e{t_*pL3#&) z4#ge63%KO6ZJ@hW!QS}FRXrA;ywEO^L}5duAf`t6fxIVWK^B`ih1pZr&RF~uiD91= zLmY<3c#~C|Y9~!b7^@M}BifSw-0PiWUG;4y->wx)>Gnn9_EgK)MOSn1>H`z3oEQ)s zYzOwPP~H)Rw$XmWg@+*M6EKrwZ1oGV$yWvUYTiF!K!$nM1)a=EsPG3vac(Z&YHeBH z^PPpHfJ3y&GBxPvW7cp7@ zD2mNga!Lx+J@L?9KQF7)8s!1Hyzt&Im~Qg-#pL$cT?boFQtaitf`ZW**a&5M!uLT1 zp7tXMoBiiN4c*~U?qp@&(Q`sv=Z&M6iHC(CO7Mb~Q_A_26*lLO2zb1wq>LA9W2 zX1=i-!KAgG8cmyOm850o*b<)h?8SkKV+qc^(WM~md-YUS2d`55XVZEmRYJ-OHi7=b zOd025s{s-i|4XnTV>LJ1H8U5$lBiPX1bI1e^l=!S6Hs5Z(&m7R9%_^;RfOO@pjp$B z*u?%$3TkS7c_i4-rt_tq_yU%5TvJSDOhi}xrh)(sckC7M^B)SBhc$VH2hYB0Exkn= zqf2XYILWrA?qopa;%g6kZv_nM{iN7uIGxU|h0#0nU2s9MzcUSnBh6AVeA1iV+&p_> zc<&{bA{H8%oG+15t*31in{(J&V_>y+EM17Cs2ax4YDQc>2x;`Sz-v#-6l%BX?RRmFN68hr-=<&6*m?@@rOdWcOl?bfqR`Qi+E&O+IJ&* z*_qU@=dF@@3fHF_T=;}j@4v_lDFG;tmQ^dVY;l#j)d2WdtP7va-W`s;FAEjic$37(W9FBDXR#_6+pK( zDe>)nD$DTK$O_YDSvr#-R6i61+e;&JwHcVdhx!r8Jp1k1Zw_X}a*> zuA7m?&MXoJY($#9wOBCT$GjoEg&en+5Qs8GD8di~Kw)T!awL$G@U1R#C*@~RY7%=A$?A1PU@Aosi37CxRd@4B@X zKPwjXD=yeCi0(=W|H3IXYCoLSF=EQ0-FQ;pE%z0#YA>|n`W9(%a(in6a@-o#3`t z6O*y-@ZTbr_^e-{tW%=1Ps63k8-ws|s|0E!*jQY0N?E^yXbr8Pu z1uqz(n$bokdGw@j_mxHOMd9dq4O-aL35|Vh$h@gnqRk@wnFe!pEFnI)FTtNqBVIIA z>er9}Dt2jm9|G_%9)gJ*8uIou?*{FT(0xxZc|2BV{*m(L!S=Prhj1||*=*GLLUDcR ziVD*l-th}?AEEL_RR_6CDpld(`R;SRs2q;1q#$*B7gSzttyihF;>mHQ%(_#*X^GIKo%00i zSeFrzQYM+Q>@nwSysHk}kKW;^;G0k%xX!|%nM&`KW>oZ?Ez4^hT3{t{Yi4|U2Qld21T*)7XsEJIoX!q@e zZv9@njbRIwm)-EbANf4NCd?1P8s!wysEtheZIcABKh+KTkR4cZ6ec9wFi^*$YLOAn-Cb|{ck&-olRQiY9!$6X0Nib>H5C)8jQ zOqVtziNXaUP6;%^`8}uS1q~!xRz327=|WZQN(E?uv1h}f-c#nL~*=u zp?1Z#8s}AR^c!zI+Oz_Qv17@Gu8MVOK+nKXOPQ&nwtw0uo6to{nW9&vHQGz>P2zCv zQ5q6JkVpb4MbYTS_kAtG2IgQ(OG5@_s4G9o*jU5HzB%iuPDB)OYaR}LZ(Buw^!p_N zMWx1?W6J1^usZMHs`zL|Rg^W4`xlDvNTJFy^A+9-HtrdgdSIbmw2-gPSx%H9W({_J z={{xbqUrCTeTY$pQj*)5$frK|ze3;7nwuPC!nanPM|5Y47Or(-5BEqB@; zZ1m12-I^#Ot(h-LGgRy(8L@AsfaHtSe38$2?wrcqQf|LB3y=?9ZbA?+7*P3bmxKzl^Onsg56IP!2j4rFcK;f>Y(&(?dtiP@W_wPZ zmeE#pBU>~_P^`TA(9ev#!sI3bDMP&FiZ5QVE;!cih!zIesgt%1$V69>=JqQAHZ`pc zUXoW)NG&Hk-g6si@A$^HDQ=S7ICy)bVw0@IN`@^|M=Bs0$fb2YE!&-Rt0J)!&w`f@ z%Hhd^Zs;|QDD882Lj8qms(!6Z>^JGq<*6?AA7;99f@uF)ukKodf}Y9J`Sb^n{OQJR zVD8(%BCb#nwqvB%*^2E@MNp?H!o|1K1{HfU>+x?DR1H0eh-jG3KEy=nFE0>y1RV4Y zybLroWUb&Xq88S0OB+!?7dHYo5s{pqEaB0`#>;}o&&Ao*Q`S$O?-z$G;rWa##>exE z#mh;a&p=a$M;Y#6!y_dsDJsqfq2}S?k@K*&mDPpa{u54U$@AHJdAZ4oiTV2aiuy{3 z!aeN7Kr%8iV&d1uu3r}+aEN&NyLwspiMV?5{~_dGa$q)|Rvr#+UJh_qo-?@?mT+$` zc|N`~MgRW$W1LiMTx1Ovs+dc;7Y;$nXXv+;BIUtniF|0~Se>fctmd3!kj zvdP*?%*NTq#m3dklYj&LtvZ5l{|)@FLHb#^{h{oa6aTPH?rh|;x;CD0XYVr?+;esC zf?SvTWzb*A|MAElC}p^dn}?03CjkZ#{}b{%?XUa>|4{>x{+Ev5iGKxXdpHm*wKy9W z1oWqZ-)Vp4H~c#VzY~80oVi|B$HC9W*#PE1aOf{9uFDYY_}iYpB9-CJa1T8<3oDzm zk^O@FM*1uFFFl68>5=*`J-_w*hP1Yl_4KxMw6XH~ZN3Pz_AC08EbPR7Bg={XEw>yY z7GyQy)(*D*Fbgjm$aQg$j0i|vL|j@QBq<9Lmz4&~iT#D=w-KE6D0|pgc)>mN;c#cj zFEd=unmiJsH$?x${?73ySWfILlYYyrKhpLWm5{d(YJ%2()c^=JJcHr^5fS&g8cb2& zk9aN9w_Nzva5M!ozoizBx_AyUmfE)38=$BmM9tWD4Oz|L+5DE8Y8e=wV?!J+Lz7Br z!xJaNYI(nn`T|iUNtRtb+--d%AAaySlnP3WrLZWOUS+vT&9eQZ@-l z2@+Gq$2SJh@b>c-TEH7Z_GxvS@89-uDS>SlJwU{K7J-P9df_WWQLzMCk`mGLWz|rWWY4GtZ z5S?riJMyFdftktXh&zU>B&Bxla{mrGTk1C z=37@1&|hMD4|-W;DoeEV5uvPmZ{&Tb)KCG3dre2O-A4znIhB|l&#w{nz|)g5s407hUvBE6;DKdcHV_(}<(WA$SvAa%SL;(`Q=gcu6zo6)v2g zN(*~S-_y(xLL~K@2Cm(c7K>x*X*8xho4k-A`}~CEiNlu1r*lcUdwB4NYEAFtjIm3C zhi~JQ1^LnXIRnbsjVOyC+0PC56Wjyf=|*(P$=)Q?#?cjnEyvFS@k7^OTUhf}$u6M+-uu5+U`m-4j?2CH4#OR| zowoInuxt()a>aTgS`X805>!6a&Wn_s?2e+#p7Vi~PrT2sAl97BIOGZ|R%*!jD1~4E z=9DM2;Q0In*w#le9+*ZzzM)-GqcEYTGDq&MBsYE^Ge}AiT*Y_zFx3p^=_+iY3+zJS zFi+`ERGCs8fPib$V)gqoO@;0w?e0JuYuDKXX;Uy)L3_Hy0^O_I;#c~-Lium^lBGu) z4+06oc9@A4j&x?5!Thf>oZqU|)w^u9OJ@h9QS*HpUtMbNTJh~ji$E9*^G5?xu=92%yE6p2)w=4HAfmY>eZ{c z614iRSF8Vxt9Qw+!!Vv7wV3H^>b;O=H}C^)&#L|N(M~vh$o}UQmnZqhUpo{mz7RrA znvox5+*J?AXs&G&k=4e-gJwCr6GET2KZMuqj0L5Erya6Tt*Tg-P?JkxOVXNhiC~o@+k7bWHo2uZu3GQ8TEz^&|tPV zrGER>vOLaTdnX@wQUrm~mibF9dQDcGBxfI^l-@xOzG>gD!PN&=jk?yb^d~Vi@73Xg z<8~jh3vJb>GJZB{!pN_?-$Hn8$^Pgh1hA{HYifOl!;7@XT5)oBDFF{{i5iwP;p$-j zyk3zC6&G8q5sz?b5AGcG43*$Z9T07(f~Q5gf)ORFnBz?qV6d0`howeRdX0idnqMfO z1^#Xp4dcul-#Mh}n6%|8OgDZ64?pNtpD)RzFObpsfnShrFy!*;Qe<-SHwo+uH;>7A zzE9lwp>EA(@UV$;$%Dq{K7}bLt3b}gVKh;uD@6&aYW4;>LZg|qZmk2uzJUij$#11A zO|pgPPQ=8yg{*sKm(W3vH=K@q1X|+uvn~$7{XTQNP7IpkuWEE}_aHq`Ko%Qv1#L9- z!wKnMtT|2b!-2so0^CgRcwbU2gs{A6tzitb^pPZJGVMEA?^mD8FKo1kAu0~Xafrc( zy$Wsy+`YkX>j|X-ojt)^pUY06G*BCLLo#w-1nz%lfbN;{d%ejgVvd&E4w~PUuOcQv z&fV2PUwNZ7q#HnPUv z`JmyjfB9@NFo!bmjm5vHxOS~}>&^nKNn_i~eFDcY7A@)lw@ zL{r2oA$jv8*42kH`pOPJx7XkyHL?q-R?8z@I(nf>E%q8r_P4tVn-el)Rl=rk4txSx zJmqzbptP{O1(gz){jj8=)go9&A2dbUPs+U!8epB^1yDy+r8IxXf4Rl1dvGbZN7;NI z$h*9I#KVZ{mY{9SgZQ7@5S@CZz_~$7W5z{ngMJgNE8(NXJ zcR}IZZ0t~MzSVcqDm~|3-bA2yNfgwzn}ad;k1b&nTrP&5<{c z`N_Q0@vC;wl9o^-T8yq`7pjf6{;ZUOx7#%}6}4EnspXY6YZv9w+*M1gItvrdAI5!l zNUYQO<8OG=Fl;(>=vrA=NQRvj^C!x^Wu_5iU=G+Cei+ zPPXT}Kb-KOU7+T@ixg7mXt_Dw9pkdHY^$SEhJ>|P>wV?ve$@4$bQ43NDBH(w!?xqs>vr40ymhKD~s0=tlG4RVS2<{kquXW5;DoN5W-$@dPjBg%8xC`Ig zANi(7B*b|<#t<2rQNvZgm5IAV1KMPePG^Xt>bp90lM1kME@-hZnU6Gbz6#OCxuuv)5@DnJwUE}3YYVSG@-(KtHhdjCy#ERFhs zqER0N^XUYB^AWgxL~~={E2eED)Ct=0Jv$X#mH6Od(Igo z!ef(6qA~qSU~qjlZNO()ZXgk)i(GT%p_h!rvKcuyhRC8Cem+N+0-odS8AEV^lO^jO zDK&kQm56wOhuOeV-j$UpQ~t1lu-3$9p<7&1ie>apy{(+gc_6dQ%Rgy}^T3~R>D?i2 z*+*l7X^iC0@22Zd=k7LZcnJIy0qZEJHgJ@y6-wQ?iA5z)?kASNa^&lKVITEYNLsM` z8<5`#sgVNkM}d3DV3h9a2TeOU1z#=aUN-8}q}Q#!Ao}bx#s2=pK18|z&yoi+Ngy#3 z(jW8MmN-xD8u0pOUnq8)lUi~Z>y)SSieyOye7=R3s|SS^Qb#gW*}_U!u3^#~lQ@d1 zDw3q{&GL!$wWf>DQJnbs5~e6akL^TDP~K8Kz=U_kb2RZ5NA}%_{&~;aG3gq@#4!5| zm-oesr#zC%#Jo8Ul4=prR`wpZ>*7T4Qog2tUwqCS-KcA-`#ynXHQYj zOx;~TsL#k6Ue5uxPh8^~>U*iP?4+oZ`Y!V~nGr?g1s}x04>MEfbo3Xb0H89?a8c@t zPNQz5vu^EEoLb^BjNDN^I7}(sk#9FMTNc9HQ2dG!E~E`WZ1+c0jPLS4j|Oq-AzR&8 zNP-lCgqSsi=YmxgKFTECT>lso=ee|p-3@+ca+%gqvm4DjIl8316=FtwS#+r@?I9y} zZ;7Ta)?CxT5n5DTK;l2g4o09lE`A0b{gM})6SF%f2(32NZVk%RpK3(`qjprIxdZIs zycRPimCkui(Y8cfs7}9Di2-a1HmfGJlbKW2ET4plrRGE+!G+pxN!{B=M`KtJs6ppK z7TR6CfEPH~4U7Szq`oEk7+D>g@v_g}yRsdli5cRLp1i)|Bl%5wTtut2aY=*~P`H*1Ijd7xDMky6U(%h^1hPE)w+ z<_!#C1MS|$zp>Lr@aVcg51reNK5AC_n}uCg%}#G49P*A2V_q&dIzOG08{h|Zmx+l7 zJYTlUZPY2Tq<;yD9wnR=cYY=?Q&5Ypyidg?Ga0mHuwTcpl5}xDv1;d%y7btWl(6L_ z2Yshn%nm^WiyXpA*Hj*#7=B4hW5_)W)-Eu#_AS0IpJRhw zM4?DVCw9lDn0a=2fXjkQBdFPDZ>}>au=db-m-@~w1Qp!I*zR2cUB;t2cLlEGva8dZ zIy=7jR0!aR+8up(PLE%yPcL&|UlB-;Kt(uyrRP3JI1YdBL!2K@U-Ttxxw6i+ts5_M zz_Y|LFTlwxM+JE4`krIU=Fv?IaFWw#EfXS0?fE(ftQoskzgJh%a`8|>FDYC(Kn$@N zSHf1m5!qjX;eIH#W*$CgNn@FO-%Jkn?A3)UynLdX`nD?kB)#XpaDdjF=y<_xG=x=` zJcMcEkqK?oaZXgEf;I;`RCg=2JPfGu%(@a^5lz^+zSb7J%2iZc*gT}DxdJ55L+fZb z@h{6ogx{5XVTp=BsYN?ACqb^Y)(`{oh<4-^guVighA^+svaxQ_JlUy(F46gsBE<~{ zU7=yt2p5U{7CP213&$_mr6dNDd98~ZUvLInem_8hEWaaW+nTwl+wveYNdABimv}`> z4dbHB*2+KA-8^qfQ^p2WCHkS3@85R8E+@>^&kL?C)|Gp3PEUpP&;=DV zR!q{!G+EL)q3=kIjpFt*0$tb(*C#IW(2ojEP&|`XS;WuxpY0*dJVOS0Le& zeY#yZm=QZo`a|3Rr0pVHb(4QFIi%jMopo{IXkxD{g2#LpG@uy%!<5Qy%e&E5^*f$XKp8X~)`%PwX`uOX8zM7k9 z1=GsF!!G<~@fp1BRhXMWh25go;7BKQ2> zNre!A_Q)=+mc*k;zI?Uk>5K}*GeKWM*_0P*Df@KO)73|9M^(rkPtIEoQ@!^fIj!{M zz1ChaX!2Fv<>kraqHdm`@vgbB4kzwsl&p6I*@?}4u19ioul%6cyZ%1=pv_J|=E*`< z;JP7PHyUYP!!d%`_`xZI*ie|0#Wmnh>jrBw)@z|?|68_G04`rS8QMzT#@?)8CxgiL zB+u7GQ&i;f2{lC*<#vj-P1Nfxz#66cP<8%~z5&?eNa-wgk8 z+XE@@k7Jr(gcd!2V^sCJOw+p;xU$%f9x$luLtBzavia-kQAU$kMlf_Q_RwuhbeAp! z>jHt1C4k8K@F*8*zWiIO)~cTNLgME;4=#tI&&3%EDJzZCk@zKS!G6e*4bMLX%5 zAKg#?LD`Cuo8NEZzB@k7D|jG@!?ENMO4Y@J29NpgpBnc>C>mK@gQzJh%W9Mlx2L;` z<1cJ(i&0S$gM}oSzpI5oIA9*SO_|ow+eKWA45%Ij$W%+#aNoy6N275shS-HBhG`1$ z#-oDC%IuTXiS+w}i4{Tjk*yw=ZRaSYpncJLXnFmT5iaFI% znvjut9(Hul{IwP0$GT8#vQ;18iAgEYC3|59Z+wWfO*3>gIs{{#IUNOH`w=HKlPBwj z{7~g3>8j{@Pobe+$h|8;s~?UDn?YB%IQp#Bc&{hf>#h45FXrs34*F>knEEAOCF8>j z(oz)ER2&tc*Hx)fHv3!QY_&Pu#Ip}>VOO~rdm;DR-J`Yv(hFv5eeg4iFBBc9 zI3f^9t1Vt5wT<@L{n8?pt7#ZmJ$A?}9@y4t74i66$#{1MB)dq)y%R+iS356feAtt| z>o>vB61o!^cW)pGSf07XqTDm)%$!uO^wdh4C7R|dN)w@;N&;lLAZjc58a<*tZO)Fj zq$XQw6Nr&Gu$V)tJqcdDFi^lFfodaz$`)@HR;O*IbvXKomuhb%nH8zBggf}(MKa{& zR)fz!FN|8&bC{CmBVs}vkciHOJxpWs$C!PgsOh5yBt30!(?yiKCOpwRHYY3Zr2i5P zsU(z&`YK?YVRMsr6GZAqpVWLHH}iP5((+*qgS7rlrG)9#)Cgoo%+;+fRc$u6B|B=W zDstBW?<+dy0F8(kwHPGX-c_a6ygSrx5+CeKjlYtX41FW5lB2#x&ZMZUnwAJNEs_$k z;n9qE-_AIhD%-suCU)7;$DjuoefR!#I8tRk|ara;fLefH=K|xxEy9r;#9>Tb;8u$6}7slzn0;DlerUjsa%xF zU?ma>;J!^|3&1AlCBsL$;|%w$*%=M!$OasFXGm5FM+t9=N!>UASA5$w3!k-AUtwc9 z=~s}lK(c}5yOj$bZ;ivqBhDx4I>pdQVp4UYuoY{|aw-yeTLtn|C`#e6t=tiSn}R1k!+?0^qmESHN?s<<(s?JLvOREg0t%8$WSFo7zRgpnViOeWX-FpPqY_! z&;ld0`3Tok00{pb1rh+^eI{k>NKzV@4B3dfd9D-Q=h|aHsYbr9ry;pc1_#c7)`0$^w`G3ij#t>qT>`x9)ud$V$ Q{US!~mKLl`$vpgj0C85IdH?_b diff --git a/docs/img/premium/rollbar-readme.png b/docs/img/premium/rollbar-readme.png index 630cddb32d2d3b6957b3fc4b7b57df0fed6546dd..c1d6e98d56b8c354aef244c57026d544674b4c67 100644 GIT binary patch delta 16399 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 I7BdO{U-{g7_5c6? delta 16434 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 diff --git a/docs/img/premium/sentry-readme.png b/docs/img/premium/sentry-readme.png index b322e37357a0bc12bc3877bbdab7faefd04e01bf..e4b5b8f34c3df2687691ed2eeff370d863255fcb 100644 GIT binary patch literal 24549 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 Kbe*JW$bSHrTxjS3 delta 23677 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 VahWN+pZdhnRF$+8>*OI}{{w!hCp-WE diff --git a/docs/img/premium/stream-readme.png b/docs/img/premium/stream-readme.png index 967ee7fc8dc36121a7e9206e5d6de297345cb1b4..0ca650eaa85c0b9675870ff6467d8e346c9e830b 100644 GIT binary patch literal 19281 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 literal 19311 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 Date: Tue, 8 Oct 2019 19:38:14 +0300 Subject: [PATCH 30/79] Fix minor grammatical errors in docs (#6979) --- docs/api-guide/fields.md | 2 +- docs/api-guide/serializers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 19abb04249..92c692bf5e 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -52,7 +52,7 @@ The `default` is not applied during partial update operations. In the partial up May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context). -When serializing the instance, default will be used if the the object attribute or dictionary key is not present in the instance. +When serializing the instance, default will be used if the object attribute or dictionary key is not present in the instance. Note that setting a `default` value implies that the field is not required. Including both the `default` and `required` keyword arguments is invalid and will raise an error. diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index ef70adbe13..d6d4d5e8cf 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1014,7 +1014,7 @@ The signatures for these methods are as follows: Takes the object instance that requires serialization, and should return a primitive representation. Typically this means returning a structure of built-in Python datatypes. The exact types that can be handled will depend on the render classes you have configured for your API. -May be overridden in order modify the representation style. For example: +May be overridden in order to modify the representation style. For example: def to_representation(self, instance): """Convert `username` to lowercase.""" From 9850441e6f58d67f4d533ee144aeef54724ac661 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Tue, 8 Oct 2019 16:37:58 -0700 Subject: [PATCH 31/79] Fix docs typo (#6974) --- docs/topics/documenting-your-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index 5cdf631a6f..cf3977d902 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -74,7 +74,7 @@ See the [Swagger UI documentation][swagger-ui] for advanced usage. ### A minimal example with ReDoc. Assuming you've followed the example from the schemas documentation for routing -a dynamic `SchemaView`, a minimal Django template for using Swagger UI might be +a dynamic `SchemaView`, a minimal Django template for using ReDoc might be this: ```html From 0fd72f17ee18506c02b4c0f0e4af368e3bedac3e Mon Sep 17 00:00:00 2001 From: Guilherme Munarolo Date: Thu, 10 Oct 2019 03:50:20 -0300 Subject: [PATCH 32/79] Fixed crash deleting required schema parameter key on PATCH requests. (#6944) Closes #6941 --- rest_framework/schemas/openapi.py | 4 ++-- tests/schemas/test_openapi.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index ac846bf801..09a5598f59 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -387,7 +387,7 @@ def _map_serializer(self, serializer): result = { 'properties': properties } - if len(required) > 0: + if required: result['required'] = required return result @@ -463,7 +463,7 @@ def _get_request_body(self, path, method): content = self._map_serializer(serializer) # No required fields for PATCH if method == 'PATCH': - del content['required'] + content.pop('required', None) # No read_only fields for request. for name, schema in content['properties'].copy().items(): if 'readOnly' in schema: diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index d9375585bd..e1d29f6fe9 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -169,6 +169,31 @@ class View(generics.GenericAPIView): for response in inspector._get_responses(path, method).values(): assert 'required' not in response['content']['application/json']['schema'] + def test_empty_required_with_patch_method(self): + path = '/' + method = 'PATCH' + + class Serializer(serializers.Serializer): + read_only = serializers.CharField(read_only=True) + write_only = serializers.CharField(write_only=True, required=False) + + class View(generics.GenericAPIView): + serializer_class = Serializer + + view = create_view( + View, + method, + create_request(path) + ) + inspector = AutoSchema() + inspector.view = view + + request_body = inspector._get_request_body(path, method) + # there should be no empty 'required' property, see #6834 + assert 'required' not in request_body['content']['application/json']['schema'] + for response in inspector._get_responses(path, method).values(): + assert 'required' not in response['content']['application/json']['schema'] + def test_response_body_generation(self): path = '/' method = 'POST' From 3aa1089a6af6774c1e0b12977b85062f837998ca Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Tue, 15 Oct 2019 10:47:05 -0400 Subject: [PATCH 33/79] Update serializer docs to use correct param name (#6995) Avoids a pylint warning ".to_representation: Parameters differ from overridden 'to_representation' method" if people copy these examples. --- docs/api-guide/serializers.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index d6d4d5e8cf..4679b1ed16 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -887,10 +887,10 @@ To implement a read-only serializer using the `BaseSerializer` class, we just ne It's simple to create a read-only serializer for converting `HighScore` instances into primitive data types. class HighScoreSerializer(serializers.BaseSerializer): - def to_representation(self, obj): + def to_representation(self, instance): return { - 'score': obj.score, - 'player_name': obj.player_name + 'score': instance.score, + 'player_name': instance.player_name } We can now use this class to serialize single `HighScore` instances: @@ -945,10 +945,10 @@ Here's a complete example of our previous `HighScoreSerializer`, that's been upd 'player_name': player_name } - def to_representation(self, obj): + def to_representation(self, instance): return { - 'score': obj.score, - 'player_name': obj.player_name + 'score': instance.score, + 'player_name': instance.player_name } def create(self, validated_data): @@ -965,10 +965,10 @@ The following class is an example of a generic serializer that can handle coerci A read-only serializer that coerces arbitrary complex objects into primitive representations. """ - def to_representation(self, obj): + def to_representation(self, instance): output = {} - for attribute_name in dir(obj): - attribute = getattr(obj, attribute_name) + for attribute_name in dir(instance): + attribute = getattr(instance, attribute_name) if attribute_name.startswith('_'): # Ignore private attributes. pass @@ -1010,7 +1010,7 @@ Some reasons this might be useful include... The signatures for these methods are as follows: -#### `.to_representation(self, obj)` +#### `.to_representation(self, instance)` Takes the object instance that requires serialization, and should return a primitive representation. Typically this means returning a structure of built-in Python datatypes. The exact types that can be handled will depend on the render classes you have configured for your API. From f98b6f35770c6cf7dd964d0fa7b052f6f86d5253 Mon Sep 17 00:00:00 2001 From: zach valenta Date: Thu, 17 Oct 2019 08:58:12 -0400 Subject: [PATCH 34/79] proper Github repo issue already resolved (#6999) per the ticket anyway https://github.com/encode/django-rest-framework/issues/2162 --- docs/community/project-management.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/community/project-management.md b/docs/community/project-management.md index 5d7dab5612..293c65e246 100644 --- a/docs/community/project-management.md +++ b/docs/community/project-management.md @@ -195,7 +195,6 @@ If `@tomchristie` ceases to participate in the project then `@j4mie` has respons The following issues still need to be addressed: -* [Consider moving the repo into a proper GitHub organization][github-org]. * Ensure `@jamie` has back-up access to the `django-rest-framework.org` domain setup and admin. * Document ownership of the [live example][sandbox] API. * Document ownership of the [mailing list][mailing-list] and IRC channel. @@ -206,6 +205,5 @@ The following issues still need to be addressed: [transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/ [transifex-client]: https://pypi.org/project/transifex-client/ [translation-memory]: http://docs.transifex.com/guides/tm#let-tm-automatically-populate-translations -[github-org]: https://github.com/encode/django-rest-framework/issues/2162 [sandbox]: https://restframework.herokuapp.com/ [mailing-list]: https://groups.google.com/forum/#!forum/django-rest-framework From a734e58d44b960070ab4f4c826db3738fedb8b82 Mon Sep 17 00:00:00 2001 From: Chris Guo <41265033+chrisguox@users.noreply.github.com> Date: Tue, 22 Oct 2019 06:11:12 +0800 Subject: [PATCH 35/79] Fix docs typos (#7006) --- docs/community/3.10-announcement.md | 2 +- docs/topics/documenting-your-api.md | 2 +- tests/test_relations.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/community/3.10-announcement.md b/docs/community/3.10-announcement.md index 065dd3480a..578e900dc1 100644 --- a/docs/community/3.10-announcement.md +++ b/docs/community/3.10-announcement.md @@ -84,7 +84,7 @@ urlpatterns = [ ### Customization -For customizations that you want to apply across the the entire API, you can subclass `rest_framework.schemas.openapi.SchemaGenerator` and provide it as an argument +For customizations that you want to apply across the entire API, you can subclass `rest_framework.schemas.openapi.SchemaGenerator` and provide it as an argument to the `generateschema` command or `get_schema_view()` helper function. For specific per-view customizations, you can subclass `AutoSchema`, diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index cf3977d902..b4c7dea4dc 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -221,7 +221,7 @@ If the python `Markdown` library is installed, then [markdown syntax][markdown] [ref]: http://example.com/activating-accounts """ -Note that when using viewsets the basic docstring is used for all generated views. To provide descriptions for each view, such as for the the list and retrieve views, use docstring sections as described in [Schemas as documentation: Examples][schemas-examples]. +Note that when using viewsets the basic docstring is used for all generated views. To provide descriptions for each view, such as for the list and retrieve views, use docstring sections as described in [Schemas as documentation: Examples][schemas-examples]. #### The `OPTIONS` method diff --git a/tests/test_relations.py b/tests/test_relations.py index 3281b7ea22..c892934157 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -251,7 +251,7 @@ def test_representation_with_format(self): def test_improperly_configured(self): """ If a matching view cannot be reversed with the given instance, - the the user has misconfigured something, as the URL conf and the + the user has misconfigured something, as the URL conf and the hyperlinked field do not match. """ self.field.reverse = fail_reverse From 43397a81aec2b10ece63099f9f89c51d56785c35 Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 22 Oct 2019 12:06:37 +0300 Subject: [PATCH 36/79] Fixed decimal snan deserialization (#7002) * Added test case causes exception in DecimalField deserialization * Fixed NaN checking which throws exception with sNaN value --- rest_framework/fields.py | 4 +--- tests/test_fields.py | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index c0ceebe3b7..ea8f47b2d9 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1062,9 +1062,7 @@ def to_internal_value(self, data): except decimal.DecimalException: self.fail('invalid') - # Check for NaN. It is the only value that isn't equal to itself, - # so we can use this to identify NaN values. - if value != value: + if value.is_nan(): self.fail('invalid') # Check for infinity and negative infinity. diff --git a/tests/test_fields.py b/tests/test_fields.py index 7c495cd637..1d302b730e 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1080,6 +1080,7 @@ class TestDecimalField(FieldValues): invalid_inputs = ( ('abc', ["A valid number is required."]), (Decimal('Nan'), ["A valid number is required."]), + (Decimal('Snan'), ["A valid number is required."]), (Decimal('Inf'), ["A valid number is required."]), ('12.345', ["Ensure that there are no more than 3 digits in total."]), (200000000000.0, ["Ensure that there are no more than 3 digits in total."]), From 65ed7be7540e54b21dbe616cbfe9df0da7caae93 Mon Sep 17 00:00:00 2001 From: nautikos1235 <4201782+nautikos1235@users.noreply.github.com> Date: Tue, 22 Oct 2019 20:10:15 +0200 Subject: [PATCH 37/79] Fix markdown code indent in schemas (#7009) --- docs/api-guide/schemas.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index bdbb952dc5..76cc0ca4d3 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -90,6 +90,7 @@ The `get_schema_view()` helper takes the following keyword arguments: url='https://www.example.org/api/', urlconf='myproject.urls' ) + * `patterns`: List of url patterns to limit the schema introspection to. If you only want the `myproject.api` urls to be exposed in the schema: From a8c86be660462bfcea6e2397e408c5f29f25a1f9 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 22 Oct 2019 11:18:51 -0700 Subject: [PATCH 38/79] Update linter requirements (#7010) --- requirements/requirements-codestyle.txt | 8 +++---- rest_framework/schemas/__init__.py | 2 +- setup.cfg | 2 +- tests/schemas/test_coreapi.py | 2 +- tests/test_permissions.py | 32 ++++++++++++------------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/requirements/requirements-codestyle.txt b/requirements/requirements-codestyle.txt index 8cbd41c50f..482deac667 100644 --- a/requirements/requirements-codestyle.txt +++ b/requirements/requirements-codestyle.txt @@ -1,7 +1,7 @@ # PEP8 code linting, which we run on all commits. -flake8==3.5.0 -flake8-tidy-imports==1.1.0 -pycodestyle==2.3.1 +flake8==3.7.8 +flake8-tidy-imports==3.0.0 +pycodestyle==2.5.0 # Sort and lint imports -isort==4.3.3 +isort==4.3.21 diff --git a/rest_framework/schemas/__init__.py b/rest_framework/schemas/__init__.py index 588680362d..b63cb23536 100644 --- a/rest_framework/schemas/__init__.py +++ b/rest_framework/schemas/__init__.py @@ -23,8 +23,8 @@ from rest_framework.settings import api_settings from . import coreapi, openapi -from .inspectors import DefaultSchema # noqa from .coreapi import AutoSchema, ManualSchema, SchemaGenerator # noqa +from .inspectors import DefaultSchema # noqa def get_schema_view( diff --git a/setup.cfg b/setup.cfg index c021fdde0d..81da18b1c1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ addopts=--tb=short --strict -ra testspath = tests [flake8] -ignore = E501 +ignore = E501,W504 banned-modules = json = use from rest_framework.utils import json! [isort] diff --git a/tests/schemas/test_coreapi.py b/tests/schemas/test_coreapi.py index 66275ade95..a634d69685 100644 --- a/tests/schemas/test_coreapi.py +++ b/tests/schemas/test_coreapi.py @@ -24,8 +24,8 @@ from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet, ModelViewSet -from . import views from ..models import BasicModel, ForeignKeySource, ManyToManySource +from . import views factory = APIRequestFactory() diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 03b80aae8a..b6178c0bbc 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -494,28 +494,28 @@ def setUp(self): self.custom_message = 'Custom: You cannot access this resource' def test_permission_denied(self): - response = denied_view(self.request, pk=1) - detail = response.data.get('detail') - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertNotEqual(detail, self.custom_message) + response = denied_view(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertNotEqual(detail, self.custom_message) def test_permission_denied_with_custom_detail(self): - response = denied_view_with_detail(self.request, pk=1) - detail = response.data.get('detail') - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(detail, self.custom_message) + response = denied_view_with_detail(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(detail, self.custom_message) def test_permission_denied_for_object(self): - response = denied_object_view(self.request, pk=1) - detail = response.data.get('detail') - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertNotEqual(detail, self.custom_message) + response = denied_object_view(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertNotEqual(detail, self.custom_message) def test_permission_denied_for_object_with_custom_detail(self): - response = denied_object_view_with_detail(self.request, pk=1) - detail = response.data.get('detail') - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(detail, self.custom_message) + response = denied_object_view_with_detail(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(detail, self.custom_message) class PermissionsCompositionTests(TestCase): From 64f567a021cc005c9a8ad5230f0fea1e3b30ad70 Mon Sep 17 00:00:00 2001 From: Bastien Vallet Date: Tue, 22 Oct 2019 23:39:01 +0200 Subject: [PATCH 39/79] Bump CI to Python 3.8 (#7008) --- .travis.yml | 10 ++++++---- setup.py | 1 + tox.ini | 4 +--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index a4a4ed8b5b..f89e775310 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,11 +21,13 @@ matrix: - { python: "3.7", env: DJANGO=2.2 } - { python: "3.7", env: DJANGO=master } - - { python: "3.7", env: TOXENV=base } - - { python: "3.7", env: TOXENV=lint } - - { python: "3.7", env: TOXENV=docs } + - { python: "3.8", env: DJANGO=master } - - python: "3.7" + - { python: "3.8", env: TOXENV=base } + - { python: "3.8", env: TOXENV=lint } + - { python: "3.8", env: TOXENV=docs } + + - python: "3.8" env: TOXENV=dist script: - python setup.py bdist_wheel diff --git a/setup.py b/setup.py index 2f8dafd218..c9d6443d5b 100755 --- a/setup.py +++ b/setup.py @@ -101,6 +101,7 @@ def get_version(package): 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3 :: Only', 'Topic :: Internet :: WWW/HTTP', ], diff --git a/tox.ini b/tox.ini index 699ca909c9..587c469b1b 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ envlist = {py35,py36,py37}-django20, {py35,py36,py37}-django21 {py35,py36,py37}-django22 - {py36,py37}-djangomaster, + {py36,py37,py38}-djangomaster, base,dist,lint,docs, [travis:env] @@ -44,14 +44,12 @@ deps = -rrequirements/requirements-optionals.txt [testenv:lint] -basepython = python3.7 commands = ./runtests.py --lintonly deps = -rrequirements/requirements-codestyle.txt -rrequirements/requirements-testing.txt [testenv:docs] -basepython = python3.7 skip_install = true commands = mkdocs build deps = From 4d57cd31f64dcfa71958a77d7adffd7a87e16e9b Mon Sep 17 00:00:00 2001 From: Aaron Yong Date: Thu, 24 Oct 2019 00:54:37 -0600 Subject: [PATCH 40/79] Update method override example in Schemas docs (#6887) (#7013) get_link() was a method in the old CoreAPI-based AutoSchema implementation. The new OpenAPI one defines get_operation() instead: the example code block was overlooked. --- docs/api-guide/schemas.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 76cc0ca4d3..de12ba5178 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -115,7 +115,6 @@ The `get_schema_view()` helper takes the following keyword arguments: * `renderer_classes`: May be used to pass the set of renderer classes that can be used to render the API root endpoint. - ## Customizing Schema Generation You may customize schema generation at the level of the schema as a whole, or @@ -155,7 +154,7 @@ Returns a dictionary that represents the OpenAPI schema: The `request` argument is optional, and may be used if you want to apply per-user permissions to the resulting schema generation. -This is a good point to override if you want to customise the generated +This is a good point to override if you want to customize the generated dictionary, for example to add custom [specification extensions][openapi-specification-extensions]. @@ -184,14 +183,13 @@ provide richer path field descriptions. (The key hooks here are the relevant --- -In order to customise the operation generation, you should provide an `AutoSchema` subclass, overriding `get_operation()` as you need: - +In order to customize the operation generation, you should provide an `AutoSchema` subclass, overriding `get_operation()` as you need: from rest_framework.views import APIView from rest_framework.schemas.openapi import AutoSchema class CustomSchema(AutoSchema): - def get_link(...): + def get_operation(...): # Implement custom introspection here (or in other sub-methods) class CustomView(APIView): @@ -219,4 +217,4 @@ project you may adjust `settings.DEFAULT_SCHEMA_CLASS` appropriately. [openapi]: https://github.com/OAI/OpenAPI-Specification [openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions -[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject \ No newline at end of file +[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject From c9f06bf73f371c2ae30cc799f4ac3c5f7eca0303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Thu, 24 Oct 2019 10:51:16 +0200 Subject: [PATCH 41/79] Fix a spelling error in openapi AutoSchema method (#7004) Replace "pagninator" by "paginator". --- rest_framework/schemas/openapi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 09a5598f59..0fd33cd118 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -209,7 +209,7 @@ def _get_pagination_parameters(self, path, method): if not is_list_view(path, method, view): return [] - paginator = self._get_pagninator() + paginator = self._get_paginator() if not paginator: return [] @@ -429,7 +429,7 @@ def _map_field_validators(self, field, schema): schema['maximum'] = int(digits * '9') + 1 schema['minimum'] = -schema['maximum'] - def _get_pagninator(self): + def _get_paginator(self): pagination_class = getattr(self.view, 'pagination_class', None) if pagination_class: return pagination_class() @@ -502,7 +502,7 @@ def _get_responses(self, path, method): 'type': 'array', 'items': item_schema, } - paginator = self._get_pagninator() + paginator = self._get_paginator() if paginator: response_schema = paginator.get_paginated_response_schema(response_schema) else: From 5ee970c090d76c684c03d494ec167571f40334a4 Mon Sep 17 00:00:00 2001 From: Chris Guo <41265033+chrisguox@users.noreply.github.com> Date: Fri, 25 Oct 2019 02:31:12 +0800 Subject: [PATCH 42/79] Fix docs typos (#7015) --- docs/api-guide/fields.md | 12 ++++++------ docs/api-guide/requests.md | 2 +- docs/api-guide/schemas.md | 2 +- docs/community/release-notes.md | 6 +++--- docs/community/third-party-packages.md | 2 +- docs/coreapi/schemas.md | 2 +- tests/test_serializer.py | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 92c692bf5e..29cb5aec94 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -713,7 +713,7 @@ the coordinate pair: fields = ['label', 'coordinates'] Note that this example doesn't handle validation. Partly for that reason, in a -real project, the coordinate nesting might be better handled with a nested serialiser +real project, the coordinate nesting might be better handled with a nested serializer using `source='*'`, with two `IntegerField` instances, each with their own `source` pointing to the relevant field. @@ -746,7 +746,7 @@ suitable for updating our target object. With `source='*'`, the return from ('y_coordinate', 4), ('x_coordinate', 3)]) -For completeness lets do the same thing again but with the nested serialiser +For completeness lets do the same thing again but with the nested serializer approach suggested above: class NestedCoordinateSerializer(serializers.Serializer): @@ -768,14 +768,14 @@ declarations. It's our `NestedCoordinateSerializer` that takes `source='*'`. Our new `DataPointSerializer` exhibits the same behaviour as the custom field approach. -Serialising: +Serializing: >>> out_serializer = DataPointSerializer(instance) >>> out_serializer.data ReturnDict([('label', 'testing'), ('coordinates', OrderedDict([('x', 1), ('y', 2)]))]) -Deserialising: +Deserializing: >>> in_serializer = DataPointSerializer(data=data) >>> in_serializer.is_valid() @@ -802,8 +802,8 @@ But we also get the built-in validation for free: {'x': ['A valid integer is required.'], 'y': ['A valid integer is required.']})]) -For this reason, the nested serialiser approach would be the first to try. You -would use the custom field approach when the nested serialiser becomes infeasible +For this reason, the nested serializer approach would be the first to try. You +would use the custom field approach when the nested serializer becomes infeasible or overly complex. diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 3bc0838939..1c336953ca 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -49,7 +49,7 @@ If a client sends a request with a content-type that cannot be parsed then a `Un # Content negotiation -The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialisation schemes for different media types. +The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialization schemes for different media types. ## .accepted_renderer diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index de12ba5178..ec5366d8eb 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -176,7 +176,7 @@ for each view, allowed method, and path. **Note**: For basic `APIView` subclasses, default introspection is essentially limited to the URL kwarg path parameters. For `GenericAPIView` subclasses, which includes all the provided class based views, `AutoSchema` will -attempt to introspect serialiser, pagination and filter fields, as well as +attempt to introspect serializer, pagination and filter fields, as well as provide richer path field descriptions. (The key hooks here are the relevant `GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`, `filter_backends` and so on.) diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index cdaa350447..283dbae671 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -222,11 +222,11 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10. def perform_create(self, serializer): serializer.save(owner=self.request.user) - Alternatively you may override `save()` or `create()` or `update()` on the serialiser as appropriate. + Alternatively you may override `save()` or `create()` or `update()` on the serializer as appropriate. * Correct allow_null behaviour when required=False [#5888][gh5888] - Without an explicit `default`, `allow_null` implies a default of `null` for outgoing serialisation. Previously such + Without an explicit `default`, `allow_null` implies a default of `null` for outgoing serialization. Previously such fields were being skipped when read-only or otherwise not required. **Possible backwards compatibility break** if you were relying on such fields being excluded from the outgoing @@ -464,7 +464,7 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10. * Deprecated `exclude_from_schema` on `APIView` and `api_view` decorator. Set `schema = None` or `@schema(None)` as appropriate. [#5422][gh5422] * Timezone-aware `DateTimeField`s now respect active or default `timezone` during serialization, instead of always using UTC. [#5435][gh5435] - Resolves inconsistency whereby instances were serialised with supplied datetime for `create` but UTC for `retrieve`. [#3732][gh3732] + Resolves inconsistency whereby instances were serialized with supplied datetime for `create` but UTC for `retrieve`. [#3732][gh3732] **Possible backwards compatibility break** if you were relying on datetime strings being UTC. Have client interpret datetimes or [set default or active timezone (docs)][djangodocs-set-timezone] to UTC if needed. diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index 9d7d09d9bc..10a67840c4 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -254,7 +254,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque ### Misc * [cookiecutter-django-rest][cookiecutter-django-rest] - A cookiecutter template that takes care of the setup and configuration so you can focus on making your REST apis awesome. -* [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer. +* [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serializer that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer. * [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI. * [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server. * [gaiarestframework][gaiarestframework] - Utils for django-rest-framework diff --git a/docs/coreapi/schemas.md b/docs/coreapi/schemas.md index 6ee6203433..69606f8532 100644 --- a/docs/coreapi/schemas.md +++ b/docs/coreapi/schemas.md @@ -191,7 +191,7 @@ each view, allowed method and path.) **Note**: For basic `APIView` subclasses, default introspection is essentially limited to the URL kwarg path parameters. For `GenericAPIView` subclasses, which includes all the provided class based views, `AutoSchema` will -attempt to introspect serialiser, pagination and filter fields, as well as +attempt to introspect serializer, pagination and filter fields, as well as provide richer path field descriptions. (The key hooks here are the relevant `GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`, `filter_backends` and so on.) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 0d4b50c1dd..fab0472b94 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -555,7 +555,7 @@ class Serializer(serializers.Serializer): bar = serializers.CharField(source='foo.bar', allow_null=True) optional = serializers.CharField(required=False, allow_null=True) - # allow_null=True should imply default=None when serialising: + # allow_null=True should imply default=None when serializing: assert Serializer({'foo': None}).data == {'foo': None, 'bar': None, 'optional': None, } From 39bd6cc5cb696d4ba3ab1e7066bda2c4331ff17e Mon Sep 17 00:00:00 2001 From: Erwan Rouchet Date: Mon, 28 Oct 2019 02:13:01 +0100 Subject: [PATCH 43/79] Set the proper JSON schema type for HStoreFields in OpenAPI schemas (#6914) --- rest_framework/schemas/openapi.py | 1 + tests/schemas/test_openapi.py | 16 ++++++++++++++++ tests/schemas/views.py | 1 + 3 files changed, 18 insertions(+) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 0fd33cd118..a3970ff7ae 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -344,6 +344,7 @@ def _map_field(self, field): serializers.BooleanField: 'boolean', serializers.JSONField: 'object', serializers.DictField: 'object', + serializers.HStoreField: 'object', } return {'type': FIELD_CLASS_SCHEMA_TYPE.get(field.__class__, 'string')} diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index e1d29f6fe9..78c00d9850 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -462,6 +462,22 @@ def test_serializer_datefield(self): assert properties['date']['format'] == 'date' assert properties['datetime']['format'] == 'date-time' + def test_serializer_hstorefield(self): + path = '/' + method = 'GET' + view = create_view( + views.ExampleGenericAPIView, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + responses = inspector._get_responses(path, method) + response_schema = responses['200']['content']['application/json']['schema'] + properties = response_schema['items']['properties'] + assert properties['hstore']['type'] == 'object' + def test_serializer_validators(self): path = '/' method = 'GET' diff --git a/tests/schemas/views.py b/tests/schemas/views.py index d1fc75eb81..6b83e5bde0 100644 --- a/tests/schemas/views.py +++ b/tests/schemas/views.py @@ -33,6 +33,7 @@ def get(self, *args, **kwargs): class ExampleSerializer(serializers.Serializer): date = serializers.DateField() datetime = serializers.DateTimeField() + hstore = serializers.HStoreField() class ExampleGenericAPIView(generics.GenericAPIView): From ab40b80fa64d0448cdfbe9125a77bae39085d3f5 Mon Sep 17 00:00:00 2001 From: Daniel Hnyk Date: Mon, 28 Oct 2019 11:46:44 +0100 Subject: [PATCH 44/79] Remove unmaintained django-rest-framework-bulk from docs (#7021) --- docs/api-guide/generic-views.md | 5 ----- docs/community/third-party-packages.md | 2 -- 2 files changed, 7 deletions(-) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 8d9ead1078..a2f19ff2e2 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -378,10 +378,6 @@ If you need to generic PUT-as-create behavior you may want to include something The following third party packages provide additional generic view implementations. -## Django REST Framework bulk - -The [django-rest-framework-bulk package][django-rest-framework-bulk] implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests. - ## Django Rest Multiple Models [Django Rest Multiple Models][django-rest-multiple-models] provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request. @@ -394,5 +390,4 @@ The [django-rest-framework-bulk package][django-rest-framework-bulk] implements [RetrieveModelMixin]: #retrievemodelmixin [UpdateModelMixin]: #updatemodelmixin [DestroyModelMixin]: #destroymodelmixin -[django-rest-framework-bulk]: https://github.com/miki725/django-rest-framework-bulk [django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index 10a67840c4..ea5d6b854f 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -222,7 +222,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque ### Views -* [djangorestframework-bulk][djangorestframework-bulk] - Implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests. * [django-rest-multiple-models][django-rest-multiple-models] - Provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request. ### Routers @@ -305,7 +304,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [djangorestframework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore [drf-compound-fields]: https://github.com/estebistec/drf-compound-fields [django-extra-fields]: https://github.com/Hipo/drf-extra-fields -[djangorestframework-bulk]: https://github.com/miki725/django-rest-framework-bulk [django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels [drf-nested-routers]: https://github.com/alanjds/drf-nested-routers [wq.db.rest]: https://wq.io/docs/about-rest From 5521eacb024e8b477652a7c81eea88c941e0c322 Mon Sep 17 00:00:00 2001 From: Chris Guo <41265033+chrisguox@users.noreply.github.com> Date: Sat, 2 Nov 2019 01:56:59 +0800 Subject: [PATCH 45/79] Update docs imports (#7030) --- docs/api-guide/caching.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/caching.md b/docs/api-guide/caching.md index 502a0a9a94..96517b15ee 100644 --- a/docs/api-guide/caching.md +++ b/docs/api-guide/caching.md @@ -17,11 +17,16 @@ other cache decorators such as [`cache_page`][page] and [`vary_on_cookie`][cookie]. ```python +from django.utils.decorators import method_decorator +from django.views.decorators.cache import cache_page +from django.views.decorators.vary import vary_on_cookie + from rest_framework.response import Response from rest_framework.views import APIView from rest_framework import viewsets -class UserViewSet(viewsets.Viewset): + +class UserViewSet(viewsets.ViewSet): # Cache requested url for each user for 2 hours @method_decorator(cache_page(60*60*2)) @@ -32,6 +37,7 @@ class UserViewSet(viewsets.Viewset): } return Response(content) + class PostView(APIView): # Cache page for the requested url From 82f256989559b95a0cd5ba318a95f7c66945436a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 5 Nov 2019 16:43:32 +0000 Subject: [PATCH 46/79] Update __init__.py --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 4d4225c964..2b96e7336e 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -10,7 +10,7 @@ __title__ = 'Django REST framework' __version__ = '3.10.3' __author__ = 'Tom Christie' -__license__ = 'BSD 2-Clause' +__license__ = 'BSD 3-Clause' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' # Version synonym From 5e8fe6edf0b25506c5bc3ce749c5b9d5cb9e7e7e Mon Sep 17 00:00:00 2001 From: Erwan Rouchet Date: Wed, 6 Nov 2019 18:34:28 +0100 Subject: [PATCH 47/79] Fix link to Django docs (#7040) --- docs/api-guide/responses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 1a56b01017..dbdc8ff2cc 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -94,5 +94,5 @@ As with any other `TemplateResponse`, this method is called to render the serial You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle. -[cite]: https://docs.djangoproject.com/en/stable/stable/template-response/ +[cite]: https://docs.djangoproject.com/en/stable/ref/template-response/ [statuscodes]: status-codes.md From 14d740d08800397015ce89e43aec4ee0bbf29c04 Mon Sep 17 00:00:00 2001 From: Aaron Yong Date: Wed, 6 Nov 2019 13:37:13 -0700 Subject: [PATCH 48/79] Update DEFAULT_SCHEMA_CLASSES default value in Settings docs (#7014) The default value was changed to point to the OpenAPI AutoSchema class. The docs were leading users to believe that rest_framework.schemas.AutoSchema was the default. As of this commit, the root AutoSchema is in fact imported from the coreapi module. --- docs/api-guide/settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 768e343a78..d42000260b 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -101,7 +101,7 @@ Default: `'rest_framework.negotiation.DefaultContentNegotiation'` A view inspector class that will be used for schema generation. -Default: `'rest_framework.schemas.AutoSchema'` +Default: `'rest_framework.schemas.openapi.AutoSchema'` --- From 8b06ce72d7414a62bb1a7da0c5aec24ec16e811d Mon Sep 17 00:00:00 2001 From: Dima Knivets Date: Wed, 6 Nov 2019 22:44:51 +0200 Subject: [PATCH 49/79] OpenAPI: Map renderers/parsers for request/response media-types. (#6865) --- rest_framework/schemas/openapi.py | 34 +++++++++++--- tests/schemas/test_openapi.py | 73 +++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index a3970ff7ae..a67974ed66 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -1,4 +1,5 @@ import warnings +from operator import attrgetter from urllib.parse import urljoin from django.core.validators import ( @@ -8,7 +9,7 @@ from django.db import models from django.utils.encoding import force_str -from rest_framework import exceptions, serializers +from rest_framework import exceptions, renderers, serializers from rest_framework.compat import uritemplate from rest_framework.fields import _UnvalidatedField, empty @@ -78,7 +79,9 @@ def get_schema(self, request=None, public=False): class AutoSchema(ViewInspector): - content_types = ['application/json'] + request_media_types = [] + response_media_types = [] + method_mapping = { 'get': 'Retrieve', 'post': 'Create', @@ -339,6 +342,12 @@ def _map_field(self, field): self._map_min_max(field, content) return content + if isinstance(field, serializers.FileField): + return { + 'type': 'string', + 'format': 'binary' + } + # Simplest cases, default to 'string' type: FIELD_CLASS_SCHEMA_TYPE = { serializers.BooleanField: 'boolean', @@ -434,9 +443,20 @@ def _get_paginator(self): pagination_class = getattr(self.view, 'pagination_class', None) if pagination_class: return pagination_class() - return None + def map_parsers(self, path, method): + return list(map(attrgetter('media_type'), self.view.parser_classes)) + + def map_renderers(self, path, method): + media_types = [] + for renderer in self.view.renderer_classes: + # BrowsableAPIRenderer not relevant to OpenAPI spec + if renderer == renderers.BrowsableAPIRenderer: + continue + media_types.append(renderer.media_type) + return media_types + def _get_serializer(self, method, path): view = self.view @@ -456,6 +476,8 @@ def _get_request_body(self, path, method): if method not in ('PUT', 'PATCH', 'POST'): return {} + self.request_media_types = self.map_parsers(path, method) + serializer = self._get_serializer(path, method) if not isinstance(serializer, serializers.Serializer): @@ -473,7 +495,7 @@ def _get_request_body(self, path, method): return { 'content': { ct: {'schema': content} - for ct in self.content_types + for ct in self.request_media_types } } @@ -486,6 +508,8 @@ def _get_responses(self, path, method): } } + self.response_media_types = self.map_renderers(path, method) + item_schema = {} serializer = self._get_serializer(path, method) @@ -513,7 +537,7 @@ def _get_responses(self, path, method): '200': { 'content': { ct: {'schema': response_schema} - for ct in self.content_types + for ct in self.response_media_types }, # description is a mandatory property, # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 78c00d9850..bbdae0fed4 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -5,6 +5,8 @@ from rest_framework import filters, generics, pagination, routers, serializers from rest_framework.compat import uritemplate +from rest_framework.parsers import JSONParser, MultiPartParser +from rest_framework.renderers import JSONRenderer from rest_framework.request import Request from rest_framework.schemas.openapi import AutoSchema, SchemaGenerator @@ -364,6 +366,77 @@ class View(generics.DestroyAPIView): }, } + def test_parser_mapping(self): + """Test that view's parsers are mapped to OA media types""" + path = '/{id}/' + method = 'POST' + + class View(generics.CreateAPIView): + serializer_class = views.ExampleSerializer + parser_classes = [JSONParser, MultiPartParser] + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + request_body = inspector._get_request_body(path, method) + + assert len(request_body['content'].keys()) == 2 + assert 'multipart/form-data' in request_body['content'] + assert 'application/json' in request_body['content'] + + def test_renderer_mapping(self): + """Test that view's renderers are mapped to OA media types""" + path = '/{id}/' + method = 'GET' + + class View(generics.CreateAPIView): + serializer_class = views.ExampleSerializer + renderer_classes = [JSONRenderer] + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + responses = inspector._get_responses(path, method) + # TODO this should be changed once the multiple response + # schema support is there + success_response = responses['200'] + + assert len(success_response['content'].keys()) == 1 + assert 'application/json' in success_response['content'] + + def test_serializer_filefield(self): + path = '/{id}/' + method = 'POST' + + class ItemSerializer(serializers.Serializer): + attachment = serializers.FileField() + + class View(generics.CreateAPIView): + serializer_class = ItemSerializer + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + request_body = inspector._get_request_body(path, method) + mp_media = request_body['content']['multipart/form-data'] + attachment = mp_media['schema']['properties']['attachment'] + assert attachment['format'] == 'binary' + def test_retrieve_response_body_generation(self): """ Test that a list of properties is returned for retrieve item views. From becb96216025a9e030f59f7bbab4e22daa5b0e3f Mon Sep 17 00:00:00 2001 From: Kentalot Date: Wed, 6 Nov 2019 12:46:19 -0800 Subject: [PATCH 50/79] OpenAPI: Use int64 format for large integers. (#7018) --- rest_framework/schemas/openapi.py | 11 +++++++++-- tests/schemas/test_openapi.py | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index a67974ed66..0d45e27cf9 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -268,9 +268,13 @@ def _map_field(self, field): 'items': {}, } if not isinstance(field.child, _UnvalidatedField): - mapping['items'] = { - "type": self._map_field(field.child).get('type') + map_field = self._map_field(field.child) + items = { + "type": map_field.get('type') } + if 'format' in map_field: + items['format'] = map_field.get('format') + mapping['items'] = items return mapping # DateField and DateTimeField type is string @@ -340,6 +344,9 @@ def _map_field(self, field): 'type': 'integer' } self._map_min_max(field, content) + # 2147483647 is max for int32_size, so we use int64 for format + if int(content.get('maximum', 0)) > 2147483647 or int(content.get('minimum', 0)) > 2147483647: + content['format'] = 'int64' return content if isinstance(field, serializers.FileField): diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index bbdae0fed4..fc22c32ab4 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -50,6 +50,10 @@ def test_list_field_mapping(self): (serializers.ListField(child=serializers.BooleanField()), {'items': {'type': 'boolean'}, 'type': 'array'}), (serializers.ListField(child=serializers.FloatField()), {'items': {'type': 'number'}, 'type': 'array'}), (serializers.ListField(child=serializers.CharField()), {'items': {'type': 'string'}, 'type': 'array'}), + (serializers.ListField(child=serializers.IntegerField(max_value=4294967295)), + {'items': {'type': 'integer', 'format': 'int64'}, 'type': 'array'}), + (serializers.IntegerField(min_value=2147483648), + {'type': 'integer', 'minimum': 2147483648, 'format': 'int64'}), ] for field, mapping in cases: with self.subTest(field=field): From 7c3477dcdae558b40acfa116ed08eeb5700818eb Mon Sep 17 00:00:00 2001 From: Yann Savary Date: Wed, 6 Nov 2019 21:52:02 +0100 Subject: [PATCH 51/79] OpenAPI: Ported docstring operation description from CoreAPI inspector. (#6898) --- rest_framework/schemas/coreapi.py | 48 +--------------------------- rest_framework/schemas/inspectors.py | 46 ++++++++++++++++++++++++++ rest_framework/schemas/openapi.py | 3 +- tests/schemas/test_openapi.py | 40 ++++++++++++++++------- tests/schemas/views.py | 24 ++++++++++++++ 5 files changed, 100 insertions(+), 61 deletions(-) diff --git a/rest_framework/schemas/coreapi.py b/rest_framework/schemas/coreapi.py index d811da2d83..75ed5671af 100644 --- a/rest_framework/schemas/coreapi.py +++ b/rest_framework/schemas/coreapi.py @@ -1,26 +1,18 @@ -import re import warnings from collections import Counter, OrderedDict from urllib import parse from django.db import models -from django.utils.encoding import force_str, smart_text +from django.utils.encoding import force_str from rest_framework import exceptions, serializers from rest_framework.compat import coreapi, coreschema, uritemplate from rest_framework.settings import api_settings -from rest_framework.utils import formatting from .generators import BaseSchemaGenerator from .inspectors import ViewInspector from .utils import get_pk_description, is_list_view -# Used in _get_description_section() -# TODO: ???: move up to base. -header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:') - -# Generator # - def common_path(paths): split_paths = [path.strip('/').split('/') for path in paths] @@ -397,44 +389,6 @@ def get_link(self, path, method, base_url): description=description ) - def get_description(self, path, method): - """ - Determine a link description. - - This will be based on the method docstring if one exists, - or else the class docstring. - """ - view = self.view - - method_name = getattr(view, 'action', method.lower()) - method_docstring = getattr(view, method_name, None).__doc__ - if method_docstring: - # An explicit docstring on the method or action. - return self._get_description_section(view, method.lower(), formatting.dedent(smart_text(method_docstring))) - else: - return self._get_description_section(view, getattr(view, 'action', method.lower()), view.get_view_description()) - - def _get_description_section(self, view, header, description): - lines = [line for line in description.splitlines()] - current_section = '' - sections = {'': ''} - - for line in lines: - if header_regex.match(line): - current_section, seperator, lead = line.partition(':') - sections[current_section] = lead.strip() - else: - sections[current_section] += '\n' + line - - # TODO: SCHEMA_COERCE_METHOD_NAMES appears here and in `SchemaGenerator.get_keys` - coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES - if header in sections: - return sections[header].strip() - if header in coerce_method_names: - if coerce_method_names[header] in sections: - return sections[coerce_method_names[header]].strip() - return sections[''].strip() - def get_path_fields(self, path, method): """ Return a list of `coreapi.Field` instances corresponding to any diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 86fcdc435e..3b7e7f9637 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -3,9 +3,13 @@ See schemas.__init__.py for package overview. """ +import re from weakref import WeakKeyDictionary +from django.utils.encoding import smart_text + from rest_framework.settings import api_settings +from rest_framework.utils import formatting class ViewInspector: @@ -15,6 +19,9 @@ class ViewInspector: Provide subclass for per-view schema generation """ + # Used in _get_description_section() + header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:') + def __init__(self): self.instance_schemas = WeakKeyDictionary() @@ -62,6 +69,45 @@ def view(self, value): def view(self): self._view = None + def get_description(self, path, method): + """ + Determine a path description. + + This will be based on the method docstring if one exists, + or else the class docstring. + """ + view = self.view + + method_name = getattr(view, 'action', method.lower()) + method_docstring = getattr(view, method_name, None).__doc__ + if method_docstring: + # An explicit docstring on the method or action. + return self._get_description_section(view, method.lower(), formatting.dedent(smart_text(method_docstring))) + else: + return self._get_description_section(view, getattr(view, 'action', method.lower()), + view.get_view_description()) + + def _get_description_section(self, view, header, description): + lines = [line for line in description.splitlines()] + current_section = '' + sections = {'': ''} + + for line in lines: + if self.header_regex.match(line): + current_section, separator, lead = line.partition(':') + sections[current_section] = lead.strip() + else: + sections[current_section] += '\n' + line + + # TODO: SCHEMA_COERCE_METHOD_NAMES appears here and in `SchemaGenerator.get_keys` + coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES + if header in sections: + return sections[header].strip() + if header in coerce_method_names: + if coerce_method_names[header] in sections: + return sections[coerce_method_names[header]].strip() + return sections[''].strip() + class DefaultSchema(ViewInspector): """Allows overriding AutoSchema using DEFAULT_SCHEMA_CLASS setting""" diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 0d45e27cf9..e33759e81c 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -17,8 +17,6 @@ from .inspectors import ViewInspector from .utils import get_pk_description, is_list_view -# Generator - class SchemaGenerator(BaseSchemaGenerator): @@ -94,6 +92,7 @@ def get_operation(self, path, method): operation = {} operation['operationId'] = self._get_operation_id(path, method) + operation['description'] = self.get_description(path, method) parameters = [] parameters += self._get_path_parameters(path, method) diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index fc22c32ab4..5a56bae878 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -77,7 +77,7 @@ def test_path_without_parameters(self): method = 'GET' view = create_view( - views.ExampleListView, + views.DocStringExampleListView, method, create_request(path) ) @@ -86,7 +86,8 @@ def test_path_without_parameters(self): operation = inspector.get_operation(path, method) assert operation == { - 'operationId': 'listExamples', + 'operationId': 'listDocStringExamples', + 'description': 'A description of my GET operation.', 'parameters': [], 'responses': { '200': { @@ -108,23 +109,38 @@ def test_path_with_id_parameter(self): method = 'GET' view = create_view( - views.ExampleDetailView, + views.DocStringExampleDetailView, method, create_request(path) ) inspector = AutoSchema() inspector.view = view - parameters = inspector._get_path_parameters(path, method) - assert parameters == [{ - 'description': '', - 'in': 'path', - 'name': 'id', - 'required': True, - 'schema': { - 'type': 'string', + operation = inspector.get_operation(path, method) + assert operation == { + 'operationId': 'RetrieveDocStringExampleDetail', + 'description': 'A description of my GET operation.', + 'parameters': [{ + 'description': '', + 'in': 'path', + 'name': 'id', + 'required': True, + 'schema': { + 'type': 'string', + }, + }], + 'responses': { + '200': { + 'description': '', + 'content': { + 'application/json': { + 'schema': { + }, + }, + }, + }, }, - }] + } def test_request_body(self): path = '/' diff --git a/tests/schemas/views.py b/tests/schemas/views.py index 6b83e5bde0..f8d143e715 100644 --- a/tests/schemas/views.py +++ b/tests/schemas/views.py @@ -29,6 +29,30 @@ def get(self, *args, **kwargs): pass +class DocStringExampleListView(APIView): + """ + get: A description of my GET operation. + post: A description of my POST operation. + """ + permission_classes = [permissions.IsAuthenticatedOrReadOnly] + + def get(self, *args, **kwargs): + pass + + def post(self, request, *args, **kwargs): + pass + + +class DocStringExampleDetailView(APIView): + permission_classes = [permissions.IsAuthenticatedOrReadOnly] + + def get(self, *args, **kwargs): + """ + A description of my GET operation. + """ + pass + + # Generics. class ExampleSerializer(serializers.Serializer): date = serializers.DateField() From 0d3d548aa52c92c57bb919c2965b723aee6b903f Mon Sep 17 00:00:00 2001 From: Yann Savary Date: Wed, 6 Nov 2019 21:54:12 +0100 Subject: [PATCH 52/79] OpenAPI: Fixed generation when title or version not provided. (#6912) --- docs/api-guide/schemas.md | 2 +- rest_framework/schemas/generators.py | 2 +- rest_framework/schemas/openapi.py | 5 +++-- tests/schemas/test_openapi.py | 13 +++++++++++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index ec5366d8eb..e33a2a6112 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -73,7 +73,7 @@ The `get_schema_view()` helper takes the following keyword arguments: * `title`: May be used to provide a descriptive title for the schema definition. * `description`: Longer descriptive text. -* `version`: The version of the API. Defaults to `0.1.0`. +* `version`: The version of the API. * `url`: May be used to pass a canonical base URL for the schema. schema_view = get_schema_view( diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 77e92eeb8c..4b6d82a149 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -151,7 +151,7 @@ class BaseSchemaGenerator(object): # Set by 'SCHEMA_COERCE_PATH_PK'. coerce_path_pk = None - def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version=''): + def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version=None): if url and not url.endswith('/'): url += '/' diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index e33759e81c..134df50434 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -21,9 +21,10 @@ class SchemaGenerator(BaseSchemaGenerator): def get_info(self): + # Title and version are required by openapi specification 3.x info = { - 'title': self.title, - 'version': self.version, + 'title': self.title or '', + 'version': self.version or '' } if self.description is not None: diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 5a56bae878..622f78cdda 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -704,3 +704,16 @@ def test_schema_information(self): assert schema['info']['title'] == 'My title' assert schema['info']['version'] == '1.2.3' assert schema['info']['description'] == 'My description' + + def test_schema_information_empty(self): + """Construction of the top level dictionary.""" + patterns = [ + url(r'^example/?$', views.ExampleListView.as_view()), + ] + generator = SchemaGenerator(patterns=patterns) + + request = create_request('/') + schema = generator.get_schema(request=request) + + assert schema['info']['title'] == '' + assert schema['info']['version'] == '' From 0d6589cf45940bb67ace74a06b2c5b053f1c31ef Mon Sep 17 00:00:00 2001 From: brantmorton <49286726+brantmorton@users.noreply.github.com> Date: Thu, 7 Nov 2019 06:20:56 -0600 Subject: [PATCH 53/79] Updated url() with re_path() in Versioning docs. (#7043) --- docs/api-guide/versioning.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api-guide/versioning.md b/docs/api-guide/versioning.md index ad76ced3d6..6076b1ed2f 100644 --- a/docs/api-guide/versioning.md +++ b/docs/api-guide/versioning.md @@ -132,12 +132,12 @@ This scheme requires the client to specify the version as part of the URL path. Your URL conf must include a pattern that matches the version with a `'version'` keyword argument, so that this information is available to the versioning scheme. urlpatterns = [ - url( + re_path( r'^(?P(v1|v2))/bookings/$', bookings_list, name='bookings-list' ), - url( + re_path( r'^(?P(v1|v2))/bookings/(?P[0-9]+)/$', bookings_detail, name='bookings-detail' @@ -158,14 +158,14 @@ In the following example we're giving a set of views two different possible URL # bookings/urls.py urlpatterns = [ - url(r'^$', bookings_list, name='bookings-list'), - url(r'^(?P[0-9]+)/$', bookings_detail, name='bookings-detail') + re_path(r'^$', bookings_list, name='bookings-list'), + re_path(r'^(?P[0-9]+)/$', bookings_detail, name='bookings-detail') ] # urls.py urlpatterns = [ - url(r'^v1/bookings/', include('bookings.urls', namespace='v1')), - url(r'^v2/bookings/', include('bookings.urls', namespace='v2')) + re_path(r'^v1/bookings/', include('bookings.urls', namespace='v1')), + re_path(r'^v2/bookings/', include('bookings.urls', namespace='v2')) ] Both `URLPathVersioning` and `NamespaceVersioning` are reasonable if you just need a simple versioning scheme. The `URLPathVersioning` approach might be better suitable for small ad-hoc projects, and the `NamespaceVersioning` is probably easier to manage for larger projects. From 8988afa0827a139efeeb72afb21da08670fb4775 Mon Sep 17 00:00:00 2001 From: Maxime Jacques Date: Mon, 11 Nov 2019 16:41:10 -0500 Subject: [PATCH 54/79] Update bootstrap to 3.4.1 (#6923) --- rest_framework/static/rest_framework/js/bootstrap.min.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/static/rest_framework/js/bootstrap.min.js b/rest_framework/static/rest_framework/js/bootstrap.min.js index 4cd8219908..eb0a8b410f 100644 --- a/rest_framework/static/rest_framework/js/bootstrap.min.js +++ b/rest_framework/static/rest_framework/js/bootstrap.min.js @@ -1,6 +1,6 @@ /*! - * Bootstrap v3.4.0 (https://getbootstrap.com/) - * Copyright 2011-2018 Twitter, Inc. + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. * Licensed under the MIT license */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");!function(t){"use strict";var e=jQuery.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||3this.$items.length-1||t<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):i==t?this.pause().cycle():this.slide(idocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth

    ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},m.prototype.init=function(t,e,i){if(this.enabled=!0,this.type=t,this.$element=g(e),this.options=this.getOptions(i),this.$viewport=this.options.viewport&&g(document).find(g.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var o=this.options.trigger.split(" "),n=o.length;n--;){var s=o[n];if("click"==s)this.$element.on("click."+this.type,this.options.selector,g.proxy(this.toggle,this));else if("manual"!=s){var a="hover"==s?"mouseenter":"focusin",r="hover"==s?"mouseleave":"focusout";this.$element.on(a+"."+this.type,this.options.selector,g.proxy(this.enter,this)),this.$element.on(r+"."+this.type,this.options.selector,g.proxy(this.leave,this))}}this.options.selector?this._options=g.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},m.prototype.getDefaults=function(){return m.DEFAULTS},m.prototype.getOptions=function(t){return(t=g.extend({},this.getDefaults(),this.$element.data(),t)).delay&&"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t},m.prototype.getDelegateOptions=function(){var i={},o=this.getDefaults();return this._options&&g.each(this._options,function(t,e){o[t]!=e&&(i[t]=e)}),i},m.prototype.enter=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusin"==t.type?"focus":"hover"]=!0),e.tip().hasClass("in")||"in"==e.hoverState)e.hoverState="in";else{if(clearTimeout(e.timeout),e.hoverState="in",!e.options.delay||!e.options.delay.show)return e.show();e.timeout=setTimeout(function(){"in"==e.hoverState&&e.show()},e.options.delay.show)}},m.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},m.prototype.leave=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusout"==t.type?"focus":"hover"]=!1),!e.isInStateTrue()){if(clearTimeout(e.timeout),e.hoverState="out",!e.options.delay||!e.options.delay.hide)return e.hide();e.timeout=setTimeout(function(){"out"==e.hoverState&&e.hide()},e.options.delay.hide)}},m.prototype.show=function(){var t=g.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(t);var e=g.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(t.isDefaultPrevented()||!e)return;var i=this,o=this.tip(),n=this.getUID(this.type);this.setContent(),o.attr("id",n),this.$element.attr("aria-describedby",n),this.options.animation&&o.addClass("fade");var s="function"==typeof this.options.placement?this.options.placement.call(this,o[0],this.$element[0]):this.options.placement,a=/\s?auto?\s?/i,r=a.test(s);r&&(s=s.replace(a,"")||"top"),o.detach().css({top:0,left:0,display:"block"}).addClass(s).data("bs."+this.type,this),this.options.container?o.appendTo(g(document).find(this.options.container)):o.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var l=this.getPosition(),h=o[0].offsetWidth,d=o[0].offsetHeight;if(r){var p=s,c=this.getPosition(this.$viewport);s="bottom"==s&&l.bottom+d>c.bottom?"top":"top"==s&&l.top-dc.width?"left":"left"==s&&l.left-ha.top+a.height&&(n.top=a.top+a.height-l)}else{var h=e.left-s,d=e.left+s+i;ha.right&&(n.left=a.left+a.width-d)}return n},m.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},m.prototype.getUID=function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},m.prototype.tip=function(){if(!this.$tip&&(this.$tip=g(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},m.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},m.prototype.enable=function(){this.enabled=!0},m.prototype.disable=function(){this.enabled=!1},m.prototype.toggleEnabled=function(){this.enabled=!this.enabled},m.prototype.toggle=function(t){var e=this;t&&((e=g(t.currentTarget).data("bs."+this.type))||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e))),t?(e.inState.click=!e.inState.click,e.isInStateTrue()?e.enter(e):e.leave(e)):e.tip().hasClass("in")?e.leave(e):e.enter(e)},m.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null,t.$element=null})};var t=g.fn.tooltip;g.fn.tooltip=function e(o){return this.each(function(){var t=g(this),e=t.data("bs.tooltip"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.tooltip",e=new m(this,i)),"string"==typeof o&&e[o]())})},g.fn.tooltip.Constructor=m,g.fn.tooltip.noConflict=function(){return g.fn.tooltip=t,this}}(jQuery),function(n){"use strict";var s=function(t,e){this.init("popover",t,e)};if(!n.fn.tooltip)throw new Error("Popover requires tooltip.js");s.VERSION="3.4.0",s.DEFAULTS=n.extend({},n.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),((s.prototype=n.extend({},n.fn.tooltip.Constructor.prototype)).constructor=s).prototype.getDefaults=function(){return s.DEFAULTS},s.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](e),t.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof i?"html":"append":"text"](i),t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},s.prototype.hasContent=function(){return this.getTitle()||this.getContent()},s.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},s.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var t=n.fn.popover;n.fn.popover=function e(o){return this.each(function(){var t=n(this),e=t.data("bs.popover"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.popover",e=new s(this,i)),"string"==typeof o&&e[o]())})},n.fn.popover.Constructor=s,n.fn.popover.noConflict=function(){return n.fn.popover=t,this}}(jQuery),function(s){"use strict";function n(t,e){this.$body=s(document.body),this.$scrollElement=s(t).is(document.body)?s(window):s(t),this.options=s.extend({},n.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",s.proxy(this.process,this)),this.refresh(),this.process()}function e(o){return this.each(function(){var t=s(this),e=t.data("bs.scrollspy"),i="object"==typeof o&&o;e||t.data("bs.scrollspy",e=new n(this,i)),"string"==typeof o&&e[o]()})}n.VERSION="3.4.0",n.DEFAULTS={offset:10},n.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},n.prototype.refresh=function(){var t=this,o="offset",n=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),s.isWindow(this.$scrollElement[0])||(o="position",n=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=s(this),e=t.data("target")||t.attr("href"),i=/^#./.test(e)&&s(e);return i&&i.length&&i.is(":visible")&&[[i[o]().top+n,e]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},n.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),o=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),o<=e)return a!=(t=s[s.length-1])&&this.activate(t);if(a&&e=n[t]&&(n[t+1]===undefined||e .active"),n=i&&r.support.transition&&(o.length&&o.hasClass("fade")||!!e.find("> .fade").length);function s(){o.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),t.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),n?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu").length&&t.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),i&&i()}o.length&&n?o.one("bsTransitionEnd",s).emulateTransitionEnd(a.TRANSITION_DURATION):s(),o.removeClass("in")};var t=r.fn.tab;r.fn.tab=e,r.fn.tab.Constructor=a,r.fn.tab.noConflict=function(){return r.fn.tab=t,this};var i=function(t){t.preventDefault(),e.call(r(this),"show")};r(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',i).on("click.bs.tab.data-api",'[data-toggle="pill"]',i)}(jQuery),function(l){"use strict";var h=function(t,e){this.options=l.extend({},h.DEFAULTS,e);var i=this.options.target===h.DEFAULTS.target?l(this.options.target):l(document).find(this.options.target);this.$target=i.on("scroll.bs.affix.data-api",l.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",l.proxy(this.checkPositionWithEventLoop,this)),this.$element=l(t),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};function i(o){return this.each(function(){var t=l(this),e=t.data("bs.affix"),i="object"==typeof o&&o;e||t.data("bs.affix",e=new h(this,i)),"string"==typeof o&&e[o]()})}h.VERSION="3.4.0",h.RESET="affix affix-top affix-bottom",h.DEFAULTS={offset:0,target:window},h.prototype.getState=function(t,e,i,o){var n=this.$target.scrollTop(),s=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return nthis.$items.length-1||t<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):i==t?this.pause().cycle():this.slide(idocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth
    ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0},sanitize:!0,sanitizeFn:null,whiteList:t},m.prototype.init=function(t,e,i){if(this.enabled=!0,this.type=t,this.$element=g(e),this.options=this.getOptions(i),this.$viewport=this.options.viewport&&g(document).find(g.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var o=this.options.trigger.split(" "),n=o.length;n--;){var s=o[n];if("click"==s)this.$element.on("click."+this.type,this.options.selector,g.proxy(this.toggle,this));else if("manual"!=s){var a="hover"==s?"mouseenter":"focusin",r="hover"==s?"mouseleave":"focusout";this.$element.on(a+"."+this.type,this.options.selector,g.proxy(this.enter,this)),this.$element.on(r+"."+this.type,this.options.selector,g.proxy(this.leave,this))}}this.options.selector?this._options=g.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},m.prototype.getDefaults=function(){return m.DEFAULTS},m.prototype.getOptions=function(t){var e=this.$element.data();for(var i in e)e.hasOwnProperty(i)&&-1!==g.inArray(i,o)&&delete e[i];return(t=g.extend({},this.getDefaults(),e,t)).delay&&"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t.sanitize&&(t.template=n(t.template,t.whiteList,t.sanitizeFn)),t},m.prototype.getDelegateOptions=function(){var i={},o=this.getDefaults();return this._options&&g.each(this._options,function(t,e){o[t]!=e&&(i[t]=e)}),i},m.prototype.enter=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusin"==t.type?"focus":"hover"]=!0),e.tip().hasClass("in")||"in"==e.hoverState)e.hoverState="in";else{if(clearTimeout(e.timeout),e.hoverState="in",!e.options.delay||!e.options.delay.show)return e.show();e.timeout=setTimeout(function(){"in"==e.hoverState&&e.show()},e.options.delay.show)}},m.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},m.prototype.leave=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusout"==t.type?"focus":"hover"]=!1),!e.isInStateTrue()){if(clearTimeout(e.timeout),e.hoverState="out",!e.options.delay||!e.options.delay.hide)return e.hide();e.timeout=setTimeout(function(){"out"==e.hoverState&&e.hide()},e.options.delay.hide)}},m.prototype.show=function(){var t=g.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(t);var e=g.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(t.isDefaultPrevented()||!e)return;var i=this,o=this.tip(),n=this.getUID(this.type);this.setContent(),o.attr("id",n),this.$element.attr("aria-describedby",n),this.options.animation&&o.addClass("fade");var s="function"==typeof this.options.placement?this.options.placement.call(this,o[0],this.$element[0]):this.options.placement,a=/\s?auto?\s?/i,r=a.test(s);r&&(s=s.replace(a,"")||"top"),o.detach().css({top:0,left:0,display:"block"}).addClass(s).data("bs."+this.type,this),this.options.container?o.appendTo(g(document).find(this.options.container)):o.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var l=this.getPosition(),h=o[0].offsetWidth,d=o[0].offsetHeight;if(r){var p=s,c=this.getPosition(this.$viewport);s="bottom"==s&&l.bottom+d>c.bottom?"top":"top"==s&&l.top-dc.width?"left":"left"==s&&l.left-ha.top+a.height&&(n.top=a.top+a.height-l)}else{var h=e.left-s,d=e.left+s+i;ha.right&&(n.left=a.left+a.width-d)}return n},m.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},m.prototype.getUID=function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},m.prototype.tip=function(){if(!this.$tip&&(this.$tip=g(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},m.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},m.prototype.enable=function(){this.enabled=!0},m.prototype.disable=function(){this.enabled=!1},m.prototype.toggleEnabled=function(){this.enabled=!this.enabled},m.prototype.toggle=function(t){var e=this;t&&((e=g(t.currentTarget).data("bs."+this.type))||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e))),t?(e.inState.click=!e.inState.click,e.isInStateTrue()?e.enter(e):e.leave(e)):e.tip().hasClass("in")?e.leave(e):e.enter(e)},m.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null,t.$element=null})},m.prototype.sanitizeHtml=function(t){return n(t,this.options.whiteList,this.options.sanitizeFn)};var e=g.fn.tooltip;g.fn.tooltip=function i(o){return this.each(function(){var t=g(this),e=t.data("bs.tooltip"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.tooltip",e=new m(this,i)),"string"==typeof o&&e[o]())})},g.fn.tooltip.Constructor=m,g.fn.tooltip.noConflict=function(){return g.fn.tooltip=e,this}}(jQuery),function(n){"use strict";var s=function(t,e){this.init("popover",t,e)};if(!n.fn.tooltip)throw new Error("Popover requires tooltip.js");s.VERSION="3.4.1",s.DEFAULTS=n.extend({},n.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),((s.prototype=n.extend({},n.fn.tooltip.Constructor.prototype)).constructor=s).prototype.getDefaults=function(){return s.DEFAULTS},s.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();if(this.options.html){var o=typeof i;this.options.sanitize&&(e=this.sanitizeHtml(e),"string"===o&&(i=this.sanitizeHtml(i))),t.find(".popover-title").html(e),t.find(".popover-content").children().detach().end()["string"===o?"html":"append"](i)}else t.find(".popover-title").text(e),t.find(".popover-content").children().detach().end().text(i);t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},s.prototype.hasContent=function(){return this.getTitle()||this.getContent()},s.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},s.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var t=n.fn.popover;n.fn.popover=function e(o){return this.each(function(){var t=n(this),e=t.data("bs.popover"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.popover",e=new s(this,i)),"string"==typeof o&&e[o]())})},n.fn.popover.Constructor=s,n.fn.popover.noConflict=function(){return n.fn.popover=t,this}}(jQuery),function(s){"use strict";function n(t,e){this.$body=s(document.body),this.$scrollElement=s(t).is(document.body)?s(window):s(t),this.options=s.extend({},n.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",s.proxy(this.process,this)),this.refresh(),this.process()}function e(o){return this.each(function(){var t=s(this),e=t.data("bs.scrollspy"),i="object"==typeof o&&o;e||t.data("bs.scrollspy",e=new n(this,i)),"string"==typeof o&&e[o]()})}n.VERSION="3.4.1",n.DEFAULTS={offset:10},n.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},n.prototype.refresh=function(){var t=this,o="offset",n=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),s.isWindow(this.$scrollElement[0])||(o="position",n=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=s(this),e=t.data("target")||t.attr("href"),i=/^#./.test(e)&&s(e);return i&&i.length&&i.is(":visible")&&[[i[o]().top+n,e]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},n.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),o=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),o<=e)return a!=(t=s[s.length-1])&&this.activate(t);if(a&&e=n[t]&&(n[t+1]===undefined||e .active"),n=i&&r.support.transition&&(o.length&&o.hasClass("fade")||!!e.find("> .fade").length);function s(){o.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),t.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),n?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu").length&&t.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),i&&i()}o.length&&n?o.one("bsTransitionEnd",s).emulateTransitionEnd(a.TRANSITION_DURATION):s(),o.removeClass("in")};var t=r.fn.tab;r.fn.tab=e,r.fn.tab.Constructor=a,r.fn.tab.noConflict=function(){return r.fn.tab=t,this};var i=function(t){t.preventDefault(),e.call(r(this),"show")};r(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',i).on("click.bs.tab.data-api",'[data-toggle="pill"]',i)}(jQuery),function(l){"use strict";var h=function(t,e){this.options=l.extend({},h.DEFAULTS,e);var i=this.options.target===h.DEFAULTS.target?l(this.options.target):l(document).find(this.options.target);this.$target=i.on("scroll.bs.affix.data-api",l.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",l.proxy(this.checkPositionWithEventLoop,this)),this.$element=l(t),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};function i(o){return this.each(function(){var t=l(this),e=t.data("bs.affix"),i="object"==typeof o&&o;e||t.data("bs.affix",e=new h(this,i)),"string"==typeof o&&e[o]()})}h.VERSION="3.4.1",h.RESET="affix affix-top affix-bottom",h.DEFAULTS={offset:0,target:window},h.prototype.getState=function(t,e,i,o){var n=this.$target.scrollTop(),s=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return n Date: Sat, 16 Nov 2019 03:39:47 +0100 Subject: [PATCH 55/79] Cleanup "Documenting your API" 3rd party recommendations (#7057) --- docs/community/third-party-packages.md | 2 - docs/img/apiary.png | Bin 55554 -> 0 bytes docs/img/django-rest-swagger.png | Bin 76945 -> 0 bytes docs/topics/documenting-your-api.md | 75 ++++--------------------- 4 files changed, 10 insertions(+), 67 deletions(-) delete mode 100644 docs/img/apiary.png delete mode 100644 docs/img/django-rest-swagger.png diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index ea5d6b854f..c4b567a5f8 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -254,7 +254,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [cookiecutter-django-rest][cookiecutter-django-rest] - A cookiecutter template that takes care of the setup and configuration so you can focus on making your REST apis awesome. * [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serializer that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer. -* [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI. * [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server. * [gaiarestframework][gaiarestframework] - Utils for django-rest-framework * [drf-extensions][drf-extensions] - A collection of custom extensions @@ -315,7 +314,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [djangorestframework-rapidjson]: https://github.com/allisson/django-rest-framework-rapidjson [djangorestframework-chain]: https://github.com/philipn/django-rest-framework-chain [djangorestrelationalhyperlink]: https://github.com/fredkingham/django_rest_model_hyperlink_serializers_project -[django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger [django-rest-framework-proxy]: https://github.com/eofs/django-rest-framework-proxy [gaiarestframework]: https://github.com/AppsFuel/gaiarestframework [drf-extensions]: https://github.com/chibisov/drf-extensions diff --git a/docs/img/apiary.png b/docs/img/apiary.png deleted file mode 100644 index 923d384ebb66d833b13b9971cda69eb718651c1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55554 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 diff --git a/docs/img/django-rest-swagger.png b/docs/img/django-rest-swagger.png deleted file mode 100644 index 96a6b23800d2f7dec53631e0b017e2f383bda659..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76945 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%0D(6%`>FHckpDt$oGc4=IC`V= zZLSPaf&xF6mD2DiZ8no5wF&3>O~#_aJL6<~vgMG^~PsX{}&tvTw{^T^Y*K?-4(Nm5gIMqdAU8p>YZC+ErJ*T8i{ZKkr= zY|aaH@lR62L{lv#Mvxs-7z0iR1=^Ugm1wii&*894esW4>su=n#=3@83M@Mh{I}QP$ zIC$zSdw=Ta{Y8yZxoJ_Kk~;7|5?{WNY4+8!fQ9i-Jad zb>Ay*&jR|bbh{_ySEut9DMZg8wIf4{9KTfZ%VsO;p+!v7tHNS~xSN-l&*nK(+S^A_ z!h~jFB`j2M&*qa(``K?2a%J%(c0UdVR?hpcAN3mYc{-Lm!3AJlEO%TCbo9KUOa1z}a=ZJeSy|cjG}M&; zPA?bWW}+4ZdW@N#+(0GaSfBR2#9lb%Ys^qxsGH|PEbCjUN7-INE3-Z3MYi{GU_DZuXg@Zz;O4yf&n1x0`E!XbG+Kym%iF$ z{rcL?w#lIGRJn36VdA##azG%@o5cD3H$*&+(#mV!#)xZ6E@Q$m8S;BA%R4|)foeX~ zkb4Ln;VTqKx3!*&|06%<-R{|m=t09N-9=t6a8^n+VM0vLd1<$zUL`frH8^p2sdspF z`8&b*Ps0eLRnA})p@c$n7ua-RzhO(exMf&aSR5Gr6$h^h^P<6R7`5^|7DOz%Lq?Mn zc=HVYd4hqpgb+H}6bsZVpRd2ypE`%~*N7neTU)s-_g0`7*PG_Msx()E9BzuDc1$*V z8VtAT_UWE|S0YujiVzY^H2)J6!q%tAWPr+NiZA)O@ke!(&Dc?cX#t5blcs(3nrXw- zt4Eau7j*bU03}-b4v;FOA&9MhDY`gAhS9rPG+m=4vK?QB_177;ray8eSN+}`YNx1e zOh>T<12rt{XLXwNXaG}}2})5fGQ}UXe-#S)=U99H`0j{f}sPidiU!HzkyJz1LV}YfVKY+LInpY=>*{C8|snAK&5VmcanWygM5E1@3<; z+?tc&>(wq+wHyoAJ&=DStFOwWQ(XEM79M#DR67I)g~r^glzBh3sA9U@Ilp`vGy zo?vxN_anvbMBYXRmkKIPJB@k8ElQamzV#;IAzus0rySD}F2eRs;VhJy`ivpefs_ojkvSSK;%B+!#<9Lic;oJGLlpev&ix^7!vOZ}BWwM1#Bi{7G{yhRD0vOSm?5mC*gL z@qG3X8;|KU8g|`p?rk)Dj(Cb4x;2w!hJC+FpZ0J5xu#VqjhrjHPLl$AlKT+TgSKL? z|GtrUZ_7IRAjAAB^UQk-R~!6J(;-k~5)3<7Ye8xK{9{sg=^{nFXh!zHGD@d8Bp7x) zJyH#@x^(3>^;%P3f0j?+K>FnDFpXce&uj1}{ zL%el;gNh)+^u9I7wC4u>@(u-MH}4^ON*HARMPb4i^AQ<&60c}z6VmMICry{pKQ|*1 z8K-%DYOJY6MR9*!bjn-?Q1F*n1ynSzr5@`4s0jZ3Ao7d-Sp;85k&0YcJXSw>-P_-V z-=dO7TYKqjZoY-rx`CW+Nl00_*hwN=ciPh86!m_;)oI0wvH<*iJ1L#tSjh2^p&k-? z9OM$Rs`Z=w^==b3hhN;@TYfqDWrf>_IdOm}g{FLve`ge#HaMyNu=~;gF1VtlUrDU* znye_BkY~T%PC1?jHB2cLat$m=-VjJ9OVYSh_{Y*j+9okr5}h#_pzLAX{~uCA|AZKC zbL{}5D8O%aX?nc)kX023{rxRKg)o6?Hym9AO%w~)wu;N9iN&cW(G8#|QCzLQ6ri2* znBf^@oq*o>0Dr<2669FZTX1Wuo5^!_%%CSJ+}YLMD9`uwJivU_)h5bI&!>{_4E!Rn zD*3GpsQfo9pB$3c^u$dIkl3+_bmW5*Mr(DB)zg4FogWSwY(hw z24W?{%8rT6h>+kGeUyjv1ZE@99sC{^Z>@RZ@UL&_0;DCJ2hHvqwp*X=a14q?yhF!Z zfA*lfk#6F7z_D|Lgve@uvRhm@^-c6Gm^%+%@0BSh`FyX-Q5r6J!6*ZdypDG}vjfi(^ z!GxqyVF{@qE?^pgtgOzgi56!nQUTlxgX90l-dhF5wMAW{AtWI{0tt--2oT)eJ%Qlv z4#8>Mq0ta5c#y{38+V6JaCg_>?$Aghm*Xer-uqYGms{V%SJe-@s@Gm?uDQmNJ?5Tc zFVje#U?KJC&UHR!{ynN*L(A6=7uw$;b`eLML3zweBXhbg_5g$(}5!uT!G z%I6WrX947Q+VRhRt^O$F(OHmQ-M2Tt*7EN2lXSVq&vA19)94M!BhM8a2D1Ou^86jb zR`jD*f8ei;BK&_g9V4)9S?$?>tADWm_4iAO&)-|={xxYIQk*_7J(zhT8t~%ZNp26< z2oGjryenfu{ng7qIUkbZppnFgzt-~p^Mi+;B2x1=eF>};3 z?!@W`7vit$f1TtLw=B6(5YJA>Sft{o2FZy%5$2|HCQe>nPU!fag^8iU9qcrw&dcS4 z55nUPHA``%U)EGl(R7^aY&yaFN3?p(^t37qrdZn+!X7}`jnOzg-JL*zku4>Fzj^!4 zSXFOJ$*Vn|zt&f6`p1;A$#t3{x3+xuQU0>We16aQArIRYy@|s0EsIkV z!P*P^Lt3Ys%i~2;QQ4*EXWrdvckix5Gog7q_bvs9AMQKl=5>{Vwl#RBUc7XWQ0;ui zQJRd_5%0dypTJI^uqJ8XMj_RTZ$BD$VD;L7G}?3^!68CPJ$d$9s>1Z{)%iJ6cuo7_ zY5z{GsvxV3vh+OUOR@o;o5BOFtp?>7%26;dy)|LyJz?RIrk-hZ+_o;)6k2Gy%fxCP z#R-9s6XTfc>ZUT`2d;MZgMjB2jQp$->`ct0a}JI@g?3N= z-08ooPz7ET|W=dwv z_b#nCrT(P_kXckORilcOk0ubmug%{(E?kUv(G*`@BV`-bNkHp>J%II^u~(zMZF)l$7$e9+-u-d_b5t6(@Z-go{xH904KN5cD}0i1vxGr3Qga{LRrjX zt(r~Dm>km(Ty3nAE7s$Ll}x_e&x+t2yVDEBme>jm(zINqoNQBbuz$!DvX_~;r-=y# zcxPW;+8shA7=1Iym1^j;A0NQEwh;u5@T{ogV)P%)-c=DDXq(fjJM{;VgRYfxX4V|V zLMBX&jVs7zw0&m!D?3;JOy>9T`j|dawu8Rg>vWo`6^}V<#DQmFp!~xJk_x(y4IU1{ zP5g9V>+&=|>dcLSx~P$CNm%=$+MI2VZ$AEKvnKZH+>l^n2cL82mrV)_p<*yR1ZB|! zc9p26n;=E@*wHax?gET1PvF6nbat7B6N>$=d%a-m^T<&Kg%HL(gh7Iw-oscQV@e%3 zU1z>fv0eb?R#MihsTCLh|+Iq)25oqxMwQidWuneJxVr`l%e*p_hJ#KCR2;jF^jkPz1E)#u_09kbQMptyO@r z*&NHNG|refvf4?`2Q{1MjoOa+_AK1q9D75Wp+~D49>dN9g0X502Dsb2lhR{nwAdSL zazZMKzHg$iU=w>cY0auU&SrPA^J@Eq5_F z>)gf5HhKQWm5-uX036t9#%4t_(6rehLfaqF)Z#<)5beZBjaC6n&35FZm;VcrlHtT1-qjZJ@!o*z z`sQ3gX5Mb&8>6kv$c?CVT4&9(q2`*6YVzm-a(GhmwmSP{5+xmI|9nb!Sj&ZBC`(hj zxXoZbzBVVgSNHUqcF0&7zfdB%j?6}%1c&<;?A;8vcDEDy-YZ2@mz|)*H&>jt6g z-6(Vx4P-gsv;{I^H^C=Sqb(*GwP@>x9LK)9wKY^f8~7p!Jt0x@)GniO+dF&uaqC%^ZArSb>!-+u-_*&pd*69U-evsh%%B8nry->f(rhI7cRp)MJX$ z*ng#la=waA4z%JR(JbD5Iv9z|2m)K3v`ZB+eo!<2LH$9Q8k9rH31#c!*So(tz(U^| zMG0*gM*#jQ`SG*h_4A}Uvt^^xh7LKp9$E|)|3b5R>mN2GdXq~L48NFu#DjAITPO>EY<-6{dIv)B63Wikz>-i7bRqJ zlFpOuL-BhDxmxIS?p0QIKUV13OBDG92jDfT-IS($nNso{p)Kw|ET`BMEA>M@2zdUm z)De4fr85J$1{ z>P>16gh}vpQdv6A?v=~HYe(2(t?|vmQk%^V+D8(fjJ%d(KeleRW_vv?@pUZH%ys|u!19-Bn^JnNB9IX= zz7GJ+<||i8VSqbOL=8R4l-@3n59rEYt9+;hEhkatEY!i$5*FF|rkuBq68Ga^6@;||bFf>do8knw!Z;iQknzPbeS^2s0PUmvgcpBJ@C z#|P$XE6r95OgXU!)T5#^0B<465+w;#_B`d9#mODo3Ih;|MiZ1LVi94>&V(fu{n8}M z3ZL@J%|hV=%&ZjBu{g!EdWJ#Nj5fikt#_81p>vY`iS+>f>l2AnUG?{9+^zbM`38@U zWmzh^i99n~(L)*rHe%7i1Zr@HN8PNZ-tk`6+`Puh09iQSJaqoGr#A+l+rgKfp;A{^ z(CwJXP%6I;PTRG)4$+=*W%aIEumZRLGch#HkqlKs<7o1t#%B+^hK`bYn%(|2|JssB z&ae#LoE0#3hv6XI)>Ju>NzL#pdE=@VN6nnGbNE@jcP7TOnY#D{RMSK4CNJ+B(o$wd z#;Fn9*PP*RpEYwfIndIX9HK|{HC+brPNW8Qfz!$ScQ1jcdfH5pw^FAkKc9~EeJz0^ z@$H?TjEi};IvZo=CXLllT8GhMLdLUqFllAWeZrfsL^?yI!_)x|2|tQPfNVhOaz-NK zN-h{SCO#_*RV*ha7MD&o#yPbtzpxTVyN9+$`H$pCNi}1Ur%1{AUfxt-2IdckbmE6~ zB{wYDP$kd>6;cZ?c3zY9Qy@zvzFMg2 zQfIBnW^uDu3>8fst~-_=sJm6kkTA?j;G1bEr3U_ZGNtEa!pBAou{uBiyw@Qh1ly)m zy#BJK!N|wSKZ}J?bh0(zYz!%FRZ*%?-5G^h(ow0y&T6(9n<_qOPsO%ns%R_8r~oJj z5pe*Tg|xfe%bt|cT9bppnRCx-8X3WJvA4!~=8GY;rsl15fw4n+6g**08pJlqbI-JS zmh4I#4B^txoa4Po#_P*P->7Elr;9TD(22tNxWG0qMH_E5RXKX6Oa;DeO|j3~@(_}= zU*1YQ=cWzS$2<1)QM=>!KzFR!)%*IOhvEU$B_4aDZK-ME4iY+ie5SEIxF81;$eGB^ z&CAOhiBd018M$!FQL$dtIWG%-W6?QsFC znlfWDy(mZq0qz*9!zn5X%1LFV2yT~MBBo5~$4EV_kAKmZTLeB#1Ab2iwa2C>^{s3u z)kP2~Hc;Y_B6Buu*>U*8&()L-d#cUu?}ljG99IqMm!|QLqZuszAdC=K`kfQw9A*YFus*RF>m# ze#?=*QnN{)R1HE_Vz8Sk8Bx*QwZjz0HPGSvnMXuK30xlJ0TuGJ`DD0$M`LX{>-ciF zmvbT>*xlyjS}Zr^G7KJ=Gu`ZTlbS%%k3Bi3y(l!fb&EB>?>#3(MRO4ymucgj$|^*H z=t#~(oVjLon8*{-6Dn+%{W`5c_%l4SI!A{v_w`!Ee^|RFXd!q4DQ`xp{Y-(+i!GCYAi_G zV-d9`i|6dqbtC!us%wM#;!D#W(#0AoADZ9CpR}58ncIeM7d|CUQA|=W!kA0B{gd*14<-HtnLl<` z2+Ei@V~fRiF4A#P7f?-RTB#18S=j5D-?W(8XH`uC7LQc(CPlS#lm9>luuTe+2iBDX zR;W$;&2-}JHU(HgF8+35fx$15tvl5(=4?xIo1YeRk2M4DXTt&{9jeQ-KV|)_`hctg zFp>Lqs8?@Z_(NR-$X8{$J1Oi1wLVkT)rzl%RDAjNTr5ZGUC;pH<#B z>3^pcx~sOgc7|#ym)tBrw69I<=;trQL%Nf>tfLJ1ZRqWP8~P?~KbZ#!D9eG^CM$3( z!==jw|Id!}v=0JTF5opR)I<_y;c5GFrPnl`Ez4g8C?DPxcV%=4T)_;ARvgnF+XBz_ zudrC@I?H-RrfXrA_gq)?CnrnC*~FR2MQbRxnwqR*@AEIUicrW(4fXWNu;vuHjG2($ z5lQOE9-9b|HhxzzGS@8aWK~}eYxt5pf0Kz(Zk0^pEFCOBDm9<32JJyU!*5LqG2QJLKW^sDh|-tw~M=;*;)nWHwqq`o!__xcvg5cx(N^)k4)0 zHyBWJkV60jbI=&8Q{IF1hXiTLD-c~u7 zoiS#%Z6s%BJm2iRpLEWq_~8j*Pw%HxEs07ZT=+`El0bQ+DhBpsmlZ9AI_pJ;pcqck zT!r%tb}Xga-ddMlPhF#x&cY?G+v)XYT8VikhrmfP7j@Uvv(cMx{$rtqcNLe&3@lfX zBB%YJw8|*oxv9KjmbK3D34$}}HM@hHa>8;oXU*dco4I$;Z_#DWTC0AWg!+79f)$@y zQ=w~CW1BhUBu(#57LQ7H#wL8n8Tb#BdEYKFC@WzP&edEFG+6;+-93-YwyFvb5o|VY zUPKRjop_miu>U8fvNiQYHkpgrp#AHevdFBH(eWK_=_W|_*`eI|u87Vxp1t6Qak+w# z5d2Z&*p?P09qe(n*AFl{SJD5!$D0;@li_=YmiZqcyAqm*NKRoezW?7u-5+z?QTWlR zT!W7<)nC^5SVVni-?a6nSA6!s-j(P$xS(9GX@aWc<^C%1`$4Pj4*5HD_Zr$|M*Iu! zrA_+~xhVnj`n&xSboqWR_vm`p1SxzEA$cM!x%r>I2C_a*8f8^%P2A z)V2m4G`Fd2#9F`bj8HhH>8MBxlflJ3=buCnd3f>Q-?5@I@t@OIoe%N%wWW!)4rKNS zKfpv8+?fRX*U()0J>Whgj7x7DC#Ygj&Mue}!THFw*lO zWSEVv--`K5yPv;fz5XmvPvX)2;umW7-EiQ;7+t@jkp2%Av|m49gtj%X|36*-KUKd> zYv0-4A#kSeS;~Ea6i3k&b~mK6Pp4f!qdV2r6{=gfZ>akqtq#pet?u}jwBuRu=kw7- zH38YVvH8M&0VDGgtxC6ASbaowADRbVnUF_**G1YIQmNb}qQz8Nc>LAbTDR`fiyVXL zc)P6VEG$Co>+eN@?A~&_F8xIOR&7@Ym7qfB3+|>)FDU)FCi!1P;6v6}+>^vz4x4M5 z{RpB;*&Bz(gPS?dU0JP08?!c_($U}@tp>UA5cN@-(u2gxygtuicLnQ#*zBEMZzL{{KBuO2P7^wz0JyotF4BVoq=SI`fQJW z@K00*nmkJv{nGP3BJIfn+jb}Voog*!e1C z_$C|&-?*biF@le9Psj_7)PD^1wn5?mr=jzZ(J(k+6E_WQcHrX?StOc@&Fh3 ztZ&*a{i!p12m$|o!nqh|XnHzOz(vPFo3}Ei;5rLxw8$IW(#HF3ufntV{EnP zwpe4@dY40t6lHc`IimaY+<>RG9%n^IZ<{da+J=$7&aMsV||SdNtlN z%V=#h#bU6xu6tNN+IBA72^mW)Y-eC6!0IVuGbV;sJbYZa~<^98Qgrp{Q^)uz<9=;vhb>eSB;4GgRiJ=s62c}?{c*(g1|PM zx>^~$a5ayU#Hhdp%H8+!7~00Ut8wQ8!@HV==6H`^N8Mg5H#k`cP6*VxZwINC=&M4fEh;L}&yQLM}MY9u4MD%*yh*HRLiQLvAT?L`4-fnlQ zA5349LpAs*PjjQSh!lADy)Raj>MPT_Xj4uVIc|V zEi+8#+k$tuFtdZ3DLwmD1NGeNq1gVWLnrHvv=a`QP$0KvOye5Vno+O$aG^?_k)rwG z<-$GE`Uq%~`C+iW@?00;Rom^5G*b(!(s|aN%aYQ(w_fT=eahh=Zm#@C#g|VNl#Wd{ zd^h6L?to6Jmi@|V>3s}P@1A}n4dc6uoS^x;Deu!j4Z%Xjg@ov$HNmR^Pbgi>wi+nu zAbfd{=*G2cW?I}Q_FJW=luqZrH6cAE(`U3@M@yPbU2($YvUOkFJ8&;gbRd)N(8S&C z{w!Tlyt9+)*11nO8C?=^_uML;8zt#^nRdM^CAe&Al+A~2B?xLLAubW-ROfL0`5ozC zY_0uiZ8DYK(c*HeRxaOi+q9I_#4*f7m^WsQ@QFlZY1~A6^K})2h8O5G*BA8;tI6ua z%|5#9wb*4U?h=K^O;PUHr9_{YzuxBf$L6zq4hkk@zm)Wfe`p6>{F!l?ydZkldc`KW?+Cl-Q5#tZU zzV;>`J?eoMLxO;fOKlb#e!0UFZdt0zj@cbzp?JL4GM)1^Wh(t-1LA_~^jDH-`UZBpVa zfQVbZB#F_kFo)uSe0tiFe!icS(nA=E`(iTqIpCO_yIVs`8+!em3zY&=H6pO! zi^d76Pnm`hoZS=N(RPSdus{>BxEAKYriPGf1<**ibivZ4vu{egktQlt!A%S7-M~a3 z4Yby5lj-s$g%A>YpK2V@BIqLsn51m5NRu5=9V+Wg8SpX-F=4X8V;#6_e}hr0Fyi4Q zW;l{+RKo?4vSxIbhV{_fDR%*VyK6O0JC>sx-(m!*L)7hI04fwDcR4jeup3WEGiwHq zCVLUr?MIr9bKEgqX60F}Wc#l`qMzwzo9-{|a}IQxzAr{9zl7YTM5jd?FbxHZ1^H=K zDB$g2VSXQfqp7|AZ{Z7&JuV?045G!0AoAHR-Sed|Ho_ z4$FL6jNQnOj@ag^3l%~28Qw8jD3ZvXz;h_)SucD*YwA`}kw?HXmL64>}Ua@9q zWdD$qG^4n(MlxVjASNLx0>bQ)f+!XUzrq-7?feMSknLQ%zOVl%_R@IQs6cnBynail zWa6YyE$3p3d{JQ#xb5P70I5SD8RV+7@0kVQC>>BUw1W=?hu>;$0~+d#*rOgJQ|n=}e_<1; zE)&WS7h+DC(AAs;X`g)al0}2C?6EZzhM6pmn)DY-w&_(RfJ^R#^fUZ0Zf6@ON_&@G zEt?IAFe5E%jei8cY;$K=28C)?x-F=0J99}|<@5^My?8+KZ*G0`WJlriSpOF{=gkjK z$v+9q1aoj&S?Q|u1C2=T7mEB=p2+PnwQAobxsZ~E=_nd$M-!4Zw7WDDIo}MWvxC~Y z-0AfX#k)u88sI%8NZ1I3rj18+V01yf*zChA05}I2pu(-Qil4I-U~#Brh{Pf-MN};# zA!`42Ke}5JxUk^(&Vpg-bCRr^0~3`(^J~XcOu<4-{4u_o$GuI7Ta9O?sR1?L{A4uPakO=yU6A5;eNl?rr;4WCP7F zRLG2b(_|@W=(1cr)`R{1hoAQ?N8%~>rMx7JP)04-7kUi|G4rvfSGvujpr%z;mf4d& zzak=xkW!leOwhGbk_x%w5lp+CeiUqY$mw7z4O=iwLi!4mM!DH~p7ga-a+j63{mY-r z!lTEMk7eq7wG?_b9c-u%xuVi2U5GW6us}o7mIF!MEz-tu-D`+7q`<#bLG+p7jwuXOHrdX*}nghrzppsPefi@6hwU4Vp_ z0?mX+3;-?#&y!j0ufICr9P270N@(Yi6dJpi30k@c#IAnL1uK!ZSRXAp;BVhLVW+#laGNWnbGhuSI$8^qxahN4 zk0IlC(=FYYj^cP|TW$%b~yp@cozpu*Q(!X3r z5^;vzo(W9LCTKpZ&deko%{qu#s@KDdz9|z|du3ShGE}l9hCuC%jd4@A8-jQEXnRnJ z90&_TGqUQNdaK?WXz;~Nq-;T==HXSbz|m&QsBH!ms( zaSC02?`1S>i zFiqBGzeQPaXp1|rlzbcbx#TQ0D`dl;KINzAX1hNn4&%@ZkZ(nKZv4Yo*)mQ+L&HDk>r(s!7BNd1D_6>I#50B`+|vT6%%YC8@nJFwXaL ztkGK?e9{beCW3aH2?VTi+uwH2Ki3?c&ov&jmbnsJ<$GnfPxq{RM6pfgf9NDu0C3^q zN6&+ra2sxC4NitV91iM;IV{}`UG7x{=I-CVj@l<21_cge#IuL&RIo!8oVE(AxjKd5D8b@e2iNXCDARO6m~T{;J5 zAr7PwfHy146+p*)ipo9C?W(=+q0sv~t^52CTy(g%i;6eVf+_Ic$C_9`Rkj~p4t$hB z#X@p?nj|wxzk8B#qp9U_ns}P~nE1+L*=J#pYtdO3o^B0~w=O-KN(q=vsF)tH36p#& z0Dy|ZsGd6LCgXFPo;0PEL<`>Nud`boU$g-N!g{r%J@z~aPcMXL&jb$|iQC=o3(9D@ zF@S*+S=rT3+&M;RS=Qs#}1~X?mWYnk4vQaY(`)abO%?xvOGwa|23fd1q6MTj>Ay0`zbIX+!;aC()K< zDEb$Q|1h@k(GFmr!R+5fA3ed-eRTf+RQB_Sa|wrra@a_zOko)A?d;l@kAEBdTU$On zC~uV0{KDjr>QC8Oa{t>3_-e=9ezyiy@3t zo_`Jcb?CmMK8zfQSm4D!`{W-7x(Dp>8<6}8fFq&D`EuWtTa_S45Z=Q9^AjotcBLn3CJV} z`)T7+4TZ1e8nm|o=p;;Q9K4*35mUilqNk}bE4UZBv%6WC%gkl;$zmQ_=H?<2ws!0o{R8zM-=<-TjyTaS+|_O91kG~NQ%tnJ`l$%e!iaFp+@BpmzTAp*L0vuHx^168PgV} z>z@Zrn%}=)BtcA?ZE!+cOryuw5P$4!(N$9mBd9z3SVQ8+<(ytDQ>X!)co|1Ar(P%1 z(8(cwdbND=DO zztWAuxCE?Zpl#W5-`vllzQJjO;A0eGIm_uCv6kv^R@R8R2BYvEjy2*@L1c^dTvPXm%83ka&{1bI=k@qmD(9gBy_l7?q0`N17k)6c56B{QYqmoV=^>y| z*I>=pyR?tx;G<10m8ynU5!G-;-B@)V+O2=*iUSKw!p_Z+;Pb6za9mc>XxtcDz+{`* z?>sK|&{&JH$t`+wGZCQ{dm_TgA(M1uXA?9-B=}<3c}ax5=%}^m^M_@UrHHrym-1|s z(k`X_nQyJAr-;`#;^%sG%kpBh5*ryMrNvhZ^Ny3TVq)FId?!jz)~T)-1Fpp#>B9QmWhVORWJU!Qqbxq z(hGyA8JLV1&0S!9NB|Ow;Sim}`d7w*eO7q(Tpm8!m6pO&py}Ykssq-&+IvWeGU1E+ zzZYX4H>~_i2X8y$B$%(0rH9*F7k;CZ>4z7a4vNV)H*{{TW4CL0H51OaD$LjG<%B_f znf&`edkKvS3*8N0uf@VIiE4}VcS=SDHT6sFnZUUpCCxaeu2HQE(NV21Xgz~^aYzlj zgk+|28_M{G3c(o{12NfJ5AV|TfUV8VPx)4tXfGSL`KhTX2N2gS=)LYm{NgS9K^kAU zgnGlrt?~+ovc~cw7$DVZwaZSe`boKRip2RsbO>jO*TO#BsIoUZ#-FtG5Vq-^nQDp! z3Gu^Gs#Ti~Uz%I&jLmblk>suDJbC#}X+ZjPK`LJ^JDQJCs!zbK#y8Ip00|b0xrEkhpN4mTzoXv6@n_Fhwqx~{8~bD!+(ci`4<2=**Q86ZeEl^Z37(?3-9}p(g43 zCP>KeJYNK!Z=UlS^o9iG-9@nxG>vvBiHcc|Q|a=20~T=fseK%CmtTT6Vn(yG>`uF- zLnW3q6rK(^IbTjG7sST(mC1yN%xR3 zH0J%=x0w5r1W5G?7b^_qE@Udg=H8oIo7r#rt5+0AGGZ-b0-Rsqy@TRpkN3U}9+T}< z(k?|`lQ?Sj`@mvlU5+98dUL8J!MAKwcgi=014z5H zCkOk9&L$00%MnfC#r!{RV&~K8`PltcdW28NXd_nYIpe_mj$<`7%FIg7x4p`R>6#MN zZ~#uvg&q_i*~vL08T-ggodg`cV5|!yvf&+;J}rC4v*tmkF4}R@Yln)kd1q^XT}B= z&16(C@$r+oX}_ZM8e|Uby_Q)=r{~-N?nRNT_Pe|odDnVwcx<(=csuDOx zW@L~mHckqqsGF6x`K~amwU~y|Ua&3!jwT~PmG<3EjN}k1EKci(Pc>6YwYMjWsbH$I z2r6Y(0Me4*9K3Z`w-n@Ttp<1cHw}*K?fG%-=k04aUTpKPlZ(&H3@s*M#*P`^_29dt zVG6q18z;VZdKT1si>%=DZAF7xjn2|0tn4izP98Okyh-!*8BFby#L#G?>1d$4)W5 zDw#DS5AChf@yzJZE+${hoqdA5m(Yj=?&zrYxYMAgt4~)+QcI+BZ~OS>UR;%niIbcv zMAl8vKQx!X60$=~7Jl%q(~-15D78NO2B8sImZuW-JJVPDZ>Dbt5Qa&X86#bAj)eV? z>#WmP@+J7pPi#U56*UVg$@uDMA1`0vb>9BhPj`DgjVGqA?K7Jg{xMxejL+&TWn?R_~#al+fPdu2O<8VQ4sEyLd zmUZe~NH0KQE-Scu$GXF4iDnclo0-}vf33*SZrvC}Zbw{uubiOShra+acb@bqvbsw2 zwU|1=fVluE-nBFky9<5XtdC~2T2Q+FIz&{}Q|V;Bb8Rt1U7|5XNKua?br_W7DqFUR zgN;drQ;`_CQz-KGKI?2KmCBjV?vlY7(~1AAQ5bf+*UN(L4Ig-UuKY2u>uK%1NdTu_ z{N>n9%F}tDr33%FQJ{|NN`Ex1iC#vdO+2F~SL!NLI%@N7dnRKIW6GA0sUC{f8ySH$*m@%$K%(Z!+M$SzOxerw$#$uep=pR*6BW|X#HXlv>iS+6`%vS)A${Mj_2u)Nb_QI)+B_Hz2tho(3r;FrjJ!0)a5W5j-j2Gk ziL$T~v|cE79(|xEefJg3a-T5f5Y+R?;CRCf@w9XlLlv~>i8iYDAVwakyh$(nLJNbp zFkZsYHr~`_eQb7vZTDiZDbug)TL@}TY$az==V6&mA*nd4-q6pMxLUbjZkZ7)iW;K* zRLng3^h}u!Q$1Rd#z{niZoIxJo;uaAdpP_MdX8SF{*C45-UCZeFcby#9`&XcTyA6D z6LX@jhFQtH(@1K22P8|@>O=FhGMluu@UsM-wj&DIA398eDwL=RHsN(fsz=fAKz z+ua|(@NPWaM!gmQHSq8_I9eQ`O+4d2^ebCyAkqpX#= zOl^xTAs{4TqcIdYfp|Sg$_Kih3C0?)xufC0v& zM7Jg*!Jd>#@la}m8Q{DN9C}`9l8dQ(sejOVC`a+_;ghR@sidZ!mA2n1GYx3I>TunL z3_JYB2#)i+&fqv(z>Ay&aG}+t5VUxYcw84Zsm2f`9H%2xhehv;GQqiT4rYqO^EwY7 zd4ua-f}4dwJGTiv6TZ(^YZ((y7nc3B72`%caG=bz9CW*_eRocx!xhoU_5GY*YEIu= zACKBlxF-Ui8hu&$=OOU{K>WW^F+?p%sc7dqXMGt;X3?DPXCG5fah%8n=ZIf z-4+XhtM%s_zQv_Z!4CpHe{TYh9^XGkm>0al@c@)R`I{|y;3jXR9+<})sg_9fXa8>K z?&(82cTbl-(*43u-D5luM9~|f!Gd^~i41eO5`9x_tl7Wx{`niaPx77^*Mp{|4hZk< zQIDT;(M&31t1=!*r=f=Rpj(Rl>iJT`%cBUuS1@FcbZ|WAFYam;I!?>W9NhI5~|(@Sm=~ zA^EvFt|(WVxC>yZyNp~b>`uDqBXfJ)+^S>~KSGN*=j7GcLEDd@^s5WlQH-$oCYQR> zZ*@u??1W;)P$;7^?wnTWesRgsHR=PA+f?Hsi`i1@9T2Q+_X}zLd<%mqUtom&&HlgO z^xNf*!v8CIr~4J%_g6044fDOHVnqD&U%}G!?a@v&&Pv;-e`E6f|ABA(m|QChqzR{mbVhJi9>40%++u7 zUH2Mb9-0kKeODZMDOQy%+_G{8)q0jXfNP{s_|O#P%32ve_xxwzViS*dEmzg|`sV0H z@?y*k!d4oQ!u^b+vnP4haMUyOvDlis*P25GCCbSS9#lR7Wa5~-22cM zS_JaWm#6x)O6)Y46ZPg_{l@0UDj%~NgUGC;9IN~%>8LMlW|>hZCnL-@Rs!^kJvFBV8?`TGJsHb= zagIF>;me>3Pv1SFHWc>Oh>uqgt=FWLgK)&1P`~{PfWzM(Q#cR81LMcQSS~CMLUQEi zjbj{ct@yFmQQz9TLVC|(>7g<_RjXqh6xXfeef0r}`Au866Um3?q5!S)s+Ys^M59NC zdNNUr@VlVMU0#N+SQyP5rhE-V?z-t7zr*GuZbC6Cv5m3b)fjDu13-hqGywSS7m=soYrS4Crc=aQ^no~>vao8CK^~{1 za!Q@MT~8RDcl@XTIk=2WRKI_=hd+6V1aY8$-lOYaD6)f|48gsrqectDfPI#re#;&>1N83No3p~8hG3vWR>J9ejNWc`FkMpr&U@c~WO;7W&V&*<96ZT3AV+xvuDIb2UlO49IDAVH0#bH4o+CO`fS% zaoSb1ALiU!%oB;DNTtJy`~k+2phnLm6}95q1rW#N%Vd++HBkZzXn;gLrx&a1mh+>@AM`pr-Jz1q$PVvG_8M2(=(wGS z2cc2%#oAGIUzdEl11>5o?@I^KcuGB##Fczss?umlCFMX%o2a1}qfz*C8drk-dr&x| zfyu>Elv5y+Sx?{*3RKsPgGwWptloR>zK~YINTL6r;uOHR?$LbQgs<_%(m9N5#yV|@ z?&X6J+Udj<$fdV!D&@Sig;*6AfIYX3BW#^bomaAcnG=f;_V%`EFw|1x6gdw_QjCrGRI4A(6C*0CZ&>1Xo;Up! zg-h3evNZ{78%XWY@ov{uQtK2U^MrTXFGwF&u3FKojG2%}6*@I^O%D=t#1yM_t%2|0 zjo*r8!|3}5kS-2i@pti>e>$DRu|61cF)n1M(3l^~*P(`|&iD@EG& z*Hcp!<)g~knDs3uEmteRMLSlVYyJlOrd_YsB5*X2shAp?F$^{Xz6)$%SK29s{77yh zND~9OC#S5Q7b|syf1Nw-x72h{3T}TZX#msFOKwZx$Td7OsvT;+s_E1`Rlv`AxV=N^ zsTE?glB)B<=5c6>jz!eaO87!}t;0O$z^Vi97~vrseS^j~%3_z)Sdn!_-NSr6qXHSj z?zy;nXrV-3$8Fsd$)LAmq$gf})1K=G+-LxAUUr4#iL`0aR@!e#bbT zcjUTdgHcQFGdN21HjN#6-Tm0jWldXP5pHA@>>5*j^Cba>sxcW?KW1Q6?YdjEN}yUI z*ueIQPRk-cI|GjcezcNyfJ{LcqQ2VmYzLoeKiS*fZj1j%5v$;kNnuvWBEdjKvahP8 z_yJ&rAx(k2s1yILY``MGqW#T|+U3GPmUhCp)iJny%U zyz`wiXU?2I&Tl4@xd(Rc?Cxjpy?3wc+Uuh_EvFdK`Tsz_NV>gXUTF46P{okR{e4H^ zN&P@pVFr{I5mxZ{5gYmUS5DEZOfTW+7lh|p@ze>M1`+B4f2@c9Sg0&(;-*#ef+OV9 zac?&^GqP5VE%%qDvG~X%ZDSfFVENRe^u?*z3au#z_de(QHsLhKFwmuHsWq}Yd3Lh~ z)pI}?O;+a}YK}LX7aPXHrf6T(o!i)wqF=TeJTZ|AKU}AC)eCgWHPDO8k+Sb_o~d2S z=pk9~W&Zw5Jj%ZZ@7#wl1^#Yz@_J-gnh;nw|C~ip1I4+omHQz)Ms)FIJTQvPrb_&^ zaN}vT>!w`5BZ1eR9vK@>a^;^Kn&6Opk>-;xaPIL8AhXp!gp!$hxP1|a_M_m2R$A6M z!AF6g!+*7RR!&)Zc{w8Fhx9{Ef20KP&(GG1Ws|9&pVJQZBw$}8G99jJ4X|GoOt>G? zO*6a(Esg0^)26d2o#q`6C|QD_gwmc+p)UXuUSo2xhCx>yYI-zJqBibG;MAzbjDI7% za1C75WnQ)p-r&;cjF?c)Q-l8GI+$tq^$}pK+v7&*d^ai+vDu9%y z@_N=C;T-;~vl!o_J5OH9H>uymUALvZ#=%ZbQv=^T)`m=RvCev10ehi2aGdZl=Sqwz5D4& zIN95CEY1A)U5Fk6kF^iBEhjAftu0%5`QX$0^3na%&S6oXO^Z&O#4j{3n65RsL4(CX zcqG$BFg!i!Pr>H7qWOqhza1^skTVWOvFQ<~r}IsP)>L>&O7RUH|spkmiM8|oh~)v z*J`AQQPX8YtQ%jHleuljOw=BvEHEW>j~{8(+aD5-tG={wO6_C97<9){q(nWw`gH;3 z2OC7w@F!_(swolvC0uU4e_IU<`}(wadUKjU`+g<_ z%dWM!vHFPP6HD2l6NW}(J@ZN=pGRzbHkXrFy&|-IGK6K zcw(1xG>$p;-k-p@iCtF-UyTu{$j5k{^hCE_$vJ)hG7*R~nB%W#UB|}7LXeFrwN}wl zf*^Vi7uo!;zOve8tU?guA#wvr;`quMI`3hZ1J7{h9SM^ja(z&|Vw43mv?#xCuaY4EPe>J+E z#-aF#XB%HB(0!MVb70WNr?#)JBpGKa7}99~5II?)$lVmxpcMNyLq`7$27jGb$I)!p z=$ljeEyLwZ*bukkP=cM3^qv}())L`(B7Vcu)GwCP3FE!Hv3rcYma+B<K(;bZ182mOLB=T)rR;gbdu30!)$Uo&L z%A~YN(0@If=)FOf(j{p92QqDaf-TGc6lY>$l1fuqFmpgmT#}H0fl4k;C8nj=+gi42 z;(A!j%1vvb(dt0ZZf*CLUeb$RKvznSoFKrv?dNPs+i4DW zY|kd6av>yoYkg;zvf6jJslzbQT8@PZZZH_qS)_p+Z3R)z42J9%O{B>S3dQ{1r2FvR zSp|DXP%iZ|MF^z`_-J!M$`)8}H;oQVa@RZNZhrwHxr4nUf?@rFNbWq6*Y8uTq9fcR zoRZ}uvbDzkOP-n+gx6@6QA=)mv405@UyID%+<+Q;pT|#gR%bG!eo|5oj~EeLEFxpCz9Uj8C9q9Q~Ny&t~1N!~jU zEpp}>FgPdq>9$u#R3}-=QXJ$yT&JUx>V6G=`oS=VJe1Gc`jzwY)$NED({Zs3Q>KBy zWr+FoV;l$w~Z^ zM&~_wBe-El3s(KPz}{IUAyh7lRbspO+|pb!WwjV2uVJ?OL*4v4JLFSN^TTDW(a%nz z7gXj22X=DWE)n+TvwE38*07$AdAZz6B2UaYOb^kNJlA%TpkJwSLY-9t=G) z0!j03-N$Y|pe#Zmee%Dr|BA!R_WrzvfXjH~&lFxZUEbP5?>~`rPmtw^!H~}kx~b#} zxh*D-#P`63{cy9BYt+D@ilC+;6mOfgSg2&D8VC~V;;#^NW`3d;ZGi11yyi3Sd+@pO zz`U_#+}Qhto%Mj3bQ>=-@1?@^i`m?#L%yy~>zVy0fsIP2!3N?uzgq z7rsMF__h=1`lHY%dp9K=K-B*M6HO1lCR82nPB>E%JM3rju(dF)wgXy-q z>7BaE;iI_S5B*{3un%>o)JB=jeI@Q`Yb#ChEi<9}lImf@3yIV@K1Vm1)%?E{TEv@- zYTlc^oyqaF$2X=Kq{+_%E*0{U5qocJkY~bHJ|#lj(h@!s``lB~f5E+w2!0la!yL6g zy_s8S3+q4WmnJ7$^@8$q2QV~6?BCohh|6y`zS`(>>3?rr$59VFqO=xsRM;{Y0{l~T z+pKmQ?yYeZl5&64*E_gD!WV;bo@3;s;Jv|eU2QLvplXwu6r+V5-aZ4}Qketp%aH}1A@AJ~ZyMCxyd(tt=mjxO%Rf+8qsljn2>AdaM`Zvi zB}G}{7oTVJS-(60^-f>R2APWM0!U%QTSoKMd`5vw*u7N*0REjQk&U**$0dIw=IAFMt=7aYs4fWIqyo$yF!#Ej7~}JijS~eV zVJ#Pwe*9ZZdFx{a*73FM@cZq5a}%$lFraOyYY*#hwCPVW;>|FIGO*)&i~nyZ*Yw__ zV&#m9Xfk@B(5RWY7KrMF%GPVnCx5pG1KRr#{J|^y;&S>AXMp2yLH+PK?!P&M$N%9B zUa7AC8=bz!y$8tD+trmCJ`2xo7x*{6{0Eeb(Zp)Tp1k_E#Prr}Lrir&`oB{At0Fcw z7}f=kEe7$A&|xnB|6Dfr1;bM-hROepGx%9`ln2%yKEok3K&-DATSGLGNVaiKA1bG( z!I$8m&VY%vXPY02ey;)k+)#gfft8(Nv3F$t3snwj5O_666GGIlf);Wm{#Qr&++bni zYBVp*@NfwtE-6*~*LM83P#Z%jY`=*5w^IAooimK2J&%%$;NidT_6pPO99?1U{~l4I z55I~PNh|!d+WgP0jbv_}_2a@!IsZjssKjF8xl2Iw?O&1mNJ97~fLaONNaHAYD5IN2 z(cSOf^y@JdNG<$$)pRd2_T$}}0>@>Y4JSL6XADQENr=SDG=OX=RDSTsfcCjGtd!sN;h9;CjbSp?^c9(wbeT6 z7G-zYwaui0u2_I8;>Msa&JNJ5!9DS!65XseZ1R|v&p>s5`xv!gzXv5x%WA_cw?YL@ z7gWCnrPWrS#s2cs^2}dNs*wcmb8N$gM|`sfDD}eZ?APl@k?hrN+si|+6ZO}Ba0bWitYhE1zB!n%Nj0T688u~>7G+e++G9gbDet)`{xvq^FZ=4JvX!t<%=#!#o zb)ftK>t9N%UtjNBbIe68E;?Nb`Fq>Lk~t<~GD~(=*HjVFdDfbK?yv1k=Ng4a8ygmt z&Ri?M(HFC_earA#)zjl&sa0v&=X;YYMto()Wu%x;tin-yCwVM-K~GuvB)d0oSY#F2 zpl5Rd{OsihYOgb_uh|>pT@mq&G_?m%^KPHrf`gNA+UV=fHV{ zDP4WyCpT$_6b%+HwO?+-VKEN6PypfgCfxbM?W=Y;%`7}F>2ebL&YY=|n`dV}mlvRY z{avj@#_0430q%XDN{o8Btu<-%-7^qq9+Qkn2gj3UCSLc8b8y^iZsDTFBP5IY(mOtM zcEtPqYS{$IyppOtZi@v$z86G4Sm^*ctiG+uTg&0%EW}Dm#T7|IQmw;JrS&qmz0E&q zw^d%?eP44Ie}Im}`T1BIEiO3RP%ORn;C~*(kN1f$sTMw&v)kCsws*Lj0^9Gt*eV?z z4x}BFNH|-*tbez8D7Rcze<@c`lega2C+wR%>ZIIAIVZ{%ZeXJ!BB$p?1Gwc*WHs1# zI>---HyV`-PM4O4Z@5~l%mh_;SyMAK|; zy4QqP-{WAK={~pU(M$qS`v$6Arr}WwB@n+zGt_t$&4}b6dUc6!)qi(9=3Np`Di>Wo zSNjeGw-63;3NCikn3y!_6bWx*S$I@?me}MimqcA+IxwPv?i|Uk4jEb$-dWCO2v(C9 z!=!{-C|kgI^ZS5E9OWM3SrK>dvy;VTd#sNJd>rytD-W&!>T%11CJd#5-kUu zI1MB>Z~2}OwY}=S>-JEyZNpA-ch7NlZjpbr)>VpR7*`_VE50kCh16;typUeOjZU58u)6cy@ zq5>1I0%3jG##PE6zHlhwUasDG1=llH zFfEANE>m4^$Y55SrG?fOIx=K{NZpB*QyLy(IWQ3i#O`f}Kp9L)#~A1|a8D>rM<@XU z9(G9EEvQI;!ZQv^WKsX1=lprd1jInOWR>d^feVWR?4h z-c&?1n5@^)YW~RjYdHsXbW8R`j6hw$DQD=KJ99Lprvy{@a-y)@@P z6=aWsm4WFFAoqpjB;5h2AXxJ7co|W^$|bfm^+Z>0(;!EhV2c);&r)=ARizR9ro&te zcREXFJ}4MIn&I)?ZYJ}t@lhY~J;O^+gs{Wci!d4gXOsvbR((!U6vp?e z)?NFG_*27fyba;F_zI*yYQHPbbZ;!I)5sE=xzR}NR93lT53SGhAXhUI>?e8|YPKdcjPFG2 zW1pm7D`wQdxa3;Z*oU>UXx`+MTlAZku$z;x#Zjqw z895p3>Nqs|1P_?Q6lkZuua@Q(J>_r-hEFglaJbG%6tq-SQPVED7qal)tZo1XHrNWJ zqd9twVj|kt%kR@Y-6c7H^!S%vCTBmn>{p)~?Eb`c!He&?F-rnjG=~S9B>R%epmArJ z_R0{dbI()=zJpsaOMVxE^oRYlA^2XqA=W_uo@egPX&k{s?EJ78(mZaJto`C}L#Aw@ zc~b@~*a9t`kt!a}C#U)wF!uY{*3U6U2E2~F?r9Kd$SJP22?9||saesj=e-Y5gT1C< zWk5l5-8KulmNb(K_Bl$QeNMtq0uBa&C7+<_T**^pJ)57Z$DUA<);WTk zGq;LuU#~V!Ovb~=JjdGzqXRe?O}Ue9$bhyXOs?({Urv33JE+HmmhctENSJH575;! zt88*s(BQW(!q1H&FWm=PL+f>Ioz*g(&E_hyY6p$k+Ka3X6cnj&aN$(eo6m)7?afR( z;)7FD8*vALz`5FAY47tt(rN_|RJgrIla-xC)k06h#so@4K|+UmYWM4$R;cSi#2m0) zm4uVRohFK;LB43#M~=VF@wh!46mQbDl>NX1+)CKv9@MNr>U9N05?+SC%K5He?=XAN(B98uXu{9XD z)B;#dA{7m3j%o=QW~@ne+Xp*|ullh&z#_ig2*6T_`<%PuhQC}b^tp2{owOeE2Xu7Y zhO{2BwTROw>7TUsshEN`=6lX|*MF*lN*1T1-~8T_O-8=499c|Y*o4;ZyHgZ6Di$WL zS`PzVN`i7LUPtbeu5IPxJk!7yCc)l}&`RJaD#je|nURqmdat;|SI|e1_E5|poFkW1 zalmyu(Td$DI;J;e3n=0QU1767d+|ow1wQl$LHi?T|1x2m*nr9&?tf0Kcp~q{uF2>1 zUBmY>*v>+Z9WWxwUh#G2b#`O|3zL1e1i!MK&#Ioc(^QTFXm_?BX91D+nmY$o9;c|M znW3q!$e1N%oZzu{`!qS%()Zch@Xs}0Lo3UKY+IyxpJw{?PNUYV%=&qVGE!21y(fMR zZdSXJeNcmc{+6bTzk@nm+&{->St8S&-AKlFIFm=A@mnD)i`0$kS@qo_IH3e!%>*Mn zKP_3zq8onyhwz;9XELEtlw1Wn?@T#)>VK{ zezFi;>Z?(+5Bnxi89(R0c<-@~Cbxd*Fe{I?5_7KRCde|pc`3XntD3^O*7w+lUiZ_5Q>FHVq^6|Ez?H3x8}fM- zXab$pnTwYFL@Y($4Bn?TY-dWOFQX`I&fU-uc6TS7nVW145jZfTgH#k2fAu>a-l7dR-f08Up=Bb|q(zBV~j^_2oDnV8r85)?!Zy6n=BD4pj=N2c3* z?<`<-do(^S%_8@m{6+y(_QCs+Q##j{>r*ZXxiw%nv%AcTV(|-9sW;IeQ6qTzl(xrO zV8}?cL&$Qk+Htj9R3r52!qBi37E`#_mf57J!|bP zimW7QRkZ!C)5=noVt?1^g2_ZKa3ToDd6j6fRq}fPu(S!>o2mww@V1^4)h+P5`C9wH zkgE*Qk}4K%bhCBXvOXH6gH%}0Y{}zG@U@>D8Ly>2aSDkUHf;Q){vIHn7~$MC4dRR~ zjmyG#o2mAL`;0g?vtuy_&P=?$bk9+$nn^#xO)@2q#O&yz*Jr0<5yjIJO0uO_x6aqGGFg2CHlRf0B5N?k0XqH?DF=IMG`Pbing<4 zPFIi=D>-r_#s?nXp+#w)T1pD!41Cj5nQ3FiF>#bA>?OKL=a9TH5=TMeMmzraTWD}o-30qt^M?*z>%c$S=zJo9 z-D*~qh@n)#jV-mo{oTATmXcnR-MFyTAJ!O_1OW)D2rJ{J>JC}4kifb%LnUieUpck4 zv^Z$_-qH8!0EyMI@^aka+Poev5MO~iyOmh;KL9(UqFV@IW+r6frG)AIK%?1-1c&SZ z22rNAYK*l;CdZbB<8I=d)z?5K<9Qf53$@C#R(i+0$x+bX<+X28zLV2xn^&wi(5*q= zVs|WiHwe7ZMiZX=o4dO8F(~-wM3*bS6P}H@l?+kmc-apz+nFupNAfRPPz`s;m$2;b zmMSU32o&lqE>Ae*_UP!MOHPt>swi=%?XC%A^PL2O|8|)0>Ao{c8%rz2;7)V`^!4KZ zF2F^*78-!+ii`TSMaA~hHM9CcPeoU$_1%@Xyj|1t7-T}#PD$yanh54Fz^(1yDwh9< z`UMf#y<4fIK!FiYCw*}cly3ONnXDw>0%h+W*Jg?)uG(ybN~|2`o1^WmbU1z^d`~W| zaULv^yB`jx#&PcwNva4QUB}TQ+T*gfb6IckTjKqt#>GJ9ldK=M{#Se+^Bw18boCFu^}gALVm3sJjf+3-ppyFr zk=1mCd|utQ=gF?r2bWoS$35Aml=*M>7o(s*s29ljNO;G^*{Di9^W%R!kC;otJNKs; z8t>=+n%sG`W6RF;IR^q6_Kg9n@ zA!5S@;$pe~Z3!}|#jG?dz9y=T)Z$@}O!l`GB<>w`aojwGVI>FV{ZPk+1g>>VXk=J+ zPHm18K2`iZLaz1iRjP;jfo73Ek2f^(*~K@X0V<%!1RQPFzIp@eC{?3H)w?7MkW`&A z5+qzb$dcy|ClOe*BGTPAfF1$gy%t^m<@LBch8>{C^j0)jGeT=#a;0IV{Ko6qE>!`W z;|)!9HU=RRa6C9BMdCIF5sA%z&aDZ;izcs|T?$yze#+(DK|TCX%Cr0h^~&qtvE-0{ z=`)P5eBrMX4!uY@6v8X@(eF} zYFkvW@mv-#ym5AwI8XCBqgX@UH4J<`{)8#GSF}Rr}qhIVM+|#}f-3KTar#ME(Tv35(9u{u{)n zJZYks+pA-lbPrZU6pWg0W)>B_W8y%a#SXksb1@V6+iz>)gUM`i2C(s&fFl3~ zpp|pALuo?os~607jk(?U#H4;^W75&V%RLTZ<9sxY>>sxRGqISpEDH&({VieG69_@1wo5~2kx9FugKL?k`ol67WO)dO78je9 zH&lF72?y{&ECmf%NC?W2p;xYujH-zX*Z1hjtXI$ZQJuK3JDQdcE%qN6zcatY9ZuQ` zBPWXv*GVB3lTB$pBNL-ic&N>qnW9_WuOG2qq}Mv2LR-=kdx12mUh~|2m)9AS?-2LtW#3O<6NL3IJOiQH zLX0dcj3^g@RB0|XZ!4Xz2{?{;;w>O4d9-e04c}VQfSe2BUvQO6Oo&y~T=rGG=4jaL z#|RS-&W<~%D)BTxw1ymD1&uH{Zq#$eETuWr@nP6vuU5Wh5k~!g*~zM_%&nqU9lTDO zd7~m^K1o`E3bH6PDvEm`-$P;U#CRse6BI;113N}Fx^D4rsL$$~^(AOJJ+!ssj0cG~ zXD}MdgOO@n^jQt!KiW6mYe;BB-5w zQalgC3^ohBa7O;ZPABB!u$};h>$VWlx|nr2Zz0)t7oV4A_a;*S6loss{ToW5Tb+uj zLh&OMN=~iXo&h7TtV~Ap=96gyhh2AT{Z=-9>BOt6#GOuKODCmJ!>-qxL>U4XP8QHr z7wpTbnp-F|Ut+^Lcls+ydVYG+blQ6fZxkNi#-w9zh*T*xC}qnoF02_q&rsTzj8pzp zkF7ZmtF2l2XPn{ zR@yS%w0PDE5=}4luJAr+`Ea$r5MEL4umA;vlO;f7@l5=#ZoXh z5s6IgDv4{umj}b{ofrpfVX~%M-$-_MuMyekF6^YrrIfLxmLpEWgfDKEa3^?=$}?}e zdP{FFJiJMo%-bH4U+sfM=fc5I! zMp+3JKwTW;n7f2%Gkzw_l9e(1jDvuNqc0m}vo#W%J;E4-Xh)ExUo)TV)C>l~yxR%q z`tX+!Qwr%i%x)D?K7P9zn+P6qr>lrmt+E@1E1ezTl8J>m(X(Kl6l?s=2ktyVGbB_x)* zke<;@mqq72H)wvu(??|%rx~ZEOD*CjpS9hz7TMO3HDL?gbc9RFAF4tc|r-`^>~Xlv>O+%9b1HV5q;FioUt0ne zdNXfyvoo&iO)~0t8f0`6NbdP9sRjEjr}YhPqdrnli#b{tt00{Z#M4l*cTcY z?Nm=&bzh*y)k>&zD!Wdz$5|*|NxF;7$xe^AzJ>S#YGeX`h#8ZyY?r^*F=MeRXaTrS zFsm0Zh=cE_hgSK9u?cBH{vaXg)g4Km9=FG*R+B_`&<@B*FncLp_}gKLFE!kBPvNr~ zy3|d|12>O2h|B}4&$7TIBqSVxeiNGrSG=QEuY@jP!{}7MLKw^RulFN*R}*+ZBg5sx z{T-&Vi4LgTu|f)N_izrv=b#wEdxCCu>|7YB4;L`8a;mL=bVe~a6@2G+N`#*W^iQZj z3TK`kzhL3Dr7Hp)2eTVYa0}BFcqZv&O;&Pt&=f^aj{;&bOWiFbVf~Vn<$aIQlsD(Z zgSPB@<3()9%7oPGNg5_MS%t-%W{?oQCo2VDti}6;?A$JG>Vf;2QNaF!x782H$Pf2% z!RzlmGJhq`)}2Q>?HTG`^z6na-WnSBFNhy#lm0sU|1@9vV`AkagMLxk3fN*uCV&^S>j zsWP`kaIj8wfX6xG2>cD4CZyy@sZTC;MScBYkDYQg;|?Ft?TB}`T-ZHCshtch7!1QP zf43LDIb9#`9o^^}8Cn6hyV#u2XwSg5K!rY~jY>_JtqnQbOXBKn0l($rVu&dgbqk-` zpIj}PhSoQw5I(CgIa2yYH_S^lafVxI0Q+2hj1<%uwDr$OQP=$rN_Xcr$XX+mN^0$m zYRi5>C|BQEuuOIvuBRA^E(A)`92Kx$be`VKy9QANqN3DR$}=>{U%eK<&b{p8Yx z$u4eC!*X(b?v2>vU!j+uLLpkB&Bo{ZwceC&XL5|$i4vM^$hpH8(GzqPKSYLlasCc3 z!y68RU+!MO^GKsT%?(MO^o0{1WvWWG&=M5fhPB5WeYB2$xWHRt0r_Mt>?a#kQG;Kn z{Ly2sx`v)P(4vw?>j^CwspKfQgG*#i24v~8SaR`HX0smX@bR)Cm@!eB_K2_fQPpWy zem#@`P=-fAKSmJUmp_`yFlvu7fY3&{HcVT10^4QTzZ6UwN_4Q*SyR$Uly?Qjf;w~g zOP-K_B)9fx! zH~+9U`ZJ64Vik}ZZvr@gtIqb=#G8COIzcciWO~Ve`3NQC-+x^;9IGP09oQIB zY{z<|&K7hf%tMs*X|reClcTKx>ERe@Z_ej0Hp&pDo|Mx?Ygg+rHi)$!%V2wz2+A5)%80V;qDx0(qr+Hid~pl z({Je|-MtJ3Jt2{v)$_k!c(2kq@5CBEn<jydF@XH(q6ubZaP1d{&Vd@7vfbHrjWR{uLBX1>w+#`dMnf)Wh56((4 zJK3ias(7NO3QKEWa&xgpuP8-zzb+5z%nNT1Z^m@y9C=77jx@ePl1hhRync?7P|y}? zNd?(z5RQd=HtHl3%%9{Ogn-r9Z|$w^E*ifqXcl>vSAl37hvw=V1^RAqqtZu>JlMPA zmR}3-S1Ma2Zf{80QyJG?P8JBBU$eUpA8CtTg0m5OONg<|w`Rc*9m!4pr+>fGac;YB zU^`O4`p*L>5;AQTjXv}8(RU+_ghcPw3LOsWCtK1l8v0&Xjd4jihZR8v@Ejg8d{By^ zO;ir>(zO4kw%ybhFif$JQ>RWP9yn$(5)?RX*xA(6WN*g47sx|HAR{QHWd%1Q8%e6+ z>t!IOSAT1l)SP=ZgoDd*qO=te9q>R2Zup~lv{9IqG>Wt~Mvf>sX@zO+b_R?>6VsW`LAb7Gb#qw8 z<>@HZ*Uv<7lj1>==DK3Yb<1@6ibeU2e@Mtc8gFQe${RmKVg9@HNOwle`}n?hVS5^F zpP_-w?nT2M@3Wwa{Jie&^D}aSS;_I-Z#2}KoRGt|b;eNGXGDv+mUzb#dg@P5>@V@D znmJG?>@X-joN`e%QtNuO009U%*dbnxj0NAZJ+U+1;_d&PsT*K0xK zx=7uwoGmwa8q=5c!+lbb;j8FWmKPoap(Ez#dW9{mcp9y^p}i8++lr|SyrUB!v;iZAWCMf7XGXd1p4RzWvSlmIWKRtR~+9Yc9N%ze8B$ zmHx$D(xKR_cv;QsT;PDB5`9FXG;!C(p@^wc1H&Q=;*5H_seC@` zuaH(1Rc5__Q)V1$s~8p!{@H&jl$FEo5O}f{5do)}2O%#! zNiu(YRCga;q6%8Q$OjK9A0gM~7#8;;kf@0h!v<3?QsWw0_Z=ihXzkM|PO*JiDjX!pHZ%Hl!+ z)vL|jnvayko7FLAJX}1I@1_F3`x*y_FqUVZZ1@D#lZs6!H>jl9WWo@%EfSYa>>~^B{`gdj4`f8 zdPtul8b;cEtPy)68&+oqm%ma`ac16QHWq93m>QGLE)z9ZyzbuAZEKxJfc4WV4;QYP zwD%xl01rf`Wzgji;+HY1uQ!`~7I_ZCx&VpIwDr{62dA=&U4lST;%646#=O^=!nkD%z5ddygRs;)#4>{2gN+PVc9FvkWJUrs zsdlY$e4tC#vj^(gLHyz+wRVUb>{M|~))Wi)5ivs6_M~2j^0kPbh!SS3BqZcjAkCGu z6b};dsRFjaB#(J7R6p($(2Jwra%=i|3AWd0AJ6E;8yh%Htvr`iYfkWwt$WA8@?fJ< z-}!i)!Z_ee(b|{@iTwi!pfyKVnL`wJyxa4L&6~cq!Pb9XO|4hGxF`7S_vAb~BcqNCvf)Nor{D zSlCTPxy6*;&_2Ch?$)L%zP!j!g&5DuZgD49WC^$%!$ABp;ywo)44TAEKgn43f83kU z)2lxkrynG0wF}4E+zhA?y_h>!w%@U&vu%&%YeVmnho&8#Zq|@3K@DXosYp_J-McHM z+m7E9oM;2b$>-|N)4gMBbr=JChQLObzE{9VqY7`foU3^~qMzNfTJ}!+$5}%v>heNvm9ks8FTKjF6J*Rv zTp|Y%LBYYv%va_2jrm-6%qz-`)oJ0laazSJhG`ghxr#yAvCrc%LFRnxY5)mp$miRY z$!v8mO9UF=G%uMl?5$|PFz2x{^Y9bXvd*cGz#B|Ht=}IfmfLX3taP0?g32lP=)Gor7P37;~nb*JipMJCVF96B^S zkG?6d0fU2lOd+g7viA&rdjV{ngtiIaTVeV=QQmq0$bIm$$uu4GtqhQngyhzIo}s)blOqfAX&|u{eukF|J|VVXy)ZQ#reAF zU5m&;yi2yYIppFSZI9dktOehl9o-<|EKl6`k^sWT8WLj#c54MLw68kpZ#g-={pIW1UH^OW;AfzBJ&#t1I-SCeRb`Ew z-P-7K-~6Mi|46C#la20)7Jz%ly8@7P_m-J{dn1W66FnpLKk9z#_LJxL@0hlT2R`5b z`Q#-gZ{9yXOIUZp)#&vye9GN?-2hdmES$;50|u0 zNGJc52d#c-_HB)5ykW_~ zPP{gM=__8&HXonVLpmn9*q*2t-eitHfBmy}G1sqfbQ+V}EjtE|u7t1|x+_N9yZy(b{(Q!$VG|1}og-ua>&`h;HNznTbriU}Nl2!7Uo{Xl5kt<}U=!8n@# z)i8dkTd?qkz_p~lYH6<>=d|30-R^`wDEP6DO}%dozk_lb#Z3l{%f#9neS+ImQS5#0ukD1l%M7X zmi=?{Gyc|Tk7nPOUo3)}tmbpI?-M27Qzb67t-rsEhIiV}Rd-<&Wl zc+ZI)T69qRoK#R@B)~9?OB?XLg~hZxqp6GE)bu>#f_d8Kl!~y1@&&egj#%Z!n^a5L zLiSdxu-z5#!l1glxg`(pZUgVduuRjp&2F_n!UoP%Rx#aa|M#%q zbhwunJ7CRp_Z` zd(oldrjs0*W?n1CXg02Rc~u%S`j*1WSuwp%Tq>57B$x1xnx^rmT#vtI;uMLSXDyOj zjV@lT0r9sjFUtDAZM8RMP>hymgJlw>=m)5IMqi!tEF{(ZYCP%c-7nr9h`{T4y~73%a&i(deNxJLuuD(8}|#H2o(9fWY}( zgvq*&Ydgl4Q!!E{U}o0LW5&exaPa~l+R_LgN(Hl@PSgn9fW@7ikE#syymtdFB>*To!By6&@5WbwO@3nQ_)30t>qQ}l6eqAj0h_0e|*l^dAD20`vJ$}Vg0jR;E5+lKvX%|<*`eMFY zkdjCS<+$<6#?c@i&)J}?WHET#ycc;O8bO+IaR|DZ^qT(Q6cQ+3gRVK|9gN^VuhRmF zU3)-b{6T#)G-;MJHWrP@fvw!T zoxj9yS&AbpwqlgCwK4+`VO9Lx!f%GG1K};kjwDcNvWA1$J*$DPaRL8h3GcJh$)ceX zYXRtKavR1X|MR%?ISLWxR_CQGx)QZ9d)NA}S-(^ALt-PNk`1@vu8CV+)D=yI2k@bq zb}OIXSarqjqRLcaQLT>0k#i!gMpxRIc{a58Iq1!1IJxQhmm+bpo6nGSZ*Y3s%(y4p zmmh(iguV+k0nU4l6YtBQul?F-^&RuQ;W`$TmrC(9t#bqlX~mv%+cTN3M@hdMZ>-v+ z+kCAG&rUt}KJ`!|SCUrd7pVMYo({Fc_DWid(RmoE>N@muEY9$+w6pei{BRjZk; zl;Z7pN9P6OM+Lgszn6G8C~Ew|!`yg5)$V%qskja(bGl6Y)RaTC)_7hgW1E^ZKvLTp z-iO#bGB}U&08sLeSBM(5G%G0n;ne!O_ATXX#C^n9r-$A=D6vUYoYD5UvqGF6Yx)&B0S{)sOoL=26jR0TiDfP~LX zQGs0L#?@%TF_l`0F}LNgaKZBQiM_H z@HH|0TPor%l`?>>=ujf8_<13+bRH!%pziuRlicnr@8~; zfCZpCWn*yW{v9tYHG$*K^bCd`g~!Y3ZNX{$1WOpE>~%v%H5iOSdOYr%4MDNDQq-7J zzNnu9Vg{t$*KC1mwcC~%qZ5svssmzqD84)x6i*iy&OhLxMUbST=GGYBc(fK2nB4bs z&>IM)uM%oyB&UpK752A~eWO!LlU1=W#9~O9Fu*8_l+THaf(+@&iF-bQNoB{h-FtUY z`_x)^ESY{N<>ugh<4vla%GET!(dI#RcIn5}jTkGY@NT7`Tsbrs%>+W_gPtF2&BXgS zxRMBFA}ViRLA^rN5y_Rl{S>7r)S+shngYZubHJCfyGL7(L7cJp=W z;!O|U-H?9zC9&Il;_%dJq2~Op6O4OUG({pyEqgeU(cq@rF222SKxw4Pj^}+n zLQkaAw4N9&n3)X?JpwHK%Uz^q=Re zWiN<#+hklp?$Dy}SceZ5ZbP6b&da#|#gNQxt2-I4vc$fqnHMhil`llIaV_%`!x0vL zh*j8MO%HNJY=y*Ikty+9p?5M?Q3z8oy4^+R@^NAYSZ7&RVs4nZYAe^kChy^LdF*;f z_^!WJbPk@C9%GK88{5>0Quo+mYMuL%y?Td2AG7>^BKl(r3_Z5fB zpvA2F^KoQABXK}m_Gj*3$m8n)T1j#ggY^TBUd2m^w;{JdWpO(6ww8! zS^DH%oVad>%1gqwV1>z>+Zm+l*b%l|n=d~7^Nm5zDbn;VfCK3{otaoi6gqxyU4OT* z2|LdE&jE_oiUe!p1L>s zR?F8V5!NSMI&;UL!g+yvmr$h{ve_JbMib+|Zgm)aSD5JQcpat*$hvfcfcG4XDNiMu zj-}E4C2=51e!1hE8R4Ht{`g5~67Aw^-?Faqh76<^JU1P#^n;HtEX8-y^zLTTqMnzA zY~xfP@8v7#oc$T`GGobKMu$CX67%q z#U2m2R0{S8YF+-*IVZi%Es~a&gT~&i+u}72B+4x$>f}}eXD#oFs;h;1RkG|56$$)% zM2rs&)3Wiy04@H;9^eI6T)oKA1pdQC7w|Ni7u?|e=m_IE_6)9gw%jc_eoMo9<*|Yt zvCqn@k{9kODjS#@hJ|%AbG<{2n%6nkckrVB8WO@nKlA{L_Pkt9AMH(a3fGkiO4*S4M?J-Xtf2;Mx9AJKf=1+#(gox> zv!{Goz>dn!33{UMqrnu_k%FpUaH-MX1x8j-tVy!bn`y6pevrrJZkppqwWjoKD>veN-a{w`3NKiUoNP4eQkm>-TGld=7=C_~7QwxVTFDlbGl!bXi#x?Z;qP?G)P zsCS8f$w_LxUC?A@Uj#CjA1*d9aW9mVe*wX|3&pe-Np0mdTDeEq_eL9<0MrY^u?^I# zAZbr>5>Y4>d-{>pSa z+H+P+Lc~?5f<_pmr8BUT8{JCt;?3FHf(CN0tAChHC!`?bd0Nu*s2~CI6wI5@)H}uh z3ks~@d@5+%j_RE3_X5ldbX%%Bz>(qNsLClt-VTDk>E=Auc`bPh%PY%EEku=?2;?Fz{o@rY(n=qL_l zft$QX;n``mZp#l&F9!iGwPO>vqf6^%$rklTadQ~BYC9WhE7<|8{$A+JL)dorR zb=h#PJ@UI}9yJ>u_^?aOxZ+r|K|fSmpolfW1P6W0Qae+nW`|shmf09RJm!Kn0(us| z?%ZS&Vt1_V?V{rnW_)Rd9exTmb>X|%$G8UZHDE=?z*KP(bIaGBpOS@12;1MTe@3ZG4@X|8%6|gftos)p ztDUle_{csr%^bI(uFgLk)D%Cz&|f^Hp_*#B$rfZjffGUGH|*bd482c6x(yCI&)P0~ z5p~^;pyAfCQBLnC!nF8?T^u%yFd`ctR^1sl=;>e1p%MZx=4*Fo=W^UXD?LxgMh%y~ zJU>49n|S}aP?m2MheU zf*DuNdZ1xp*Hmo>UpLf`c^ID5Xcc%>F0Q1+KPq1%b^gb*STmGA*F+I3Rnx$8sBqcZ9@TO=$IQ%!ks@+j1ZBkJdxKlb-nAk2`* z#+Zm9j@W|K^a3O@nPxJY!BqyR1LNVY=C$FTlXu_n+@7|1{d%XQ&bXG(S3l1A4vj~F zEyqpyz`=$y7uabxXWEi%70X!cHQ*d^``N^idDSZCM9^hdJN)_I~g`wS5DT1@q-a(o`(^-=*cR4J^Drpmz|n*#9i*y#Dn; z_RYore)q4vFw>Yf9qcc_phJNzmL9-kgtkCvtzd~^M4=K_$njW zW(mCguiCJo3V7J1(Z31&FX_hyS-@jc$md_LzrXv#lI;8KHk;m?kBGmk2J5e?PEP;? z)PKnnOEPaR1}xj(VB!5p{n_3k%ka^T!40ilo0r6X8?eNGGXHOlqzij-SlY z$!WEs$kv}cCp`=yaR_L^o1IrUzic#gS)qo@?HRK$-x(r1N|nk-Dr^DBxGpk)H$QAu z(V6;TF}GhaW$#{%L!!Z$)kq7>E5mGuB($WrbwJDc^C=|@VaGXzKy&3GORr+~v`JVX`iXFXq9QYfT4oUdEavPpAE zKgvs+=(vj3Rr@g}b%&u#D_4VIK~l2t1(6wc{^a*y%e0{o7Wt~}tc8#m6^?r3cj=jZ za_a3oYfARFHlpSgBSv_l*EsR$1=OjUb+s>O^6{np)Ks7Lq)6ZvGuT>MH4n0BVh8)e zM?R-9+L@|GVzJyJ{x%P-V!tzg_@Eq-m!mn5OLwvDy2`28CcE?SNmD09wth7-Ok4{7 zv(ibgsYaRtIae|l`~BAP zGgFE=b&_h=QR)qqFyLPc$QmUCo6gL$uvu@{-2$T?eurgh^$I8gO4_&pe&Tt&BFend zkOOAjk0re}$fne$;O2&q_A}rH-IqeNe7fN1dKQ6-ij!RrxvYrJTHm;Pb~daGjo#HF zOQ^6-ed0%6!QyN_)eMV9Ag@3Ou7-MgInM%hq9mz4;o7+FhzK*AtgEa0ti@P=x0o-g zSd#jjf|sK3QS+)oR(kB_I68`SuFG*PC5o(YuV^2VR6Kx1u4^MWB`=kMrm9d5&l!rm zYTy86)G%0eL=&Cn*7Wi>DEgE|xLf&T*OT~3cjF_1`LX-u!S0`~&V{wzZdhgYm`%F`yj zv`Rez@JTv!jw#s0Id7n9yE|?NolB=){XS#vJ1br2-(y!3LiM^SZf4QHb<-E}^!vmQ zW*Gg)(JE`a?;0zGJA+dFXu6+8u*C^vV=gwK1VyZbl|PgWr&4QD$BCSpr^#wLZcF5F z5;A+cg(h=gaNQ36I-BnC(ivV6*g5C2h}w1AS%P1TcY$h*#+BcU46#9bKObOC zQ?4SQdI42KMrJeIocPK~;tHU`q_!KjFjLojoDWr&X{>j$|xoEukr4= z-0Db$l9QDgQkJDz*^bw=Fs(aYMz%)_v&rc)rN_SPC*J#WhQSWtyBkRR$wiAL`94R~ zr_2-^v(kfXqh{w-FYcPC@A*RUfth`d-6q3@R_q_61Bbhc?wq!kPlUC>m4n6YHqF^p zVWoj0=UY1g0Wh+lQKy)^7-H@;$_@X3%bIyfTQMWi9KmGvik>Pwk{A-w9^6$ejfr?p zc4-0dek3-W$$;h{RoZp^zW;DY@74*kQwPE%oGd8(t~a)?`mp@%LU?anUVMF4I93jO z44nQMegR+AS~s335L$^}Ejp78X{34B7Q#p;t=;;kXK!12h9-wYE^(u+zT4q0$1pAV zO*-q|NLrdGUnuW2G~zTY8ErH74DOql5s)n<{SVCT9ISP}ZQKlrb*ZMWE{Q~qj9rZ! zy4vX+RswAg=}IOEDGaX~x5L_MvRHz{zanZ2?gL%t~y4c*s*2oGew;V5Cz$kA7q3) zj|>A_LPk4Nv<45o@^V2rm&Ss4FZ0PPTlKyH9wMKka*nhC5n9e>iJbBi{ zRN21Tgq~L4Y7JN(PnVwF1Gh==R`FZh&RBm~*w^x80y4oEUiBNcerD^0L0r>96pBD-bM z5G^9HDgWSI2D!86F^?0T8Ot59r3$;wPCnXpUSDsI1lnCW$v1oVHZGw|U>sFsi1Z*H zP(TOZgnSfuR0*$uTTT!dEg5T)*9-OZV9l07X{BAeSDT4Sysd1UvG825xJ)jWop4UF z)z|y0j#Z~lQSdWvFt!>CU&Qfv9r!piJ_h_KFM>%58?2%)Pf&# z%r5$_W)ArFy+xfIN6eb%u(KxXaj0hcPT~6cnUy8gn?j<1BnInh?#(N|U>XT@!9e^=N~z5xXN)$z572-0qO+fX z%;L7Fx$gQDRx&99=To$G5%L}dsfd9@ zSzn|>-^DL+`BVA61A{`FnNN%nyilndcsK^NzCXvRW5-iJ808U6r6)}kz{F_e?HpR4KfuuE zY7NRhYQ;;FFp=Vf|52*zZelfCF9CL8mV$g2#68w&>f!A^3@W=mu3G)+|h}Ptc=VXHzd^)&&AQHT!2gjwI)s;g))gY7HwP+_3UO{ zU}*A?ffCZ*8~CH?>#&EL6eSl`VZ&#LdjLJV5zdLVq1{(2i%ZvPUn6tg>YQ3mX*%@S zUI(3m{I;2BJ9^h^!%Db?3eI{%e`Dl?(M@-tEec5P$g3@?xR#zO97Ee#$y$ZshZeAyGxP)fZ#QJH#fSR261Z zL$C;V!SE5$RgaCe(f_q@U;aokClnpUAV`=^b(Fws_v z+!P?atrk`LAH*!~@(!(iqAqV-wDLo9H>=dbS3qdA%JgHFU}W00+B^ z_L-@b+39$68d>;#TGdEamd!@I;lrFiU`j>VYgfkA6`pYRNXobd2YKBaM|9PF!RC3m zNV#BC+o4sMSxS+I?WbJ!8%+iHzbssMvM*DtDl^neESec|OS3lR_h$YgeTGR7*OJk( zvbrA?02A5FpWLVG&N#zyRlN)*=MLZU0RVN)Ak%5860;0;EGSA&%JfmNJ-peXg`>2vU#dp%dBu*H8T2qO zSJ~6}fyDP7u6}B!Pnxy7#x+d^B|{89Bglpbw0#J&&&<+BP1$OIVp6|CV=#?j;b+7n z0hBW$S(>gJJsX21UBf~TnGQsZ;~D_j=1Dx&qo$%z7X1Vh5A6Tq6%-85>1tLy!AXh}2TAq&U7)S?Mg`$;hDWYMnu3>UeJp2%BV#4idz z5MzTl%HyFC_{n{1v%4Ve4K&I|F}{0OzD{0PXbtq^`WESgLF7ykqn&%tlMrc}N+_lc zJsv+U?NxyacN_h7`N>m?kd9Njf4gl*p6N(y1$WYLmRuE%kS&i9l>`Gn0=C%UMWb ze|g13;(iVFH&cOOBGn!&Ek4`Kk0;@<3U~nE6vpzLo5$W<4#q)$qV(#*suq0K#vwjr z$hqH+xxA7l-P#ca3s6~A>UuxlB8#it&-et;99eQ10d`#Pb%_ZX@{?U7B)e|Y{S9Ws zZ*);)EBW!~cxl9A`OOofTY*)ZhQyL<*=u9!3}LOni9AIPuDpu-bKUMYu6*aon-FA? zaC3r@EFA<-^G!9-Dv%1gUw!V zzQUe@N{H~J>~&?qKDh1b;Vl^YBtABf3-w3gLAUPzFCP4(D}DdJ zE_}R-2iDC^9W7>S1v{TrBT0U(lJamokCDNZ_)1Jm4B@%#%ge&_^);ZT9W4L`Re?DzCdvzU|@a zVEQwLl9XRt$FS1QU&zb!o5a6w*Zj5N5d`y*jqhM?nxiXD@ou2>d-tS zPQj-~NOBt6>ayqai%Z!Xm!R?now@p1+`H{5u6NR*lsEOd#q$j%>5$~%xOW{L4;GYF zK`R<-6AE6fLIxM7;r4&4B)4*?l?TPIF0UP{w{^Z>rs5O4JQ{oV=yvA@C9p29$-g5` z&b@rPs~c~66^|wgCMOkV21VW~hH4kpl!!WlU1~0=N!U1bx|+(TOs{Z7%l(5HHZ4T+ z^O;$1o~W~7q!Te_cFg-7a{xTrpU^a$wL zXyLz0tGKKrkH28&UtVV&kCu0Cda#``p+yiFqjflvZdBq6Ur?j+rI#8U1SZqA<>Bk% zqo$+dp?aDa{Q!~~0iaQ>d#L&*tDx=GRRANA1R{TNw0TE^1 z05aW$p=*F^O~+Fp8NK%A*~dVoR?~Jy zq=v8+2-)Gp(fR!@7rq2U4Ox`_0OJw#GS;8ZT50u%{V`(pLdMTmuaV(?j?da;f%}oW zOF8eJ{Zg{dN2fB8`Vf1+v*#0gb%hIoRM>?GT1D|&&Skq3VEd`szs&YPMrzC{>1lX^ zYriOlJUTu4A{h5sDa@K;IZGHaPA*ifjc)|AQ%LG2n%helI(%PS`OdSShjF7HmiJ%> zkQEj$=rpX0b;GHl1YXr+(>%q`#r@3v0}xxx7M!3A=@%imMf5G6AFv5qDJpIxi}2x3 zNFv>s{`I=q?F}k}xx>4z(#K;Jwifwe*`w zK=SMAswa;-99v*&eB!LGNkNfQ=isLfWNlW|m@q}!9ig@@g_b0dgkcQ&rK-weG&+J3 z9gt(t*QHUNSUqUmM$1KLuYck45WZ63cO^8%Xy8?qTUFX@F2H7RVMv*0NkQDVFT3qK z{{SkIKmJuIKC$OBJ%*l(860e^v&-RAT`s%EzTUp6J1R$u%QcC-%u%I&+>CB*#aXpi z%4xm2OaoRtF5-9jp~~=WVx?&JO>jRqRV!{17{X1EFZ3;+#Hn-u^^WldAauR&!`7yY zt?g#x+v`(%sSH47&&39Q&c}ORh8LfQ)@g(r2c!r2kFqnve9lr#a-Ev+?WfwMf8bsG z)}O{AQfQHBAMTqEo#J}OHFK&foX%15sz$x8A`>&jN7-x5uI^Wlt~S(>ZDCy=!)4{J zf&+UDr29f~QRcYKBF`4@y$%M3n!TdrIR5qn@j1hI>m!2o)k68m!|(ko?Ugeg=nAkW zBTGpeqGGA_|C99!3w5Rk!0pM(G0tW3l(8s5d_(CtE1s2Ew0xhCxGA2jS$hREEs?Vf zkIrRs?sHdByJ8HTAmPF8`D#JM?P(;S%VWLKRRrcUB9|02KrqMAxMD1j$*i$ib!aLp zI=;-`SfVnRQccohB9kR$8%g2PO(a0eU*YW#5Q+JiXN1VJj-8GKshO9FVl?y@D4)p$o5UX(P|+P_kyekAY{eeMc`pE2D6dX zpLiEDf8lKML3I1bMSxzy0cG8zDZ2%KoOZ+>V;Za078T@}~V&0Kaa{ zE(lHx>LvvBgq4Ng=>BPFZ#UAems@qDml$|_xuS)yPOka)Eq`50?0b?X2jh&#^Z2PB zP|tLt%KA~1WM?lg#6Z8N`k3YU`vz5tT-r=sMtc0CM>7{4Yk|b*NwM0sjlX2<2LtYv zg~HS7UX!6lt^^+$D|oZdq;h$}C~2osaf&ROyQ%qz4w=i>RhRRwg%f8}_Th*5CCl-? zt<#OkSRq7f7p?O8V*d-t3d{3CECzEa#qM(E;T7!3C{$7@RkT=g=$7ov zBmav%&o^%fcqByxRZ``|+0>@zDVr!An38%?uAIl`9)Bmf26}&xT-7RS=8*=(f1W|s zwPXp%EWz2i$BFlg_x3?_GZC4(O36Jk1yEh4?y!(S?cdCZU1!*8`b1z}?E{GPeSa*e zBF2SkNhBd11sn?$oiI)^YSyx{a$iUa2+Jf zux0X?%Y(ke)u+^5*^E9h_+ov9?4_B+`VHt}`#c$QYju}V*pqPHRU7OfoTv^}gZ7Nb zI7eDGsv7luh|yF4x;i+}u&p>b+745=9Z_~Hqz|q$OiOF! z_1z1BD;8>Bh;+?R_*s8~K9S`6XazB+Yz9g%FFu#_A8YTvzCj>F5#tav|I%^O!^{(c zHrO*QN|ru8%YxmLY42w4QA z;K^%f{~-fqi^oFDF92oyD@@$xPG(SaFYFxhxM0-Lq_X>QDU-3#z$OjMSpIYCRW38s zXr2iY6vC2(IKQ4rcmEQiwM@kmxePf@Tex7NtIXAAEKUl2_Rq+N%dRPcD8$$r7_~vk zt}*#){&oe6LS9!navPeZyB+*Z_n%Rlf`9-_;FYt=68_uaxCfq`B>rNB&WHGKvzZGQ zSxg#TRo90si>rk&L-^@Ei-nuDLuQo~lzJVF)=6?Gy&goI9$)p{X6M62cCSMe_3b*u zz-oQ@bW8(=tkEk{1b^` zT}$RS==yA@pDR?{HuPmkAL3nFQJ4kaK=x(PZ_qVGipfWECY#N^2Nn!kDz;KtLLsZ} zMopajDHp{Gx2iE~HfB_~cl>|X_N>gKGZfeg&W->glCvq2u;4OltqYjx= zEWHU)8z1r8ZFMl1u}2sA7WV!APZf8rG`FK4TC9K_s?KCd@J-DQAO{m&@-uSi0<Mm8~rnRkE(To1BEs1{7sv!<7{eoZKWI<%{(m~dD<$b#*c zgCq9)#tB1ge`+L=D;>)z>=EPpi|k`!G2LWJ78QI>)jSo{0JEI{)FA2#SZ27LQFNfs zXtCL3!cC>-m@J^jzRNk<0%cE$E1?-=G0g&Kd;?WazK(9VbWHbPKfB@-tmvKoT zX=#{JcB;+H>ZUKJsVm4e<59kNJ^ubLe5&|=!l$$MA%DVC#tQ8#Y+rh6jdq%**4F$# z@uyKxKLRoem?l&3Go3ZT8CMoZ)qqAXsj^tg zC=1I1>VW#;BRX-fYtw-$X-Mp`56nLdG>@T#`MLQA!ry?b{Th;}5YGs(^|H45^pYEM z>i!1U{UIBTwED-Ueh7_OS*=L+$)cmV#WAL{`^~n6Q=F`v-AhHTdV^g&@RCtK^~y50 zwG1z=VC*Yg z<*OG6w6;AnIo>BeGm*(Fw3rml==RIPa^q&?`tOhmQE35EcRJ{Btx@9xKDs`<*dCX^u zWktzJJ4Rg-HhuBC1(9+r#~a)|1gA%boKHWXv`w| zCxpDf9})WnHlUDzXsc+H|K^?evo_*?_>;4+!d(+I;-|xGpI0 z8UKPO@7A_2YUpG=P9!x)R-w+>l(_QL)D-h-KMc9(#XsfW{)7k%3&U@O#235~pi2~^ zYfc>G=39UtZmw_I{c%JaIA~y=l!3kn>u%9R_MRK4G(^Oy?iQVu5 zkJ;T{d=#-;US2=^^$WN6>a#70fMUVo%X6F265qXxlaZf8&v?OMT8kUsiwyb&&q@QG z9!`1e?Vl6y-|iO2?7#fv0XlNH+$u4AZ_mZEaDVw+7L#|?NX-T&t7NjS;1S-8>qfWy zbv*?uC#nJu#+3BRF z?XJUi!7D>gG6LCP&Ba9A3&FL{X92xkEjSlh50mm`Bb1N$jWk&#@UOAsqlEIsFLEu= zqtEB4EHqA;c9!4k>}%sz8g)cipOYnc{PWm+ zw~CEj@pEnJD#HB}PHmM=#xg19Xt}o;8*;izI-hk>p6}ZZ(1}r+B)%;6((-kaUGLX0 z5JmO96E*vZXuc}A=&Ze-SK>XngX2>H6;-S%X=z{j(o=iR{*s*@D|8wFAAj60}+M~wV(+ewx z#>P{#j$|Zh0?zVohr#>wap26f&l6QGTd|pGdb6Yu@O-$Mthx`yb)ouxV}XI5fzKs| zcikRDB{>Vd#J&9hDy2F_U{i;*EL;L7iVY{Oedcu;7GZhmM1v-c*tI)+b@gCe$+?iI z*V+-sHpu)yJGbD$Ds(^3+3iKzw8*nBg_e}EqTIaRq26rmXrQIy!vK0v1i|f7!!iOr zKg@Rgg$&ir`~^qiZ2}r#<@%`s1HfZ3znD#h2y`)|o_HXdN-HzgdHk7?v0_)PqPnE7 zI#Dj-p_Ij*Hgb?`FlP;n7@H-RM}5fjPQ^NqZ*nvV)jDVxYh}~DHI!$hTC`r?C!=<* z)1p6<0lCXkrM`aO5DPmfOu8_MQxx?zwDdnL*%vx&|7Yw)gqAzZ~HBNyxbkGiUqU~jeY z9GN_R(Tg^`m^<$mG_$kA>iX%#q`gQ4CqFB1A`T^TAerioWZ1J5CC)V>Ml9->5Jv|@imWUK*Gfx^NPV)lyym8WtLe^e( zL8C5I+}64~W`-@ANtJ0`(332HXZv=(XM4eUhrB&@Xu1E%elh+X!R9pZsQuZ6o+}h;@jGof9c` z?tG3$Qv5c%tR93UzO~P$?IS9c9I=FMbV{}iIn-&$^-V+smU-UqTnyyF*DDzu9`j*# z=wp@=%uTR-s&^>C?8`7`eA-FFtW){om5yd?5XK_$cuTc*)?t(Y!Wtn$Hx(@R3(m!5 zsT){fF2R{lZ|?o2auZu;Gl}voK`KvbCDbI*`(=mHpxvo1B|S9F9`TevN$>V>=)IVK z@QRvknM^Ilwh9_&%p^IiCY5ECi6jw#qNLam04Od_K3nzY#$ueFcz8rVeb)h9?)__y zcVPFy9B=FlwkiEBpQ~dUV-C9u_rkTH8ig{sZhNmn#sRN#28Zbq_tW`l)XU}NJg{>m z{lFp!8qD#rtgsW+LWS_uz^zVxsl;x)W|FqgKa1TYQglf|?0NBP z0BzHGJKD+oRUe<{Q7+@x1j}pp2C5+O0-@B%XCEJPOvmBND$pqd>D+9B_}sgXZ@&s> zz5&oJXd>8nhY$Qbt=zbKJ$0OhN}|Yo1A; z(~*3?yZaSeu^J+%D(}mFGV8ACdu6~ZwTC%c6^|z^)^0e-m{H`Ot?4dMIeehpjy}7l zin(FOpW}7`eWnM5c?UIxzVu|q5=brrt`waZ;b9_k>6xkJKGq!%`C~{rsq2~^dPdAb zJ-31Z4@r;}iK)?3Rl^Fy)myHGefpN+Yk8O?i+Z*bn$i0r+aiM$yqZ++gdG%#vM59R zQTQ$pp7D@mFUN5Z@PUvf#+%9_wI_Y0cp=ks%UK;k>fih-Yv(D_}{sH@K-_Q4X z&wbuc=lt$D_qDc>FWm;|6IWDAni@IjXiU$H8PcpOvtU9$U}d=!;!ljLTlMz1euTdBr3BMdf|?bm6xqEjv?LP2m`Lx6XZ-BM=00 za4>;RHV<3SSj(WiHdTmK_iIR(_E_nMdaufhxXE}68q1VUC!K6a%_OAGR?>S25E1;A zoLu5BqtY#ptU{dvXkPa#(FB|i|LT6-^_5+lf_JP(SgqI)dnP(Y^a8M|ma0sdaU$(9`k$% zINR6>HLRvzQa&*dECRxhP8Ccl>)n>rTmF$7dQyJw2OMEQkJ z%gQ!#T?>gedSh!G;;cMXuTQrxnI}%`9W>@u`GM}?bnO^&6`^0bx2SNxS8ALznqD|q za0Z2HnWGcT4S?0AQr3GD!)W_@^FJ0aZ-JJX7nJ&R~i4_*{3@nn@P)NETeSA*;e@T5#|{kZN9 zaO>&=X;8T!-m{VAV+EYjD#46&Y&Cy#IzAgAHKZR*wgxdZBxOvcX|$*Ni9~L?jZ5vE zX*v^le0&Aez)M#SG4Jz_u6HWP2d0nEP&+36T?D8HUnr{Hn?d$kBeq*)Kb7uD$n|OB z7`8b3S@Vc@`d{L@vgaFxh`y}y%sima^YspMALypLk#|G!<}SG4)O5h$AkSvte$qf7|v9J^5TI z_7WuE^k8n`v@5dZ{K!dTjw`0?9xVt4(s8!orKl>Ys)nEZ?*BNvVUzE49eaNGePPXV z6_3w2NF5rYQC)(iHSd`vV$6gtW^PYA#dc~$ozIQ*l=I|96%mR{TbaY5n@)#2^VqP> zVJJ}5+yPJXtuqkWNAsgJ+m*fE@3YZA34M&!?`J2rt)wuqvwfC^c+5G;kmMDkN4fN)S|?XsnOf*@9q!x!Tz(oqc|^O~u2aX3 z=#e5e=X&mdw{Ts7;p2xhJI`_lgl9^b?G1Fp>)ME-&Cj?`?|I;jw0QxR(|4sx1$+J^EuSOyO;L_v)_T|m6Z}c5%SplNYIKF{ecoyJEiPAu zr}1PF4&$r(d)rGlIS+90CP|MJhy*QJZ=v<8IfKi&YsWq^c$rAPuV@)tlfmo|w~58? zIhUiUL)QQZM#98eyMcN~$l#)2aNM4=dLYH7qm`Bet-AqJR}md>ibr8D@E zM-u*le>c734X&D>Y4G5d1Sxj(Fz#+b1jaD4L}g700#rqNvBfOAw7oq%!4g0DjY~at zF8L=lO|v3$-%d@@@;*EE4gH$xni~0z=D`bjdn|c|ywa5g=s>IDtX95{GbyI}k~*I> z)!O@dHHH?!2G|7)qQ_v3Sv)bIFROCd_hWtZV_xMx#hz)(pT1ZB{QV#(C&O)+uT&l) z4CiF~t7PFr#wC7)g4s$b^}p@=%pT2|?vDyuZv{IM0^hW_b}J~~=~o;6od88k$eP?m z0p+Siw40Zf-4b)GHzp<$^a3o5#CqjggeSlG==_s|!P$mG`r$Q-ta4~#d4r&wYC|n%IoB{p%?_d#z<@9SbNt+#DZ|+aO4Hx6^H-jbLNzv~_%5ISf~vXr&#ZDD;>HiM!=N`>&Q}HVei8M3)d5igPsi^k<*Zz+_ c!n^ZJQQ>qwJqk$qe^4KTd!{-VE$4)P0TGDZ0RR91 diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index b4c7dea4dc..5c806ea7ec 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -140,56 +140,6 @@ This also translates into a very useful interactive documentation viewer in the --- -#### Django REST Swagger - -Marc Gibbons' [Django REST Swagger][django-rest-swagger] integrates REST framework with the [Swagger][swagger] API documentation tool. The package produces well presented API documentation, and includes interactive tools for testing API endpoints. - -Django REST Swagger supports REST framework versions 2.3 and above. - -Mark is also the author of the [REST Framework Docs][rest-framework-docs] package which offers clean, simple autogenerated documentation for your API but is deprecated and has moved to Django REST Swagger. - -This package is fully documented, well supported, and comes highly recommended. - -![Screenshot - Django REST Swagger][image-django-rest-swagger] - ---- - -### DRF AutoDocs - -Oleksander Mashianovs' [DRF Auto Docs][drfautodocs-repo] automated api renderer. - -Collects almost all the code you written into documentation effortlessly. - -Supports: - - * functional view docs - * tree-like structure - * Docstrings: - * markdown - * preserve space & newlines - * formatting with nice syntax - * Fields: - * choices rendering - * help_text (to specify SerializerMethodField output, etc) - * smart read_only/required rendering - * Endpoint properties: - * filter_backends - * authentication_classes - * permission_classes - * extra url params(GET params) - -![whole structure](http://joxi.ru/52aBGNI4k3oyA0.jpg) - ---- - -#### Apiary - -There are various other online tools and services for providing API documentation. One notable service is [Apiary][apiary]. With Apiary, you describe your API using a simple markdown-like syntax. The generated documentation includes API interaction, a mock server for testing & prototyping, and various other tools. - -![Screenshot - Apiary][image-apiary] - ---- - ## Self describing APIs The browsable API that REST framework provides makes it possible for your API to be entirely self describing. The documentation for each API endpoint can be provided simply by visiting the URL in your browser. @@ -253,22 +203,17 @@ In this approach, rather than documenting the available API endpoints up front, To implement a hypermedia API you'll need to decide on an appropriate media type for the API, and implement a custom renderer and parser for that media type. The [REST, Hypermedia & HATEOAS][hypermedia-docs] section of the documentation includes pointers to background reading, as well as links to various hypermedia formats. [cite]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven -[drf-yasg]: https://github.com/axnsan12/drf-yasg/ -[image-drf-yasg]: ../img/drf-yasg.png -[drfautodocs-repo]: https://github.com/iMakedonsky/drf-autodocs -[django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger -[swagger]: https://swagger.io/ -[open-api]: https://openapis.org/ -[rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs -[apiary]: https://apiary.io/ -[markdown]: https://daringfireball.net/projects/markdown/syntax + [hypermedia-docs]: rest-hypermedia-hateoas.md -[image-django-rest-swagger]: ../img/django-rest-swagger.png -[image-apiary]: ../img/apiary.png -[image-self-describing-api]: ../img/self-describing.png [metadata-docs]: ../api-guide/metadata/ - [schemas-examples]: ../api-guide/schemas/#examples -[swagger-ui]: https://swagger.io/tools/swagger-ui/ -[redoc]: https://github.com/Rebilly/ReDoc +[image-drf-yasg]: ../img/drf-yasg.png +[image-self-describing-api]: ../img/self-describing.png + +[drf-yasg]: https://github.com/axnsan12/drf-yasg/ +[markdown]: https://daringfireball.net/projects/markdown/syntax +[open-api]: https://openapis.org/ +[redoc]: https://github.com/Rebilly/ReDoc +[swagger]: https://swagger.io/ +[swagger-ui]: https://swagger.io/tools/swagger-ui/ From cad1b082602ce1367cae6a3a3668a64436fb4bde Mon Sep 17 00:00:00 2001 From: Chris Guo <41265033+chrisguox@users.noreply.github.com> Date: Mon, 18 Nov 2019 20:30:36 +0800 Subject: [PATCH 56/79] Fix override func style and regular usage (#7050) * style: add space and rm redundant parentheses * refactor: use super to replace inherit class * Prefer explicit tuple syntax --- rest_framework/relations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 3387768847..9bde79b199 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -47,12 +47,12 @@ class Hyperlink(str): in some contexts, or render as a plain URL in others. """ def __new__(cls, url, obj): - ret = str.__new__(cls, url) + ret = super().__new__(cls, url) ret.obj = obj return ret def __getnewargs__(self): - return(str(self), self.name,) + return (str(self), self.name) @property def name(self): From 6196e9c8cd4b8a785564310304870ab5ad2873be Mon Sep 17 00:00:00 2001 From: CloudCode Hungary <56896763+cloudcode-hungary@users.noreply.github.com> Date: Mon, 18 Nov 2019 13:31:32 +0100 Subject: [PATCH 57/79] add djangorestframework-features to third-party (#7052) --- docs/community/third-party-packages.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index c4b567a5f8..4d00432521 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -270,6 +270,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [django-rest-framework-condition][django-rest-framework-condition] - Decorators for managing HTTP cache headers for Django REST framework (ETag and Last-modified). * [django-rest-witchcraft][django-rest-witchcraft] - Provides DRF integration with SQLAlchemy with SQLAlchemy model serializers/viewsets and a bunch of other goodies * [djangorestframework-mvt][djangorestframework-mvt] - An extension for creating views that serve Postgres data as Map Box Vector Tiles. +* [djangorestframework-features][djangorestframework-features] - Advanced schema generation and more based on named features. [cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html [cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework @@ -350,3 +351,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [django-restql]: https://github.com/yezyilomo/django-restql [djangorestframework-mvt]: https://github.com/corteva/djangorestframework-mvt [django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian +[djangorestframework-features]: https://github.com/cloudcode-hungary/django-rest-framework-features/ From a73d3c309f0736f46dd46c99e15e1102af26dacb Mon Sep 17 00:00:00 2001 From: Hendrik <30193551+verhoek@users.noreply.github.com> Date: Mon, 18 Nov 2019 13:35:36 +0100 Subject: [PATCH 58/79] Elaborated on nested relationships (#7051) --- docs/api-guide/relations.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 14f197b21b..ef6efec5ea 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -245,7 +245,9 @@ This field is always read-only. # Nested relationships -Nested relationships can be expressed by using serializers as fields. +As opposed to previously discussed _references_ to another entity, the referred entity can instead also be embedded or _nested_ +in the representation of the object that refers to it. +Such nested relationships can be expressed by using serializers as fields. If the field is used to represent a to-many relationship, you should add the `many=True` flag to the serializer field. From adaf97a739dc2c29589b25052daac04d5d706c1b Mon Sep 17 00:00:00 2001 From: Thomas Loiret Date: Wed, 20 Nov 2019 14:09:49 +0100 Subject: [PATCH 59/79] Remove the old reference to `JSONResponse` --- docs/tutorial/2-requests-and-responses.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index e3d21e8644..b6433695ad 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -33,9 +33,7 @@ The wrappers also provide behaviour such as returning `405 Method Not Allowed` r ## Pulling it all together -Okay, let's go ahead and start using these new components to write a few views. - -We don't need our `JSONResponse` class in `views.py` any more, so go ahead and delete that. Once that's done we can start refactoring our views slightly. +Okay, let's go ahead and start using these new components to refactor our views slightly. from rest_framework import status from rest_framework.decorators import api_view From fe840a34ff79f6fd996219ff8325b5f07cc3f62b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Nov 2019 11:38:40 +0000 Subject: [PATCH 60/79] Escape hyperlink URLs on lookup (#7059) * Escape hyperlink URLs on lookup * Rename duplicate test --- rest_framework/relations.py | 2 +- tests/test_relations.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 9bde79b199..af4dd18048 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -344,7 +344,7 @@ def to_internal_value(self, data): if data.startswith(prefix): data = '/' + data[len(prefix):] - data = uri_to_iri(data) + data = uri_to_iri(parse.unquote(data)) try: match = resolve(data) diff --git a/tests/test_relations.py b/tests/test_relations.py index c892934157..86ed623aeb 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -153,6 +153,7 @@ def setUp(self): self.queryset = MockQueryset([ MockObject(pk=1, name='foobar'), MockObject(pk=2, name='bazABCqux'), + MockObject(pk=2, name='bazABC qux'), ]) self.field = serializers.HyperlinkedRelatedField( view_name='example', @@ -191,6 +192,10 @@ def test_hyperlinked_related_lookup_url_encoded_exists(self): instance = self.field.to_internal_value('http://example.org/example/baz%41%42%43qux/') assert instance is self.queryset.items[1] + def test_hyperlinked_related_lookup_url_space_encoded_exists(self): + instance = self.field.to_internal_value('http://example.org/example/bazABC%20qux/') + assert instance is self.queryset.items[2] + def test_hyperlinked_related_lookup_does_not_exist(self): with pytest.raises(serializers.ValidationError) as excinfo: self.field.to_internal_value('http://example.org/example/doesnotexist/') From 7fbbfe2c60c314e79bf2179c76e4357f48045a2b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Nov 2019 11:55:53 +0000 Subject: [PATCH 61/79] Django 3 compat (#7058) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * First pass at Django 3.0 compat * Drop Guardian for 1.11 tests, since we're installing an incompatible version * Fix ROOT_URLCONF override in test case * Fix typo Co-Authored-By: Rémy HUBSCHER * Linting --- .travis.yml | 3 +++ requirements/requirements-optionals.txt | 2 +- tests/conftest.py | 29 ++++++++++++++----------- tests/test_relations.py | 7 ++++-- tox.ini | 3 +++ 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index f89e775310..7266df2d5e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,13 +14,16 @@ matrix: - { python: "3.6", env: DJANGO=2.0 } - { python: "3.6", env: DJANGO=2.1 } - { python: "3.6", env: DJANGO=2.2 } + - { python: "3.6", env: DJANGO=3.0 } - { python: "3.6", env: DJANGO=master } - { python: "3.7", env: DJANGO=2.0 } - { python: "3.7", env: DJANGO=2.1 } - { python: "3.7", env: DJANGO=2.2 } + - { python: "3.7", env: DJANGO=3.0 } - { python: "3.7", env: DJANGO=master } + - { python: "3.8", env: DJANGO=3.0 } - { python: "3.8", env: DJANGO=master } - { python: "3.8", env: TOXENV=base } diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index a33248d100..14957a5313 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -2,7 +2,7 @@ psycopg2-binary>=2.8.2, <2.9 markdown==3.1.1 pygments==2.4.2 -django-guardian==1.5.0 +django-guardian==2.1.0 django-filter>=2.2.0, <2.3 coreapi==2.3.1 coreschema==0.0.4 diff --git a/tests/conftest.py b/tests/conftest.py index ac29e4a429..d28edeb8a3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -67,19 +67,22 @@ def pytest_configure(config): ) # guardian is optional - try: - import guardian # NOQA - except ImportError: - pass - else: - settings.ANONYMOUS_USER_ID = -1 - settings.AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'guardian.backends.ObjectPermissionBackend', - ) - settings.INSTALLED_APPS += ( - 'guardian', - ) + # Note that for the test cases we're installing a version of django-guardian + # that's only compatible with Django 2.0+. + if django.VERSION >= (2, 0, 0): + try: + import guardian # NOQA + except ImportError: + pass + else: + settings.ANONYMOUS_USER_ID = -1 + settings.AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + 'guardian.backends.ObjectPermissionBackend', + ) + settings.INSTALLED_APPS += ( + 'guardian', + ) if config.getoption('--no-pkgroot'): sys.path.pop(0) diff --git a/tests/test_relations.py b/tests/test_relations.py index 86ed623aeb..9f05e3b314 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -145,9 +145,12 @@ def test_pk_representation(self): assert representation == self.instance.pk.int -@override_settings(ROOT_URLCONF=[ +urlpatterns = [ url(r'^example/(?P.+)/$', lambda: None, name='example'), -]) +] + + +@override_settings(ROOT_URLCONF='tests.test_relations') class TestHyperlinkedRelatedField(APISimpleTestCase): def setUp(self): self.queryset = MockQueryset([ diff --git a/tox.ini b/tox.ini index 587c469b1b..1153eae29e 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,7 @@ envlist = {py35,py36,py37}-django20, {py35,py36,py37}-django21 {py35,py36,py37}-django22 + {py36,py37,py38}-django30, {py36,py37,py38}-djangomaster, base,dist,lint,docs, @@ -13,6 +14,7 @@ DJANGO = 2.0: django20 2.1: django21 2.2: django22 + 3.0: django30 master: djangomaster [testenv] @@ -26,6 +28,7 @@ deps = django20: Django>=2.0,<2.1 django21: Django>=2.1,<2.2 django22: Django>=2.2,<3.0 + django30: Django==3.0rc1 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 8001087e9e335140b8063a23916d9c05b615acd4 Mon Sep 17 00:00:00 2001 From: Oskar Persson Date: Thu, 21 Nov 2019 15:59:50 +0100 Subject: [PATCH 62/79] Fix typo in unsupported version error message (#7060) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c9d6443d5b..24749992b6 100755 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ your version of Python. If you can't upgrade your pip (or Python), request an older version of Django REST Framework: - $ python -m pip install "django<3.10" + $ python -m pip install "djangorestframework<3.10" """.format(*(REQUIRED_PYTHON + CURRENT_PYTHON))) sys.exit(1) From 9325c3f6544c76173891786529ed5f4bc2be5876 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Tue, 3 Dec 2019 17:13:44 +0600 Subject: [PATCH 63/79] dj 3.0 (#7070) --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 1153eae29e..9b80691745 100644 --- a/tox.ini +++ b/tox.ini @@ -28,7 +28,7 @@ deps = django20: Django>=2.0,<2.1 django21: Django>=2.1,<2.2 django22: Django>=2.2,<3.0 - django30: Django==3.0rc1 + django30: Django>=3.0,<3.1 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 070cff5a0356f6ead6380062f186a2582e460601 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 3 Dec 2019 11:16:27 +0000 Subject: [PATCH 64/79] Drop `set_context()` (#7062) * Do not persist the context in validators Fixes encode/django-rest-framework#5760 * Drop set_context() in favour of 'requires_context = True' --- docs/api-guide/fields.md | 14 +++- docs/api-guide/validators.md | 16 +++-- rest_framework/fields.py | 61 +++++++++++++---- rest_framework/validators.py | 126 ++++++++++++++++------------------- tests/test_validators.py | 7 +- 5 files changed, 131 insertions(+), 93 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 29cb5aec94..e964458f9b 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -50,7 +50,19 @@ If set, this gives the default value that will be used for the field if no input The `default` is not applied during partial update operations. In the partial update case only fields that are provided in the incoming data will have a validated value returned. -May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context). +May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `requires_context = True` attribute, then the serializer field will be passed as an argument. + +For example: + + class CurrentUserDefault: + """ + May be applied as a `default=...` value on a serializer field. + Returns the current user. + """ + requires_context = True + + def __call__(self, serializer_field): + return serializer_field.context['request'].user When serializing the instance, default will be used if the object attribute or dictionary key is not present in the instance. diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index 49685838a2..009cd2468d 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -291,13 +291,17 @@ To write a class-based validator, use the `__call__` method. Class-based validat message = 'This field must be a multiple of %d.' % self.base raise serializers.ValidationError(message) -#### Using `set_context()` +#### Accessing the context -In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a `set_context` method on a class-based validator. +In some advanced cases you might want a validator to be passed the serializer +field it is being used with as additional context. You can do so by setting +a `requires_context = True` attribute on the validator. The `__call__` method +will then be called with the `serializer_field` +or `serializer` as an additional argument. - def set_context(self, serializer_field): - # Determine if this is an update or a create operation. - # In `__call__` we can then use that information to modify the validation behavior. - self.is_update = serializer_field.parent.instance is not None + requires_context = True + + def __call__(self, value, serializer_field): + ... [cite]: https://docs.djangoproject.com/en/stable/ref/validators/ diff --git a/rest_framework/fields.py b/rest_framework/fields.py index ea8f47b2d9..9507914e84 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -5,6 +5,7 @@ import inspect import re import uuid +import warnings from collections import OrderedDict from collections.abc import Mapping @@ -249,19 +250,30 @@ class CreateOnlyDefault: for create operations, but that do not return any value for update operations. """ + requires_context = True + def __init__(self, default): self.default = default - def set_context(self, serializer_field): - self.is_update = serializer_field.parent.instance is not None - if callable(self.default) and hasattr(self.default, 'set_context') and not self.is_update: - self.default.set_context(serializer_field) - - def __call__(self): - if self.is_update: + def __call__(self, serializer_field): + is_update = serializer_field.parent.instance is not None + if is_update: raise SkipField() if callable(self.default): - return self.default() + if hasattr(self.default, 'set_context'): + warnings.warn( + "Method `set_context` on defaults is deprecated and will " + "no longer be called starting with 3.12. Instead set " + "`requires_context = True` on the class, and accept the " + "context as an additional argument.", + DeprecationWarning, stacklevel=2 + ) + self.default.set_context(self) + + if getattr(self.default, 'requires_context', False): + return self.default(serializer_field) + else: + return self.default() return self.default def __repr__(self): @@ -269,11 +281,10 @@ def __repr__(self): class CurrentUserDefault: - def set_context(self, serializer_field): - self.user = serializer_field.context['request'].user + requires_context = True - def __call__(self): - return self.user + def __call__(self, serializer_field): + return serializer_field.context['request'].user def __repr__(self): return '%s()' % self.__class__.__name__ @@ -489,8 +500,20 @@ def get_default(self): raise SkipField() if callable(self.default): if hasattr(self.default, 'set_context'): + warnings.warn( + "Method `set_context` on defaults is deprecated and will " + "no longer be called starting with 3.12. Instead set " + "`requires_context = True` on the class, and accept the " + "context as an additional argument.", + DeprecationWarning, stacklevel=2 + ) self.default.set_context(self) - return self.default() + + if getattr(self.default, 'requires_context', False): + return self.default(self) + else: + return self.default() + return self.default def validate_empty_values(self, data): @@ -551,10 +574,20 @@ def run_validators(self, value): errors = [] for validator in self.validators: if hasattr(validator, 'set_context'): + warnings.warn( + "Method `set_context` on validators is deprecated and will " + "no longer be called starting with 3.12. Instead set " + "`requires_context = True` on the class, and accept the " + "context as an additional argument.", + DeprecationWarning, stacklevel=2 + ) validator.set_context(self) try: - validator(value) + if getattr(validator, 'requires_context', False): + validator(value, self) + else: + validator(value) except ValidationError as exc: # If the validation error contains a mapping of fields to # errors then simply raise it immediately rather than diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 1cbe31b5ea..2907312a9b 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -37,6 +37,7 @@ class UniqueValidator: Should be applied to an individual field on the serializer. """ message = _('This field must be unique.') + requires_context = True def __init__(self, queryset, message=None, lookup='exact'): self.queryset = queryset @@ -44,37 +45,32 @@ def __init__(self, queryset, message=None, lookup='exact'): self.message = message or self.message self.lookup = lookup - def set_context(self, serializer_field): - """ - This hook is called by the serializer instance, - prior to the validation call being made. - """ - # Determine the underlying model field name. This may not be the - # same as the serializer field name if `source=<>` is set. - self.field_name = serializer_field.source_attrs[-1] - # Determine the existing instance, if this is an update operation. - self.instance = getattr(serializer_field.parent, 'instance', None) - - def filter_queryset(self, value, queryset): + def filter_queryset(self, value, queryset, field_name): """ Filter the queryset to all instances matching the given attribute. """ - filter_kwargs = {'%s__%s' % (self.field_name, self.lookup): value} + filter_kwargs = {'%s__%s' % (field_name, self.lookup): value} return qs_filter(queryset, **filter_kwargs) - def exclude_current_instance(self, queryset): + def exclude_current_instance(self, queryset, instance): """ If an instance is being updated, then do not include that instance itself as a uniqueness conflict. """ - if self.instance is not None: - return queryset.exclude(pk=self.instance.pk) + if instance is not None: + return queryset.exclude(pk=instance.pk) return queryset - def __call__(self, value): + def __call__(self, value, serializer_field): + # Determine the underlying model field name. This may not be the + # same as the serializer field name if `source=<>` is set. + field_name = serializer_field.source_attrs[-1] + # Determine the existing instance, if this is an update operation. + instance = getattr(serializer_field.parent, 'instance', None) + queryset = self.queryset - queryset = self.filter_queryset(value, queryset) - queryset = self.exclude_current_instance(queryset) + queryset = self.filter_queryset(value, queryset, field_name) + queryset = self.exclude_current_instance(queryset, instance) if qs_exists(queryset): raise ValidationError(self.message, code='unique') @@ -93,6 +89,7 @@ class UniqueTogetherValidator: """ message = _('The fields {field_names} must make a unique set.') missing_message = _('This field is required.') + requires_context = True def __init__(self, queryset, fields, message=None): self.queryset = queryset @@ -100,20 +97,12 @@ def __init__(self, queryset, fields, message=None): self.serializer_field = None self.message = message or self.message - def set_context(self, serializer): - """ - This hook is called by the serializer instance, - prior to the validation call being made. - """ - # Determine the existing instance, if this is an update operation. - self.instance = getattr(serializer, 'instance', None) - - def enforce_required_fields(self, attrs): + def enforce_required_fields(self, attrs, instance): """ The `UniqueTogetherValidator` always forces an implied 'required' state on the fields it applies to. """ - if self.instance is not None: + if instance is not None: return missing_items = { @@ -124,16 +113,16 @@ def enforce_required_fields(self, attrs): if missing_items: raise ValidationError(missing_items, code='required') - def filter_queryset(self, attrs, queryset): + def filter_queryset(self, attrs, queryset, instance): """ Filter the queryset to all instances matching the given attributes. """ # If this is an update, then any unprovided field should # have it's value set based on the existing instance attribute. - if self.instance is not None: + if instance is not None: for field_name in self.fields: if field_name not in attrs: - attrs[field_name] = getattr(self.instance, field_name) + attrs[field_name] = getattr(instance, field_name) # Determine the filter keyword arguments and filter the queryset. filter_kwargs = { @@ -142,20 +131,23 @@ def filter_queryset(self, attrs, queryset): } return qs_filter(queryset, **filter_kwargs) - def exclude_current_instance(self, attrs, queryset): + def exclude_current_instance(self, attrs, queryset, instance): """ If an instance is being updated, then do not include that instance itself as a uniqueness conflict. """ - if self.instance is not None: - return queryset.exclude(pk=self.instance.pk) + if instance is not None: + return queryset.exclude(pk=instance.pk) return queryset - def __call__(self, attrs): - self.enforce_required_fields(attrs) + def __call__(self, attrs, serializer): + # Determine the existing instance, if this is an update operation. + instance = getattr(serializer, 'instance', None) + + self.enforce_required_fields(attrs, instance) queryset = self.queryset - queryset = self.filter_queryset(attrs, queryset) - queryset = self.exclude_current_instance(attrs, queryset) + queryset = self.filter_queryset(attrs, queryset, instance) + queryset = self.exclude_current_instance(attrs, queryset, instance) # Ignore validation if any field is None checked_values = [ @@ -177,6 +169,7 @@ def __repr__(self): class BaseUniqueForValidator: message = None missing_message = _('This field is required.') + requires_context = True def __init__(self, queryset, field, date_field, message=None): self.queryset = queryset @@ -184,18 +177,6 @@ def __init__(self, queryset, field, date_field, message=None): self.date_field = date_field self.message = message or self.message - def set_context(self, serializer): - """ - This hook is called by the serializer instance, - prior to the validation call being made. - """ - # Determine the underlying model field names. These may not be the - # same as the serializer field names if `source=<>` is set. - self.field_name = serializer.fields[self.field].source_attrs[-1] - self.date_field_name = serializer.fields[self.date_field].source_attrs[-1] - # Determine the existing instance, if this is an update operation. - self.instance = getattr(serializer, 'instance', None) - def enforce_required_fields(self, attrs): """ The `UniqueForValidator` classes always force an implied @@ -209,23 +190,30 @@ def enforce_required_fields(self, attrs): if missing_items: raise ValidationError(missing_items, code='required') - def filter_queryset(self, attrs, queryset): + def filter_queryset(self, attrs, queryset, field_name, date_field_name): raise NotImplementedError('`filter_queryset` must be implemented.') - def exclude_current_instance(self, attrs, queryset): + def exclude_current_instance(self, attrs, queryset, instance): """ If an instance is being updated, then do not include that instance itself as a uniqueness conflict. """ - if self.instance is not None: - return queryset.exclude(pk=self.instance.pk) + if instance is not None: + return queryset.exclude(pk=instance.pk) return queryset - def __call__(self, attrs): + def __call__(self, attrs, serializer): + # Determine the underlying model field names. These may not be the + # same as the serializer field names if `source=<>` is set. + field_name = serializer.fields[self.field].source_attrs[-1] + date_field_name = serializer.fields[self.date_field].source_attrs[-1] + # Determine the existing instance, if this is an update operation. + instance = getattr(serializer, 'instance', None) + self.enforce_required_fields(attrs) queryset = self.queryset - queryset = self.filter_queryset(attrs, queryset) - queryset = self.exclude_current_instance(attrs, queryset) + queryset = self.filter_queryset(attrs, queryset, field_name, date_field_name) + queryset = self.exclude_current_instance(attrs, queryset, instance) if qs_exists(queryset): message = self.message.format(date_field=self.date_field) raise ValidationError({ @@ -244,39 +232,39 @@ def __repr__(self): class UniqueForDateValidator(BaseUniqueForValidator): message = _('This field must be unique for the "{date_field}" date.') - def filter_queryset(self, attrs, queryset): + def filter_queryset(self, attrs, queryset, field_name, date_field_name): value = attrs[self.field] date = attrs[self.date_field] filter_kwargs = {} - filter_kwargs[self.field_name] = value - filter_kwargs['%s__day' % self.date_field_name] = date.day - filter_kwargs['%s__month' % self.date_field_name] = date.month - filter_kwargs['%s__year' % self.date_field_name] = date.year + filter_kwargs[field_name] = value + filter_kwargs['%s__day' % date_field_name] = date.day + filter_kwargs['%s__month' % date_field_name] = date.month + filter_kwargs['%s__year' % date_field_name] = date.year return qs_filter(queryset, **filter_kwargs) class UniqueForMonthValidator(BaseUniqueForValidator): message = _('This field must be unique for the "{date_field}" month.') - def filter_queryset(self, attrs, queryset): + def filter_queryset(self, attrs, queryset, field_name, date_field_name): value = attrs[self.field] date = attrs[self.date_field] filter_kwargs = {} - filter_kwargs[self.field_name] = value - filter_kwargs['%s__month' % self.date_field_name] = date.month + filter_kwargs[field_name] = value + filter_kwargs['%s__month' % date_field_name] = date.month return qs_filter(queryset, **filter_kwargs) class UniqueForYearValidator(BaseUniqueForValidator): message = _('This field must be unique for the "{date_field}" year.') - def filter_queryset(self, attrs, queryset): + def filter_queryset(self, attrs, queryset, field_name, date_field_name): value = attrs[self.field] date = attrs[self.date_field] filter_kwargs = {} - filter_kwargs[self.field_name] = value - filter_kwargs['%s__year' % self.date_field_name] = date.year + filter_kwargs[field_name] = value + filter_kwargs['%s__year' % date_field_name] = date.year return qs_filter(queryset, **filter_kwargs) diff --git a/tests/test_validators.py b/tests/test_validators.py index fe31ba2357..bb29a4305b 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -361,8 +361,7 @@ def filter(self, **kwargs): queryset = MockQueryset() validator = UniqueTogetherValidator(queryset, fields=('race_name', 'position')) - validator.instance = self.instance - validator.filter_queryset(attrs=data, queryset=queryset) + validator.filter_queryset(attrs=data, queryset=queryset, instance=self.instance) assert queryset.called_with == {'race_name': 'bar', 'position': 1} @@ -586,4 +585,6 @@ def test_validator_raises_error_when_abstract_method_called(self): validator = BaseUniqueForValidator(queryset=object(), field='foo', date_field='bar') with pytest.raises(NotImplementedError): - validator.filter_queryset(attrs=None, queryset=None) + validator.filter_queryset( + attrs=None, queryset=None, field_name='', date_field_name='' + ) From dff9759555eefef67c552f175d04bb7d8381e919 Mon Sep 17 00:00:00 2001 From: Kye Russell Date: Wed, 4 Dec 2019 17:29:01 +0800 Subject: [PATCH 65/79] Removed Eric S. Raymond quote from the release notes (#7073) --- docs/community/release-notes.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index 283dbae671..4be05d56b1 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -1,9 +1,5 @@ # Release Notes -> Release Early, Release Often -> -> — Eric S. Raymond, [The Cathedral and the Bazaar][cite]. - ## Versioning Minor version numbers (0.0.x) are used for changes that are API compatible. You should be able to upgrade between minor point releases without any other code changes. From 4d9f9eb192c5c1ffe4fa9210b90b9adbb00c3fdd Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Wed, 4 Dec 2019 21:24:49 +0100 Subject: [PATCH 66/79] Changed default widget for TextField with choices to select (#6892) --- rest_framework/utils/field_mapping.py | 3 ++- tests/test_model_serializer.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index b90c3eeadb..a25880d0f6 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -91,7 +91,8 @@ def get_field_kwargs(field_name, model_field): if isinstance(model_field, models.SlugField): kwargs['allow_unicode'] = model_field.allow_unicode - if isinstance(model_field, models.TextField) or (postgres_fields and isinstance(model_field, postgres_fields.JSONField)): + if isinstance(model_field, models.TextField) and not model_field.choices or \ + (postgres_fields and isinstance(model_field, postgres_fields.JSONField)): kwargs['style'] = {'base_template': 'textarea.html'} if isinstance(model_field, models.AutoField) or not model_field.editable: diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 21ec82347e..fbb562792d 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -89,6 +89,7 @@ class FieldOptionsModel(models.Model): default_field = models.IntegerField(default=0) descriptive_field = models.IntegerField(help_text='Some help text', verbose_name='A label') choices_field = models.CharField(max_length=100, choices=COLOR_CHOICES) + text_choices_field = models.TextField(choices=COLOR_CHOICES) class ChoicesModel(models.Model): @@ -211,6 +212,7 @@ class Meta: default_field = IntegerField(required=False) descriptive_field = IntegerField(help_text='Some help text', label='A label') choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green'))) + text_choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green'))) """) self.assertEqual(repr(TestSerializer()), expected) From 95d4843abeecea96754a147f4f2cca33e620ad09 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 4 Dec 2019 14:14:43 -0800 Subject: [PATCH 67/79] Fix Django 3.0 deprecations (#7074) --- rest_framework/fields.py | 4 ++-- rest_framework/relations.py | 4 ++-- rest_framework/schemas/inspectors.py | 4 ++-- rest_framework/schemas/utils.py | 2 +- rest_framework/views.py | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 9507914e84..8c80d6bd5a 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -23,7 +23,7 @@ parse_date, parse_datetime, parse_duration, parse_time ) from django.utils.duration import duration_string -from django.utils.encoding import is_protected_type, smart_text +from django.utils.encoding import is_protected_type, smart_str from django.utils.formats import localize_input, sanitize_separators from django.utils.ipv6 import clean_ipv6_address from django.utils.timezone import utc @@ -1082,7 +1082,7 @@ def to_internal_value(self, data): instance. """ - data = smart_text(data).strip() + data = smart_str(data).strip() if self.localize: data = sanitize_separators(data) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index af4dd18048..3a2a8fb4b5 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -6,7 +6,7 @@ from django.db.models import Manager from django.db.models.query import QuerySet 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_str, uri_to_iri from django.utils.translation import gettext_lazy as _ from rest_framework.fields import ( @@ -452,7 +452,7 @@ def to_internal_value(self, data): try: return self.get_queryset().get(**{self.slug_field: data}) except ObjectDoesNotExist: - self.fail('does_not_exist', slug_name=self.slug_field, value=smart_text(data)) + self.fail('does_not_exist', slug_name=self.slug_field, value=smart_str(data)) except (TypeError, ValueError): self.fail('invalid') diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 3b7e7f9637..027472db14 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -6,7 +6,7 @@ import re from weakref import WeakKeyDictionary -from django.utils.encoding import smart_text +from django.utils.encoding import smart_str from rest_framework.settings import api_settings from rest_framework.utils import formatting @@ -82,7 +82,7 @@ def get_description(self, path, method): method_docstring = getattr(view, method_name, None).__doc__ if method_docstring: # An explicit docstring on the method or action. - return self._get_description_section(view, method.lower(), formatting.dedent(smart_text(method_docstring))) + return self._get_description_section(view, method.lower(), formatting.dedent(smart_str(method_docstring))) else: return self._get_description_section(view, getattr(view, 'action', method.lower()), view.get_view_description()) diff --git a/rest_framework/schemas/utils.py b/rest_framework/schemas/utils.py index 6724eb4289..60ed698294 100644 --- a/rest_framework/schemas/utils.py +++ b/rest_framework/schemas/utils.py @@ -4,7 +4,7 @@ See schemas.__init__.py for package overview. """ from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from rest_framework.mixins import RetrieveModelMixin diff --git a/rest_framework/views.py b/rest_framework/views.py index bec10560a9..69db053d64 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -7,7 +7,7 @@ from django.http import Http404 from django.http.response import HttpResponseBase from django.utils.cache import cc_delim_re, patch_vary_headers -from django.utils.encoding import smart_text +from django.utils.encoding import smart_str from django.views.decorators.csrf import csrf_exempt from django.views.generic import View @@ -56,7 +56,7 @@ def get_view_description(view, html=False): if description is None: description = view.__class__.__doc__ or '' - description = formatting.dedent(smart_text(description)) + description = formatting.dedent(smart_str(description)) if html: return formatting.markup_description(description) return description From 90eaf51839e2bfc3ca897d1f87028ca3303aa097 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 4 Dec 2019 16:18:38 -0800 Subject: [PATCH 68/79] Update framework deprecation warnings (#7075) - Bump version numbers for deprecation warnings - Drop deprecated features --- rest_framework/__init__.py | 4 +-- rest_framework/routers.py | 24 ++----------- tests/test_routers.py | 73 +------------------------------------- 3 files changed, 6 insertions(+), 95 deletions(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 2b96e7336e..fceee6817d 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -25,9 +25,9 @@ default_app_config = 'rest_framework.apps.RestFrameworkConfig' -class RemovedInDRF311Warning(DeprecationWarning): +class RemovedInDRF312Warning(DeprecationWarning): pass -class RemovedInDRF312Warning(PendingDeprecationWarning): +class RemovedInDRF313Warning(PendingDeprecationWarning): pass diff --git a/rest_framework/routers.py b/rest_framework/routers.py index d8e19a2d7b..657ad67bce 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -14,15 +14,13 @@ urlpatterns = router.urls """ import itertools -import warnings from collections import OrderedDict, namedtuple from django.conf.urls import url from django.core.exceptions import ImproperlyConfigured from django.urls import NoReverseMatch -from django.utils.deprecation import RenameMethodsBase -from rest_framework import RemovedInDRF311Warning, views +from rest_framework import views from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.schemas import SchemaGenerator @@ -48,27 +46,11 @@ def flatten(list_of_lists): return itertools.chain(*list_of_lists) -class RenameRouterMethods(RenameMethodsBase): - renamed_methods = ( - ('get_default_base_name', 'get_default_basename', RemovedInDRF311Warning), - ) - - -class BaseRouter(metaclass=RenameRouterMethods): +class BaseRouter: def __init__(self): self.registry = [] - def register(self, prefix, viewset, basename=None, base_name=None): - if base_name is not None: - msg = "The `base_name` argument is pending deprecation in favor of `basename`." - warnings.warn(msg, RemovedInDRF311Warning, 2) - - assert not (basename and base_name), ( - "Do not provide both the `basename` and `base_name` arguments.") - - if basename is None: - basename = base_name - + def register(self, prefix, viewset, basename=None): if basename is None: basename = self.get_default_basename(viewset) self.registry.append((prefix, viewset, basename)) diff --git a/tests/test_routers.py b/tests/test_routers.py index 0f428e2a56..ff927ff339 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -1,4 +1,3 @@ -import warnings from collections import namedtuple import pytest @@ -8,9 +7,7 @@ from django.test import TestCase, override_settings from django.urls import resolve, reverse -from rest_framework import ( - RemovedInDRF311Warning, permissions, serializers, viewsets -) +from rest_framework import permissions, serializers, viewsets from rest_framework.compat import get_regex_pattern from rest_framework.decorators import action from rest_framework.response import Response @@ -488,71 +485,3 @@ def test_basename(self): initkwargs = match.func.initkwargs assert initkwargs['basename'] == 'routertestmodel' - - -class TestBaseNameRename(TestCase): - - def test_base_name_and_basename_assertion(self): - router = SimpleRouter() - - msg = "Do not provide both the `basename` and `base_name` arguments." - with warnings.catch_warnings(record=True) as w, \ - self.assertRaisesMessage(AssertionError, msg): - warnings.simplefilter('always') - router.register('mock', MockViewSet, 'mock', base_name='mock') - - msg = "The `base_name` argument is pending deprecation in favor of `basename`." - assert len(w) == 1 - assert str(w[0].message) == msg - - def test_base_name_argument_deprecation(self): - router = SimpleRouter() - - with pytest.warns(RemovedInDRF311Warning) as w: - warnings.simplefilter('always') - router.register('mock', MockViewSet, base_name='mock') - - msg = "The `base_name` argument is pending deprecation in favor of `basename`." - assert len(w) == 1 - assert str(w[0].message) == msg - assert router.registry == [ - ('mock', MockViewSet, 'mock'), - ] - - def test_basename_argument_no_warnings(self): - router = SimpleRouter() - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - router.register('mock', MockViewSet, basename='mock') - - assert len(w) == 0 - assert router.registry == [ - ('mock', MockViewSet, 'mock'), - ] - - def test_get_default_base_name_deprecation(self): - msg = "`CustomRouter.get_default_base_name` method should be renamed `get_default_basename`." - - # Class definition should raise a warning - with pytest.warns(RemovedInDRF311Warning) as w: - warnings.simplefilter('always') - - class CustomRouter(SimpleRouter): - def get_default_base_name(self, viewset): - return 'foo' - - assert len(w) == 1 - assert str(w[0].message) == msg - - # Deprecated method implementation should still be called - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - - router = CustomRouter() - router.register('mock', MockViewSet) - - assert len(w) == 0 - assert router.registry == [ - ('mock', MockViewSet, 'foo'), - ] From ebcd93163a5d0663d16a16d4691df1bbe965d42f Mon Sep 17 00:00:00 2001 From: Roy Segall Date: Tue, 10 Dec 2019 11:18:35 +0200 Subject: [PATCH 69/79] Adding I'm a teapot error code (#7081) --- rest_framework/status.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rest_framework/status.py b/rest_framework/status.py index 06d0907339..2561d7689e 100644 --- a/rest_framework/status.py +++ b/rest_framework/status.py @@ -66,6 +66,7 @@ def is_server_error(code): HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415 HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416 HTTP_417_EXPECTATION_FAILED = 417 +HTTP_418_IM_A_TEAPOT = 418 HTTP_422_UNPROCESSABLE_ENTITY = 422 HTTP_423_LOCKED = 423 HTTP_424_FAILED_DEPENDENCY = 424 From de9f1d56c45557638725bc18733387beac27ad1e Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 11 Dec 2019 00:44:08 -0800 Subject: [PATCH 70/79] Followup to set_context removal (#7076) * Raise framework-specific deprecation warnings - Use `RemovedInDRF313Warning` instead of DeprecationWarning - Update to follow deprecation policy * Pass serializer instead of model to validator The `UniqueTogetherValidator` may need to access attributes on the serializer instead of just the model instance. For example, this is useful for handling field sources. * Fix framework deprecation warning in test * Remove outdated validator attribute --- rest_framework/fields.py | 14 +++++++------- rest_framework/validators.py | 25 +++++++++---------------- tests/test_fields.py | 7 +++---- tests/test_validators.py | 7 ++++++- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 8c80d6bd5a..11a2915683 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -30,7 +30,7 @@ from django.utils.translation import gettext_lazy as _ from pytz.exceptions import InvalidTimeError -from rest_framework import ISO_8601 +from rest_framework import ISO_8601, RemovedInDRF313Warning from rest_framework.compat import ProhibitNullCharactersValidator from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings @@ -263,10 +263,10 @@ def __call__(self, serializer_field): if hasattr(self.default, 'set_context'): warnings.warn( "Method `set_context` on defaults is deprecated and will " - "no longer be called starting with 3.12. Instead set " + "no longer be called starting with 3.13. Instead set " "`requires_context = True` on the class, and accept the " "context as an additional argument.", - DeprecationWarning, stacklevel=2 + RemovedInDRF313Warning, stacklevel=2 ) self.default.set_context(self) @@ -502,10 +502,10 @@ def get_default(self): if hasattr(self.default, 'set_context'): warnings.warn( "Method `set_context` on defaults is deprecated and will " - "no longer be called starting with 3.12. Instead set " + "no longer be called starting with 3.13. Instead set " "`requires_context = True` on the class, and accept the " "context as an additional argument.", - DeprecationWarning, stacklevel=2 + RemovedInDRF313Warning, stacklevel=2 ) self.default.set_context(self) @@ -576,10 +576,10 @@ def run_validators(self, value): if hasattr(validator, 'set_context'): warnings.warn( "Method `set_context` on validators is deprecated and will " - "no longer be called starting with 3.12. Instead set " + "no longer be called starting with 3.13. Instead set " "`requires_context = True` on the class, and accept the " "context as an additional argument.", - DeprecationWarning, stacklevel=2 + RemovedInDRF313Warning, stacklevel=2 ) validator.set_context(self) diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 2907312a9b..aa79377142 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -41,7 +41,6 @@ class UniqueValidator: def __init__(self, queryset, message=None, lookup='exact'): self.queryset = queryset - self.serializer_field = None self.message = message or self.message self.lookup = lookup @@ -94,15 +93,14 @@ class UniqueTogetherValidator: def __init__(self, queryset, fields, message=None): self.queryset = queryset self.fields = fields - self.serializer_field = None self.message = message or self.message - def enforce_required_fields(self, attrs, instance): + def enforce_required_fields(self, attrs, serializer): """ The `UniqueTogetherValidator` always forces an implied 'required' state on the fields it applies to. """ - if instance is not None: + if serializer.instance is not None: return missing_items = { @@ -113,16 +111,16 @@ def enforce_required_fields(self, attrs, instance): if missing_items: raise ValidationError(missing_items, code='required') - def filter_queryset(self, attrs, queryset, instance): + def filter_queryset(self, attrs, queryset, serializer): """ Filter the queryset to all instances matching the given attributes. """ # If this is an update, then any unprovided field should # have it's value set based on the existing instance attribute. - if instance is not None: + if serializer.instance is not None: for field_name in self.fields: if field_name not in attrs: - attrs[field_name] = getattr(instance, field_name) + attrs[field_name] = getattr(serializer.instance, field_name) # Determine the filter keyword arguments and filter the queryset. filter_kwargs = { @@ -141,13 +139,10 @@ def exclude_current_instance(self, attrs, queryset, instance): return queryset def __call__(self, attrs, serializer): - # Determine the existing instance, if this is an update operation. - instance = getattr(serializer, 'instance', None) - - self.enforce_required_fields(attrs, instance) + self.enforce_required_fields(attrs, serializer) queryset = self.queryset - queryset = self.filter_queryset(attrs, queryset, instance) - queryset = self.exclude_current_instance(attrs, queryset, instance) + queryset = self.filter_queryset(attrs, queryset, serializer) + queryset = self.exclude_current_instance(attrs, queryset, serializer.instance) # Ignore validation if any field is None checked_values = [ @@ -207,13 +202,11 @@ def __call__(self, attrs, serializer): # same as the serializer field names if `source=<>` is set. field_name = serializer.fields[self.field].source_attrs[-1] date_field_name = serializer.fields[self.date_field].source_attrs[-1] - # Determine the existing instance, if this is an update operation. - instance = getattr(serializer, 'instance', None) self.enforce_required_fields(attrs) queryset = self.queryset queryset = self.filter_queryset(attrs, queryset, field_name, date_field_name) - queryset = self.exclude_current_instance(attrs, queryset, instance) + queryset = self.exclude_current_instance(attrs, queryset, serializer.instance) if qs_exists(queryset): message = self.message.format(date_field=self.date_field) raise ValidationError({ diff --git a/tests/test_fields.py b/tests/test_fields.py index 1d302b730e..0be1b1a7a0 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -565,11 +565,10 @@ def test_create_only_default_callable_sets_context(self): on the callable if possible """ class TestCallableDefault: - def set_context(self, serializer_field): - self.field = serializer_field + requires_context = True - def __call__(self): - return "success" if hasattr(self, 'field') else "failure" + def __call__(self, field=None): + return "success" if field is not None else "failure" class TestSerializer(serializers.Serializer): context_set = serializers.CharField(default=serializers.CreateOnlyDefault(TestCallableDefault())) diff --git a/tests/test_validators.py b/tests/test_validators.py index bb29a4305b..5c4a62b314 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -357,11 +357,16 @@ class MockQueryset: def filter(self, **kwargs): self.called_with = kwargs + class MockSerializer: + def __init__(self, instance): + self.instance = instance + data = {'race_name': 'bar'} queryset = MockQueryset() + serializer = MockSerializer(instance=self.instance) validator = UniqueTogetherValidator(queryset, fields=('race_name', 'position')) - validator.filter_queryset(attrs=data, queryset=queryset, instance=self.instance) + validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer) assert queryset.called_with == {'race_name': 'bar', 'position': 1} From f744da74d2878b480220ebaf9d8117ff9b79a947 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Thu, 12 Dec 2019 01:08:54 +0200 Subject: [PATCH 71/79] Improve the docstring on @action (#6951) --- rest_framework/decorators.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index eb1cad9e4f..30b9d84d4e 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -124,8 +124,23 @@ def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs): """ Mark a ViewSet method as a routable action. - Set the `detail` boolean to determine if this action should apply to - instance/detail requests or collection/list requests. + `@action`-decorated functions will be endowed with a `mapping` property, + a `MethodMapper` that can be used to add additional method-based behaviors + on the routed action. + + :param methods: A list of HTTP method names this action responds to. + Defaults to GET only. + :param detail: Required. Determines whether this action applies to + instance/detail requests or collection/list requests. + :param url_path: Define the URL segment for this action. Defaults to the + name of the method decorated. + :param url_name: Define the internal (`reverse`) URL name for this action. + Defaults to the name of the method decorated with underscores + replaced with dashes. + :param kwargs: Additional properties to set on the view. This can be used + to override viewset-level *_classes settings, equivalent to + how the `@renderer_classes` etc. decorators work for function- + based API views. """ methods = ['get'] if (methods is None) else methods methods = [method.lower() for method in methods] @@ -144,6 +159,10 @@ def decorator(func): func.detail = detail func.url_path = url_path if url_path else func.__name__ func.url_name = url_name if url_name else func.__name__.replace('_', '-') + + # These kwargs will end up being passed to `ViewSet.as_view()` within + # the router, which eventually delegates to Django's CBV `View`, + # which assigns them as instance attributes for each request. func.kwargs = kwargs # Set descriptive arguments for viewsets From 236667b717309934e9c9cae91dbcb0abf4a5e04c Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 12 Dec 2019 05:02:30 -0800 Subject: [PATCH 72/79] Fix UniqueTogetherValidator with field sources (#7086) * Add failing tests for unique_together+source * Fix UniqueTogetherValidator source handling * Fix read-only+default+source handling * Update test to use functional serializer * Test UniqueTogetherValidator error+source --- rest_framework/serializers.py | 2 +- rest_framework/validators.py | 18 ++++++++----- tests/test_validators.py | 49 +++++++++++++++++++++++++++++++---- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index f5d9a50658..63fab3dc36 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -448,7 +448,7 @@ def _read_only_defaults(self): default = field.get_default() except SkipField: continue - defaults[field.field_name] = default + defaults[field.source] = default return defaults diff --git a/rest_framework/validators.py b/rest_framework/validators.py index aa79377142..4681d4fb13 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -106,7 +106,7 @@ def enforce_required_fields(self, attrs, serializer): missing_items = { field_name: self.missing_message for field_name in self.fields - if field_name not in attrs + if serializer.fields[field_name].source not in attrs } if missing_items: raise ValidationError(missing_items, code='required') @@ -115,17 +115,23 @@ def filter_queryset(self, attrs, queryset, serializer): """ Filter the queryset to all instances matching the given attributes. """ + # field names => field sources + sources = [ + serializer.fields[field_name].source + for field_name in self.fields + ] + # If this is an update, then any unprovided field should # have it's value set based on the existing instance attribute. if serializer.instance is not None: - for field_name in self.fields: - if field_name not in attrs: - attrs[field_name] = getattr(serializer.instance, field_name) + for source in sources: + if source not in attrs: + attrs[source] = getattr(serializer.instance, source) # Determine the filter keyword arguments and filter the queryset. filter_kwargs = { - field_name: attrs[field_name] - for field_name in self.fields + source: attrs[source] + for source in sources } return qs_filter(queryset, **filter_kwargs) diff --git a/tests/test_validators.py b/tests/test_validators.py index 5c4a62b314..21c00073d6 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -301,6 +301,49 @@ class Meta: ] } + def test_read_only_fields_with_default_and_source(self): + class ReadOnlySerializer(serializers.ModelSerializer): + name = serializers.CharField(source='race_name', default='test', read_only=True) + + class Meta: + model = UniquenessTogetherModel + fields = ['name', 'position'] + validators = [ + UniqueTogetherValidator( + queryset=UniquenessTogetherModel.objects.all(), + fields=['name', 'position'] + ) + ] + + serializer = ReadOnlySerializer(data={'position': 1}) + assert serializer.is_valid(raise_exception=True) + + def test_writeable_fields_with_source(self): + class WriteableSerializer(serializers.ModelSerializer): + name = serializers.CharField(source='race_name') + + class Meta: + model = UniquenessTogetherModel + fields = ['name', 'position'] + validators = [ + UniqueTogetherValidator( + queryset=UniquenessTogetherModel.objects.all(), + fields=['name', 'position'] + ) + ] + + serializer = WriteableSerializer(data={'name': 'test', 'position': 1}) + assert serializer.is_valid(raise_exception=True) + + # Validation error should use seriazlier field name, not source + serializer = WriteableSerializer(data={'position': 1}) + assert not serializer.is_valid() + assert serializer.errors == { + 'name': [ + 'This field is required.' + ] + } + def test_allow_explict_override(self): """ Ensure validators can be explicitly removed.. @@ -357,13 +400,9 @@ class MockQueryset: def filter(self, **kwargs): self.called_with = kwargs - class MockSerializer: - def __init__(self, instance): - self.instance = instance - data = {'race_name': 'bar'} queryset = MockQueryset() - serializer = MockSerializer(instance=self.instance) + serializer = UniquenessTogetherSerializer(instance=self.instance) validator = UniqueTogetherValidator(queryset, fields=('race_name', 'position')) validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer) From 7c5459626d3850537e054a11d4fe0035a4f0de24 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 12 Dec 2019 13:03:34 +0000 Subject: [PATCH 73/79] Declare Django versions in install_requires (#7063) * Declare Django versions in install_requires Pip's dependency resolver (used in pipenv, pip-compile, poetry, etc.) can use this to infer whether there's a verison collision in what it's being asked to install or not. * No max --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 24749992b6..65536885a7 100755 --- a/setup.py +++ b/setup.py @@ -82,7 +82,7 @@ def get_version(package): author_email='tom@tomchristie.com', # SEE NOTE BELOW (*) packages=find_packages(exclude=['tests*']), include_package_data=True, - install_requires=[], + install_requires=["django>=1.11"], python_requires=">=3.5", zip_safe=False, classifiers=[ From b8c369c4cf4fef055cb69e60fefe4eb7f1821e62 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 12 Dec 2019 06:03:55 -0800 Subject: [PATCH 74/79] Fix serializer multiple inheritance bug (#6980) * Expand declared filtering tests - Test declared filter ordering - Test multiple inheritance * Fix serializer multiple inheritance bug * Improve field order test to check for field types --- rest_framework/serializers.py | 28 +++++++++++--------- tests/test_serializer.py | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 63fab3dc36..18f4d0df68 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -298,18 +298,22 @@ def _get_declared_fields(cls, bases, attrs): if isinstance(obj, Field)] fields.sort(key=lambda x: x[1]._creation_counter) - # If this class is subclassing another Serializer, add that Serializer's - # fields. Note that we loop over the bases in *reverse*. This is necessary - # in order to maintain the correct order of fields. - for base in reversed(bases): - if hasattr(base, '_declared_fields'): - fields = [ - (field_name, obj) for field_name, obj - in base._declared_fields.items() - if field_name not in attrs - ] + fields - - return OrderedDict(fields) + # Ensures a base class field doesn't override cls attrs, and maintains + # field precedence when inheriting multiple parents. e.g. if there is a + # class C(A, B), and A and B both define 'field', use 'field' from A. + known = set(attrs) + + def visit(name): + known.add(name) + return name + + base_fields = [ + (visit(name), f) + for base in bases if hasattr(base, '_declared_fields') + for name, f in base._declared_fields.items() if name not in known + ] + + return OrderedDict(base_fields + fields) def __new__(cls, name, bases, attrs): attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index fab0472b94..a58c46b2d9 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -682,3 +682,53 @@ class Grandchild(Child): assert len(Parent().get_fields()) == 2 assert len(Child().get_fields()) == 2 assert len(Grandchild().get_fields()) == 2 + + def test_multiple_inheritance(self): + class A(serializers.Serializer): + field = serializers.CharField() + + class B(serializers.Serializer): + field = serializers.IntegerField() + + class TestSerializer(A, B): + pass + + fields = { + name: type(f) for name, f + in TestSerializer()._declared_fields.items() + } + assert fields == { + 'field': serializers.CharField, + } + + def test_field_ordering(self): + class Base(serializers.Serializer): + f1 = serializers.CharField() + f2 = serializers.CharField() + + class A(Base): + f3 = serializers.IntegerField() + + class B(serializers.Serializer): + f3 = serializers.CharField() + f4 = serializers.CharField() + + class TestSerializer(A, B): + f2 = serializers.IntegerField() + f5 = serializers.CharField() + + fields = { + name: type(f) for name, f + in TestSerializer()._declared_fields.items() + } + + # `IntegerField`s should be the 'winners' in field name conflicts + # - `TestSerializer.f2` should override `Base.F2` + # - `A.f3` should override `B.f3` + assert fields == { + 'f1': serializers.CharField, + 'f2': serializers.IntegerField, + 'f3': serializers.IntegerField, + 'f4': serializers.CharField, + 'f5': serializers.CharField, + } From 3c1428ff799e4fe19e1eb28ed63a7d54c6180f1d Mon Sep 17 00:00:00 2001 From: Jordan Ephron Date: Thu, 12 Dec 2019 06:09:34 -0800 Subject: [PATCH 75/79] Fix NotImplementedError for Field.to_internal_value and Field.to_representation (#6996) --- rest_framework/fields.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 11a2915683..2c45ec6f4f 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -605,8 +605,11 @@ def to_internal_value(self, data): Transform the *incoming* primitive data into a native value. """ raise NotImplementedError( - '{cls}.to_internal_value() must be implemented.'.format( - cls=self.__class__.__name__ + '{cls}.to_internal_value() must be implemented for field ' + '{field_name}. If you do not need to support write operations ' + 'you probably want to subclass `ReadOnlyField` instead.'.format( + cls=self.__class__.__name__, + field_name=self.field_name, ) ) @@ -615,9 +618,7 @@ def to_representation(self, value): Transform the *outgoing* native value into primitive data. """ raise NotImplementedError( - '{cls}.to_representation() must be implemented for field ' - '{field_name}. If you do not need to support write operations ' - 'you probably want to subclass `ReadOnlyField` instead.'.format( + '{cls}.to_representation() must be implemented for field {field_name}.'.format( cls=self.__class__.__name__, field_name=self.field_name, ) From de497a9bf12605b8b71bf7c21da57bc2a8238786 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 12 Dec 2019 14:31:40 +0000 Subject: [PATCH 76/79] Version 3.11 (#7083) * Version 3.11 * Added notes on OpenAPI changes for 3.11. * Minor docs tweaking * Update package version and supported versions * Use a lazy import for django.test.client.encode_mutlipart. Closes #7078 --- README.md | 4 +- docs/community/3.10-announcement.md | 2 +- docs/community/3.11-announcement.md | 117 ++++++++++++++++++++++++++++ docs/index.md | 6 +- mkdocs.yml | 1 + rest_framework/__init__.py | 2 +- rest_framework/renderers.py | 3 +- 7 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 docs/community/3.11-announcement.md diff --git a/README.md b/README.md index 8774bc854a..9591bdc17b 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements -* Python (3.5, 3.6, 3.7) -* Django (1.11, 2.0, 2.1, 2.2) +* Python (3.5, 3.6, 3.7, 3.8) +* Django (1.11, 2.0, 2.1, 2.2, 3.0) We **highly recommend** and only officially support the latest patch release of each Python and Django series. diff --git a/docs/community/3.10-announcement.md b/docs/community/3.10-announcement.md index 578e900dc1..19748aa40d 100644 --- a/docs/community/3.10-announcement.md +++ b/docs/community/3.10-announcement.md @@ -144,4 +144,4 @@ continued development by **[signing up for a paid plan][funding]**. [legacy-core-api-docs]:https://github.com/encode/django-rest-framework/blob/master/docs/coreapi/index.md [sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors -[funding]: community/funding.md +[funding]: funding.md diff --git a/docs/community/3.11-announcement.md b/docs/community/3.11-announcement.md new file mode 100644 index 0000000000..83dd636d19 --- /dev/null +++ b/docs/community/3.11-announcement.md @@ -0,0 +1,117 @@ + + +# Django REST framework 3.11 + +The 3.11 release adds support for Django 3.0. + +* Our supported Python versions are now: 3.5, 3.6, 3.7, and 3.8. +* Our supported Django versions are now: 1.11, 2.0, 2.1, 2.2, and 3.0. + +This release will be the last to support Python 3.5 or Django 1.11. + +## OpenAPI Schema Generation Improvements + +The OpenAPI schema generation continues to mature. Some highlights in 3.11 +include: + +* Automatic mapping of Django REST Framework renderers and parsers into OpenAPI + request and response media-types. +* Improved mapping JSON schema mapping types, for example in HStoreFields, and + with large integer values. +* Porting of the old CoreAPI parsing of docstrings to form OpenAPI operation + descriptions. + +In this example view operation descriptions for the `get` and `post` methods will +be extracted from the class docstring: + +```python +class DocStringExampleListView(APIView): +""" +get: A description of my GET operation. +post: A description of my POST operation. +""" + permission_classes = [permissions.IsAuthenticatedOrReadOnly] + + def get(self, request, *args, **kwargs): + ... + + def post(self, request, *args, **kwargs): + ... +``` + +## Validator / Default Context + +In some circumstances a Validator class or a Default class may need to access the serializer field with which it is called, or the `.context` with which the serializer was instantiated. In particular: + +* Uniqueness validators need to be able to determine the name of the field to which they are applied, in order to run an appropriate database query. +* The `CurrentUserDefault` needs to be able to determine the context with which the serializer was instantiated, in order to return the current user instance. + +Previous our approach to this was that implementations could include a `set_context` method, which would be called prior to validation. However this approach had issues with potential race conditions. We have now move this approach into a pending deprecation state. It will continue to function, but will be escalated to a deprecated state in 3.12, and removed entirely in 3.13. + +Instead, validators or defaults which require the serializer context, should include a `requires_context = True` attribute on the class. + +The `__call__` method should then include an additional `serializer_field` argument. + +Validator implementations will look like this: + +```python +class CustomValidator: + requires_context = True + + def __call__(self, value, serializer_field): + ... +``` + +Default implementations will look like this: + +```python +class CustomDefault: + requires_context = True + + def __call__(self, serializer_field): + ... +``` + +--- + +## Funding + +REST framework is a *collaboratively funded project*. If you use +REST framework commercially we strongly encourage you to invest in its +continued development by **[signing up for a paid plan][funding]**. + +*Every single sign-up helps us make REST framework long-term financially sustainable.* + + +
    + +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), and [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship).* + +[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors +[funding]: funding.md diff --git a/docs/index.md b/docs/index.md index e06b21dffd..bccc1fb46e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -52,7 +52,7 @@ Some reasons you might want to use REST framework: * [Authentication policies][authentication] including packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section]. * [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources. * Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers]. -* [Extensive documentation][index], and [great community support][group]. +* Extensive documentation, and [great community support][group]. * Used and trusted by internationally recognised companies including [Mozilla][mozilla], [Red Hat][redhat], [Heroku][heroku], and [Eventbrite][eventbrite]. --- @@ -85,8 +85,8 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: -* Python (3.5, 3.6, 3.7) -* Django (1.11, 2.0, 2.1, 2.2) +* Python (3.5, 3.6, 3.7, 3.8) +* Django (1.11, 2.0, 2.1, 2.2, 3.0) We **highly recommend** and only officially support the latest patch release of each Python and Django series. diff --git a/mkdocs.yml b/mkdocs.yml index 83a345a3d7..484971a715 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,6 +66,7 @@ nav: - 'Contributing to REST framework': 'community/contributing.md' - 'Project management': 'community/project-management.md' - 'Release Notes': 'community/release-notes.md' + - '3.11 Announcement': 'community/3.11-announcement.md' - '3.10 Announcement': 'community/3.10-announcement.md' - '3.9 Announcement': 'community/3.9-announcement.md' - '3.8 Announcement': 'community/3.8-announcement.md' diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index fceee6817d..b6f3f65ceb 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.10.3' +__version__ = '3.11.0' __author__ = 'Tom Christie' __license__ = 'BSD 3-Clause' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 9a6f3c3c57..29ac90ea8e 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -16,7 +16,6 @@ from django.core.paginator import Page from django.http.multipartparser import parse_header from django.template import engines, loader -from django.test.client import encode_multipart from django.urls import NoReverseMatch from django.utils.html import mark_safe @@ -902,6 +901,8 @@ class MultiPartRenderer(BaseRenderer): BOUNDARY = 'BoUnDaRyStRiNg' def render(self, data, accepted_media_type=None, renderer_context=None): + from django.test.client import encode_multipart + if hasattr(data, 'items'): for key, value in data.items(): assert not isinstance(value, dict), ( From eb2c4c2dc71ade5b8376acd9d574bb7382116e4b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Aug 2020 10:06:33 +0100 Subject: [PATCH 77/79] Version 3.11.1 --- rest_framework/filters.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index c15723ec3f..8ef01743c7 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -8,7 +8,6 @@ from django.core.exceptions import ImproperlyConfigured from django.db import models from django.db.models.constants import LOOKUP_SEP -from django.db.models.sql.constants import ORDER_PATTERN from django.template import loader from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ @@ -256,7 +255,13 @@ def get_valid_fields(self, queryset, view, context={}): def remove_invalid_fields(self, queryset, fields, view, request): valid_fields = [item[0] for item in self.get_valid_fields(queryset, view, {'request': request})] - return [term for term in fields if term.lstrip('-') in valid_fields and ORDER_PATTERN.match(term)] + + def term_valid(term): + if term.startswith("-"): + term = term[1:] + return term in valid_fields + + return [term for term in fields if term_valid(term)] def filter_queryset(self, request, queryset, view): ordering = self.get_ordering(request, queryset, view) From f3d9d68970562a6d1e6247b028ce67a2fcc6fa1d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Aug 2020 10:06:49 +0100 Subject: [PATCH 78/79] Version 3.11.1 --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index b6f3f65ceb..29c53dd7ed 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.11.0' +__version__ = '3.11.1' __author__ = 'Tom Christie' __license__ = 'BSD 3-Clause' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' From 4121b01b912668c049b26194a9a107c27a332429 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 30 Sep 2020 09:15:46 +0100 Subject: [PATCH 79/79] Deprecate urlize_quoted_links in favor of Django's built-in urlize --- rest_framework/__init__.py | 2 +- rest_framework/renderers.py | 2 +- rest_framework/templates/rest_framework/base.html | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 29c53dd7ed..471b5db196 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.11.1' +__version__ = '3.11.2' __author__ = 'Tom Christie' __license__ = 'BSD 3-Clause' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 29ac90ea8e..017ebbc6da 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -418,7 +418,7 @@ def get_content(self, renderer, data, if render_style == 'binary': return '[%d bytes of binary content]' % len(content) - return content + return content.decode('utf-8') if isinstance(content, bytes) else content def show_form_for_method(self, view, method, request, obj): """ diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 5d9d80b057..0fac705a2d 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -77,7 +77,7 @@
    {% block request_forms %} - + {% if 'GET' in allowed_methods %}
    @@ -176,9 +176,9 @@

    {{ name }}

    HTTP {{ response.status_code }} {{ response.status_text }}{% for key, val in response_headers|items %}
    -{{ key }}: {{ val|break_long_headers|urlize_quoted_links }}{% endfor %}
    +{{ key }}: {{ val|break_long_headers|urlize }}{% endfor %}
     
    -{{ content|urlize_quoted_links }}
    +{{ content|urlize }}