From 6a1c4dd92cdfb59dbb0ceb8dd76e262a915ab98e Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Sun, 16 Oct 2022 13:34:04 +0600 Subject: [PATCH 1/8] try using python dict instead of ordered dict in metadata --- rest_framework_json_api/metadata.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rest_framework_json_api/metadata.py b/rest_framework_json_api/metadata.py index 14b9f69b..f1bf7162 100644 --- a/rest_framework_json_api/metadata.py +++ b/rest_framework_json_api/metadata.py @@ -1,5 +1,3 @@ -from collections import OrderedDict - from django.db.models.fields import related from django.utils.encoding import force_str from rest_framework import serializers @@ -65,7 +63,7 @@ class JSONAPIMetadata(SimpleMetadata): ) def determine_metadata(self, request, view): - metadata = OrderedDict() + metadata = {} metadata["name"] = view.get_view_name() metadata["description"] = view.get_view_description() metadata["renders"] = [ @@ -92,19 +90,19 @@ def get_serializer_info(self, serializer): # Remove the URL field if present serializer.fields.pop(api_settings.URL_FIELD_NAME, None) - return OrderedDict( + return { [ (format_field_name(field_name), self.get_field_info(field)) for field_name, field in serializer.fields.items() ] - ) + } def get_field_info(self, field): """ Given an instance of a serializer field, return a dictionary of metadata about it. """ - field_info = OrderedDict() + field_info = {} serializer = field.parent if isinstance(field, serializers.ManyRelatedField): From 8ffeee15290894457c484a40d7e4c9ae267a1e3b Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Sun, 16 Oct 2022 13:36:43 +0600 Subject: [PATCH 2/8] try using python dict instead of ordered dict in pagination field --- rest_framework_json_api/pagination.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/rest_framework_json_api/pagination.py b/rest_framework_json_api/pagination.py index 6cbd744c..3c37a20d 100644 --- a/rest_framework_json_api/pagination.py +++ b/rest_framework_json_api/pagination.py @@ -1,7 +1,6 @@ """ Pagination fields """ -from collections import OrderedDict from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination from rest_framework.utils.urls import remove_query_param, replace_query_param @@ -36,22 +35,22 @@ def get_paginated_response(self, data): { "results": data, "meta": { - "pagination": OrderedDict( + "pagination": { [ ("page", self.page.number), ("pages", self.page.paginator.num_pages), ("count", self.page.paginator.count), ] - ) + } }, - "links": OrderedDict( + "links": { [ ("first", self.build_link(1)), ("last", self.build_link(self.page.paginator.num_pages)), ("next", self.build_link(next)), ("prev", self.build_link(previous)), ] - ), + }, } ) @@ -97,21 +96,21 @@ def get_paginated_response(self, data): { "results": data, "meta": { - "pagination": OrderedDict( + "pagination": { [ ("count", self.count), ("limit", self.limit), ("offset", self.offset), ] - ) + } }, - "links": OrderedDict( + "links": { [ ("first", self.get_first_link()), ("last", self.get_last_link()), ("next", self.get_next_link()), ("prev", self.get_previous_link()), ] - ), + }, } ) From 75c4dbdf089fbbef5144ea6b56fa384f84d72d89 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Sun, 16 Oct 2022 13:44:57 +0600 Subject: [PATCH 3/8] try using python dict instead of ordered dict in relations --- rest_framework_json_api/relations.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/rest_framework_json_api/relations.py b/rest_framework_json_api/relations.py index 6eb69bdb..a0906a52 100644 --- a/rest_framework_json_api/relations.py +++ b/rest_framework_json_api/relations.py @@ -1,5 +1,4 @@ import json -from collections import OrderedDict import inflection from django.core.exceptions import ImproperlyConfigured @@ -104,7 +103,7 @@ def get_url(self, name, view_name, kwargs, request): def get_links(self, obj=None, lookup_field="pk"): request = self.context.get("request", None) view = self.context.get("view", None) - return_data = OrderedDict() + return_data = {} kwargs = { lookup_field: getattr(obj, lookup_field) @@ -257,7 +256,7 @@ def to_representation(self, value): if resource_type is None or not self._skip_polymorphic_optimization: resource_type = get_resource_type_from_instance(value) - return OrderedDict([("type", resource_type), ("id", str(pk))]) + return {[("type", resource_type), ("id", str(pk))]} def get_resource_type_from_included_serializer(self): """ @@ -301,12 +300,12 @@ def get_choices(self, cutoff=None): if cutoff is not None: queryset = queryset[:cutoff] - return OrderedDict( + return { [ (json.dumps(self.to_representation(item)), self.display_value(item)) for item in queryset ] - ) + } class PolymorphicResourceRelatedField(ResourceRelatedField): From a455dfda8fe5efe862bfcaa59710c59bc20a0782 Mon Sep 17 00:00:00 2001 From: Oliver Sauder Date: Tue, 21 Feb 2023 12:52:42 +0400 Subject: [PATCH 4/8] Adjusted to dict syntax --- rest_framework_json_api/metadata.py | 6 ++--- rest_framework_json_api/pagination.py | 36 +++++++++++---------------- rest_framework_json_api/relations.py | 8 +++--- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/rest_framework_json_api/metadata.py b/rest_framework_json_api/metadata.py index f1bf7162..c7f7b7b4 100644 --- a/rest_framework_json_api/metadata.py +++ b/rest_framework_json_api/metadata.py @@ -91,10 +91,8 @@ def get_serializer_info(self, serializer): serializer.fields.pop(api_settings.URL_FIELD_NAME, None) return { - [ - (format_field_name(field_name), self.get_field_info(field)) - for field_name, field in serializer.fields.items() - ] + format_field_name(field_name): self.get_field_info(field) + for field_name, field in serializer.fields.items() } def get_field_info(self, field): diff --git a/rest_framework_json_api/pagination.py b/rest_framework_json_api/pagination.py index 3c37a20d..0c0d3bb9 100644 --- a/rest_framework_json_api/pagination.py +++ b/rest_framework_json_api/pagination.py @@ -36,20 +36,16 @@ def get_paginated_response(self, data): "results": data, "meta": { "pagination": { - [ - ("page", self.page.number), - ("pages", self.page.paginator.num_pages), - ("count", self.page.paginator.count), - ] + "page": self.page.number, + "pages": self.page.paginator.num_pages, + "count": self.page.paginator.count, } }, "links": { - [ - ("first", self.build_link(1)), - ("last", self.build_link(self.page.paginator.num_pages)), - ("next", self.build_link(next)), - ("prev", self.build_link(previous)), - ] + "first": self.build_link(1), + "last": self.build_link(self.page.paginator.num_pages), + "next": self.build_link(next), + "prev": self.build_link(previous), }, } ) @@ -97,20 +93,16 @@ def get_paginated_response(self, data): "results": data, "meta": { "pagination": { - [ - ("count", self.count), - ("limit", self.limit), - ("offset", self.offset), - ] + "count": self.count, + "limit": self.limit, + "offset": self.offset, } }, "links": { - [ - ("first", self.get_first_link()), - ("last", self.get_last_link()), - ("next", self.get_next_link()), - ("prev", self.get_previous_link()), - ] + "first": self.get_first_link(), + "last": self.get_last_link(), + "next": self.get_next_link(), + "prev": self.get_previous_link(), }, } ) diff --git a/rest_framework_json_api/relations.py b/rest_framework_json_api/relations.py index a0906a52..bb360ebb 100644 --- a/rest_framework_json_api/relations.py +++ b/rest_framework_json_api/relations.py @@ -256,7 +256,7 @@ def to_representation(self, value): if resource_type is None or not self._skip_polymorphic_optimization: resource_type = get_resource_type_from_instance(value) - return {[("type", resource_type), ("id", str(pk))]} + return {"type": resource_type, "id": str(pk)} def get_resource_type_from_included_serializer(self): """ @@ -301,10 +301,8 @@ def get_choices(self, cutoff=None): queryset = queryset[:cutoff] return { - [ - (json.dumps(self.to_representation(item)), self.display_value(item)) - for item in queryset - ] + json.dumps(self.to_representation(item)): self.display_value(item) + for item in queryset } From 3d01f0c7cdda8894c745cb209244f4ba79f8ba57 Mon Sep 17 00:00:00 2001 From: Oliver Sauder Date: Tue, 21 Feb 2023 15:45:48 +0400 Subject: [PATCH 5/8] Removed OrderedDict from renderers.py --- rest_framework_json_api/renderers.py | 76 ++++++++++++---------------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py index 0540a39f..1390c635 100644 --- a/rest_framework_json_api/renderers.py +++ b/rest_framework_json_api/renderers.py @@ -2,7 +2,7 @@ Renderers """ import copy -from collections import OrderedDict, defaultdict +from collections import defaultdict from collections.abc import Iterable import inflection @@ -56,7 +56,7 @@ def extract_attributes(cls, fields, resource): """ Builds the `attributes` object of the JSON:API resource object. """ - data = OrderedDict() + data = {} for field_name, field in iter(fields.items()): # ID is always provided in the root of JSON:API so remove it from attributes if field_name == "id": @@ -89,7 +89,7 @@ def extract_relationships(cls, fields, resource, resource_instance): # Avoid circular deps from rest_framework_json_api.relations import ResourceRelatedField - data = OrderedDict() + data = {} # Don't try to extract relationships from a non-existent resource if resource_instance is None: @@ -127,12 +127,10 @@ def extract_relationships(cls, fields, resource, resource_instance): for related_object in relation_queryset: relation_data.append( - OrderedDict( - [ - ("type", relation_type), - ("id", encoding.force_str(related_object.pk)), - ] - ) + { + "type": relation_type, + "id": encoding.force_str(related_object.pk), + } ) data.update( @@ -171,18 +169,12 @@ def extract_relationships(cls, fields, resource, resource_instance): if not resolved: continue relation_id = relation if resource.get(field_name) else None - relation_data = { - "data": ( - OrderedDict( - [ - ("type", relation_type), - ("id", encoding.force_str(relation_id)), - ] - ) - if relation_id is not None - else None - ) - } + relation_data = {"data": None} + if relation_id is not None: + relation_data["data"] = { + "type": relation_type, + "id": encoding.force_str(relation_id), + } if isinstance( field, relations.HyperlinkedRelatedField @@ -233,12 +225,10 @@ def extract_relationships(cls, fields, resource, resource_instance): ) relation_data.append( - OrderedDict( - [ - ("type", nested_resource_instance_type), - ("id", encoding.force_str(nested_resource_instance.pk)), - ] - ) + { + "type": nested_resource_instance_type, + "id": encoding.force_str(nested_resource_instance.pk), + } ) data.update( { @@ -419,7 +409,7 @@ def extract_meta(cls, serializer, resource): else: meta = getattr(serializer, "Meta", None) meta_fields = getattr(meta, "meta_fields", []) - data = OrderedDict() + data = {} for field_name in meta_fields: data.update({field_name: resource.get(field_name)}) return data @@ -457,37 +447,35 @@ def build_json_resource_obj( # Determine type from the instance if the underlying model is polymorphic if force_type_resolution: resource_name = utils.get_resource_type_from_instance(resource_instance) - resource_data = [ - ("type", resource_name), - ( - "id", - encoding.force_str(resource_instance.pk) if resource_instance else None, - ), - ("attributes", cls.extract_attributes(fields, resource)), - ] + resource_id = ( + encoding.force_str(resource_instance.pk) if resource_instance else None + ) + resource_data = { + "type": resource_name, + "id": resource_id, + "attributes": cls.extract_attributes(fields, resource), + } relationships = cls.extract_relationships(fields, resource, resource_instance) if relationships: - resource_data.append(("relationships", relationships)) + resource_data["relationships"] = relationships # Add 'self' link if field is present and valid if api_settings.URL_FIELD_NAME in resource and isinstance( fields[api_settings.URL_FIELD_NAME], relations.RelatedField ): - resource_data.append( - ("links", {"self": resource[api_settings.URL_FIELD_NAME]}) - ) + resource_data["links"] = {"self": resource[api_settings.URL_FIELD_NAME]} meta = cls.extract_meta(serializer, resource) if meta: - resource_data.append(("meta", utils.format_field_names(meta))) + resource_data["meta"] = utils.format_field_names(meta) - return OrderedDict(resource_data) + return resource_data def render_relationship_view( self, data, accepted_media_type=None, renderer_context=None ): # Special case for RelationshipView view = renderer_context.get("view", None) - render_data = OrderedDict([("data", data)]) + render_data = {"data": data} links = view.get_links() if links: render_data.update({"links": links}), @@ -615,7 +603,7 @@ def render(self, data, accepted_media_type=None, renderer_context=None): ) # Make sure we render data in a specific order - render_data = OrderedDict() + render_data = {} if isinstance(data, dict) and data.get("links"): render_data["links"] = data.get("links") From 8f325024c3e09dc89b75ef94cfbc9f2c00758638 Mon Sep 17 00:00:00 2001 From: Oliver Sauder Date: Tue, 21 Feb 2023 16:14:20 +0400 Subject: [PATCH 6/8] Removed OrderedDict in serializers, utils and views --- rest_framework_json_api/serializers.py | 5 ++-- rest_framework_json_api/utils.py | 7 +----- rest_framework_json_api/views.py | 7 +++--- tests/test_pagination.py | 34 +++++++++----------------- 4 files changed, 17 insertions(+), 36 deletions(-) diff --git a/rest_framework_json_api/serializers.py b/rest_framework_json_api/serializers.py index 91f86464..a288471e 100644 --- a/rest_framework_json_api/serializers.py +++ b/rest_framework_json_api/serializers.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from collections.abc import Mapping import inflection @@ -95,7 +94,7 @@ def __init__(self, *args, **kwargs): pass else: fieldset = request.query_params.get(param_name).split(",") - # iterate over a *copy* of self.fields' underlying OrderedDict, because we may + # iterate over a *copy* of self.fields' underlying dict, because we may # modify the original during the iteration. # self.fields is a `rest_framework.utils.serializer_helpers.BindingDict` for field_name, _field in self.fields.fields.copy().items(): @@ -305,7 +304,7 @@ def get_field_names(self, declared_fields, info): """ meta_fields = getattr(self.Meta, "meta_fields", []) - declared = OrderedDict() + declared = {} for field_name in set(declared_fields.keys()): field = declared_fields[field_name] if field_name not in meta_fields: diff --git a/rest_framework_json_api/utils.py b/rest_framework_json_api/utils.py index b7f2f9a0..3d374eed 100644 --- a/rest_framework_json_api/utils.py +++ b/rest_framework_json_api/utils.py @@ -1,6 +1,5 @@ import inspect import operator -from collections import OrderedDict import inflection from django.conf import settings @@ -107,11 +106,7 @@ def format_field_names(obj, format_type=None): format_type = json_api_settings.FORMAT_FIELD_NAMES if isinstance(obj, dict): - formatted = OrderedDict() - for key, value in obj.items(): - key = format_value(key, format_type) - formatted[key] = value - return formatted + return {format_value(key, format_type): value for key, value in obj.items()} return obj diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index 0b3df693..90bb5574 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -23,7 +23,6 @@ from rest_framework_json_api.serializers import ResourceIdentifierObjectSerializer from rest_framework_json_api.utils import ( Hyperlink, - OrderedDict, get_included_resources, get_resource_type_from_instance, undo_format_link_segment, @@ -275,7 +274,7 @@ def get_url(self, name, view_name, kwargs, request): return Hyperlink(url, name) def get_links(self): - return_data = OrderedDict() + return_data = {} self_link = self.get_url( "self", self.self_link_view_name, self.kwargs, self.request ) @@ -284,9 +283,9 @@ def get_links(self): "related", self.related_link_view_name, related_kwargs, self.request ) if self_link: - return_data.update({"self": self_link}) + return_data["self"] = self_link if related_link: - return_data.update({"related": related_link}) + return_data["related"] = related_link return return_data def get(self, request, *args, **kwargs): diff --git a/tests/test_pagination.py b/tests/test_pagination.py index c09ac71c..10f0ebbf 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -1,5 +1,3 @@ -from collections import OrderedDict - from rest_framework.request import Request from rest_framework_json_api.pagination import JsonApiLimitOffsetPagination @@ -27,28 +25,18 @@ def test_get_paginated_response(self, rf): expected_content = { "results": list(range(11, 16)), - "links": OrderedDict( - [ - ("first", "http://testserver/?page%5Blimit%5D=5"), - ( - "last", - "http://testserver/?page%5Blimit%5D=5&page%5Boffset%5D=100", - ), - ( - "next", - "http://testserver/?page%5Blimit%5D=5&page%5Boffset%5D=15", - ), - ("prev", "http://testserver/?page%5Blimit%5D=5&page%5Boffset%5D=5"), - ] - ), + "links": { + "first": "http://testserver/?page%5Blimit%5D=5", + "last": "http://testserver/?page%5Blimit%5D=5&page%5Boffset%5D=100", + "next": "http://testserver/?page%5Blimit%5D=5&page%5Boffset%5D=15", + "prev": "http://testserver/?page%5Blimit%5D=5&page%5Boffset%5D=5", + }, "meta": { - "pagination": OrderedDict( - [ - ("count", count), - ("limit", limit), - ("offset", offset), - ] - ) + "pagination": { + "count": count, + "limit": limit, + "offset": offset, + } }, } From 3acca613713fc5fffa8766c5cf158e8aef2b2332 Mon Sep 17 00:00:00 2001 From: Oliver Sauder Date: Tue, 21 Feb 2023 16:21:06 +0400 Subject: [PATCH 7/8] Simplified creating of dicts in renderers.py --- rest_framework_json_api/renderers.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py index 1390c635..7263b96b 100644 --- a/rest_framework_json_api/renderers.py +++ b/rest_framework_json_api/renderers.py @@ -8,7 +8,7 @@ import inflection from django.db.models import Manager from django.template import loader -from django.utils import encoding +from django.utils.encoding import force_str from rest_framework import relations, renderers from rest_framework.fields import SkipField, get_attribute from rest_framework.relations import PKOnlyObject @@ -125,14 +125,10 @@ def extract_relationships(cls, fields, resource, resource_instance): relation_instance if relation_instance is not None else list() ) - for related_object in relation_queryset: - relation_data.append( - { - "type": relation_type, - "id": encoding.force_str(related_object.pk), - } - ) - + relation_data = [ + {"type": relation_type, "id": force_str(related_object.pk)} + for related_object in relation_queryset + ] data.update( { field_name: { @@ -173,7 +169,7 @@ def extract_relationships(cls, fields, resource, resource_instance): if relation_id is not None: relation_data["data"] = { "type": relation_type, - "id": encoding.force_str(relation_id), + "id": force_str(relation_id), } if isinstance( @@ -227,7 +223,7 @@ def extract_relationships(cls, fields, resource, resource_instance): relation_data.append( { "type": nested_resource_instance_type, - "id": encoding.force_str(nested_resource_instance.pk), + "id": force_str(nested_resource_instance.pk), } ) data.update( @@ -447,9 +443,7 @@ def build_json_resource_obj( # Determine type from the instance if the underlying model is polymorphic if force_type_resolution: resource_name = utils.get_resource_type_from_instance(resource_instance) - resource_id = ( - encoding.force_str(resource_instance.pk) if resource_instance else None - ) + resource_id = force_str(resource_instance.pk) if resource_instance else None resource_data = { "type": resource_name, "id": resource_id, From 6b28ec9bb0211f82f0275718c37b31c9ba506df3 Mon Sep 17 00:00:00 2001 From: Oliver Sauder Date: Wed, 22 Feb 2023 16:40:59 +0400 Subject: [PATCH 8/8] Added CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce154cbb..7a1c3444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ any parts of the framework not mentioned in the documentation should generally b * Added support to overwrite serializer methods in customized schema class * Adjusted some still old formatted strings to f-strings. +* Replaced `OrderedDict` with `dict` which is also ordered since Python 3.7. ### Fixed