From 296e1f40072d1782e579c6a2af3360bedea6e565 Mon Sep 17 00:00:00 2001
From: Oliver Sauder <os@esite.ch>
Date: Wed, 29 Dec 2021 23:30:00 +0400
Subject: [PATCH] Removed all deprecations

---
 CHANGELOG.md                                  |  3 ++
 example/factories.py                          |  2 +-
 example/fixtures/blogentry.json               |  4 +-
 ...rename_type_author_author_type_and_more.py | 31 ++++++++++++
 example/models.py                             |  2 +-
 example/serializers.py                        |  9 ++--
 example/tests/__snapshots__/test_openapi.ambr | 12 ++---
 example/tests/test_format_keys.py             |  2 +-
 example/tests/test_model_viewsets.py          | 26 ----------
 example/tests/test_views.py                   |  8 +--
 rest_framework_json_api/parsers.py            |  8 +--
 rest_framework_json_api/serializers.py        | 15 +-----
 rest_framework_json_api/utils.py              | 35 ++-----------
 setup.cfg                                     |  4 --
 tests/test_parsers.py                         |  3 +-
 tests/test_serializers.py                     |  9 ----
 tests/test_utils.py                           | 49 +------------------
 17 files changed, 64 insertions(+), 158 deletions(-)
 create mode 100644 example/migrations/0011_rename_type_author_author_type_and_more.py

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bcbce7ce..43919ce2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,9 @@ any parts of the framework not mentioned in the documentation should generally b
 * Removed support for Django 3.0.
 * Removed support for Django 3.1.
 * Removed support for Python 3.6.
+* Removed obsolete method `utils.get_included_serializers`.
+* Removed optional `format_type` argument of `utils.format_link_segment`.
+* Removed `format_type`s default argument of `utils.format_value`. `format_type` is now required.
 
 ## [4.3.0] - 2021-12-10
 
diff --git a/example/factories.py b/example/factories.py
index 96b85cad..4ca1e0b1 100644
--- a/example/factories.py
+++ b/example/factories.py
@@ -42,7 +42,7 @@ class Meta:
     email = factory.LazyAttribute(lambda x: faker.email())
 
     bio = factory.RelatedFactory("example.factories.AuthorBioFactory", "author")
-    type = factory.SubFactory(AuthorTypeFactory)
+    author_type = factory.SubFactory(AuthorTypeFactory)
 
 
 class AuthorBioFactory(factory.django.DjangoModelFactory):
diff --git a/example/fixtures/blogentry.json b/example/fixtures/blogentry.json
index 464573e4..ceb4a9fe 100644
--- a/example/fixtures/blogentry.json
+++ b/example/fixtures/blogentry.json
@@ -77,7 +77,7 @@
     "modified_at": "2016-05-02T10:09:48.277",
     "name": "Alice",
     "email": "alice@example.com",
-    "type": null
+    "author_type": null
   }
 },
 {
@@ -88,7 +88,7 @@
     "modified_at": "2016-05-02T10:09:57.133",
     "name": "Bob",
     "email": "bob@example.com",
-    "type": null
+    "author_type": null
   }
 },
 {
diff --git a/example/migrations/0011_rename_type_author_author_type_and_more.py b/example/migrations/0011_rename_type_author_author_type_and_more.py
new file mode 100644
index 00000000..191e901c
--- /dev/null
+++ b/example/migrations/0011_rename_type_author_author_type_and_more.py
@@ -0,0 +1,31 @@
+# Generated by Django 4.0 on 2021-12-29 13:07
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("contenttypes", "0002_remove_content_type_name"),
+        ("example", "0010_auto_20210714_0809"),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name="author",
+            old_name="type",
+            new_name="author_type",
+        ),
+        migrations.AlterField(
+            model_name="project",
+            name="polymorphic_ctype",
+            field=models.ForeignKey(
+                editable=False,
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="polymorphic_%(app_label)s.%(class)s_set+",
+                to="contenttypes.contenttype",
+            ),
+        ),
+    ]
diff --git a/example/models.py b/example/models.py
index 63d93e33..c3850076 100644
--- a/example/models.py
+++ b/example/models.py
@@ -54,7 +54,7 @@ class Meta:
 class Author(BaseModel):
     name = models.CharField(max_length=50)
     email = models.EmailField()
-    type = models.ForeignKey(AuthorType, null=True, on_delete=models.CASCADE)
+    author_type = models.ForeignKey(AuthorType, null=True, on_delete=models.CASCADE)
 
     def __str__(self):
         return self.name
diff --git a/example/serializers.py b/example/serializers.py
index 0e1022cc..b9bf71d6 100644
--- a/example/serializers.py
+++ b/example/serializers.py
@@ -252,10 +252,13 @@ class AuthorSerializer(serializers.ModelSerializer):
         help_text="help for defaults",
     )
     initials = serializers.SerializerMethodField()
-    included_serializers = {"bio": AuthorBioSerializer, "type": AuthorTypeSerializer}
+    included_serializers = {
+        "bio": AuthorBioSerializer,
+        "author_type": AuthorTypeSerializer,
+    }
     related_serializers = {
         "bio": "example.serializers.AuthorBioSerializer",
-        "type": "example.serializers.AuthorTypeSerializer",
+        "author_type": "example.serializers.AuthorTypeSerializer",
         "comments": "example.serializers.CommentSerializer",
         "entries": "example.serializers.EntrySerializer",
         "first_entry": "example.serializers.EntrySerializer",
@@ -270,7 +273,7 @@ class Meta:
             "entries",
             "comments",
             "first_entry",
-            "type",
+            "author_type",
             "secrets",
             "defaults",
             "initials",
diff --git a/example/tests/__snapshots__/test_openapi.ambr b/example/tests/__snapshots__/test_openapi.ambr
index 48aa21fa..cb03ca73 100644
--- a/example/tests/__snapshots__/test_openapi.ambr
+++ b/example/tests/__snapshots__/test_openapi.ambr
@@ -124,6 +124,9 @@
                   },
                   "relationships": {
                     "properties": {
+                      "authorType": {
+                        "$ref": "#/components/schemas/reltoone"
+                      },
                       "bio": {
                         "$ref": "#/components/schemas/reltoone"
                       },
@@ -135,9 +138,6 @@
                       },
                       "firstEntry": {
                         "$ref": "#/components/schemas/reltoone"
-                      },
-                      "type": {
-                        "$ref": "#/components/schemas/reltoone"
                       }
                     },
                     "type": "object"
@@ -532,6 +532,9 @@
                   },
                   "relationships": {
                     "properties": {
+                      "authorType": {
+                        "$ref": "#/components/schemas/reltoone"
+                      },
                       "bio": {
                         "$ref": "#/components/schemas/reltoone"
                       },
@@ -543,9 +546,6 @@
                       },
                       "firstEntry": {
                         "$ref": "#/components/schemas/reltoone"
-                      },
-                      "type": {
-                        "$ref": "#/components/schemas/reltoone"
                       }
                     },
                     "type": "object"
diff --git a/example/tests/test_format_keys.py b/example/tests/test_format_keys.py
index 8d99eec7..b91cf595 100644
--- a/example/tests/test_format_keys.py
+++ b/example/tests/test_format_keys.py
@@ -59,7 +59,7 @@ def test_options_format_field_names(db, client):
         "bio",
         "entries",
         "firstEntry",
-        "type",
+        "authorType",
         "comments",
         "secrets",
         "defaults",
diff --git a/example/tests/test_model_viewsets.py b/example/tests/test_model_viewsets.py
index c58b5131..21a641f8 100644
--- a/example/tests/test_model_viewsets.py
+++ b/example/tests/test_model_viewsets.py
@@ -1,4 +1,3 @@
-import pytest
 from django.contrib.auth import get_user_model
 from django.test import override_settings
 from django.urls import reverse
@@ -216,28 +215,3 @@ def test_404_error_pointer(self):
         response = self.client.get(not_found_url)
         assert 404 == response.status_code
         assert errors == response.json()
-
-
-@pytest.mark.django_db
-def test_patch_allow_field_type(author, author_type_factory, client):
-    """
-    Verify that type field may be updated.
-    """
-    # TODO remove in next major version 5.0.0 see serializers.ReservedFieldNamesMixin
-    with pytest.deprecated_call():
-        author_type = author_type_factory()
-        url = reverse("author-detail", args=[author.id])
-
-        data = {
-            "data": {
-                "id": author.id,
-                "type": "authors",
-                "relationships": {
-                    "data": {"id": author_type.id, "type": "author-type"}
-                },
-            }
-        }
-
-        response = client.patch(url, data=data)
-
-        assert response.status_code == 200
diff --git a/example/tests/test_views.py b/example/tests/test_views.py
index 560f7547..a3aa1444 100644
--- a/example/tests/test_views.py
+++ b/example/tests/test_views.py
@@ -423,7 +423,7 @@ def test_get_related_serializer_class_many(self):
         self.assertEqual(got, EntrySerializer)
 
     def test_get_serializer_comes_from_included_serializers(self):
-        kwargs = {"pk": self.author.id, "related_field": "type"}
+        kwargs = {"pk": self.author.id, "related_field": "author_type"}
         view = self._get_view(kwargs)
         related_serializers = view.get_serializer_class().related_serializers
         delattr(view.get_serializer_class(), "related_serializers")
@@ -470,14 +470,14 @@ def test_retrieve_related_single_reverse_lookup(self):
     def test_retrieve_related_single(self):
         url = reverse(
             "author-related",
-            kwargs={"pk": self.author.type.pk, "related_field": "type"},
+            kwargs={"pk": self.author.author_type.pk, "related_field": "author_type"},
         )
         resp = self.client.get(url)
         expected = {
             "data": {
                 "type": "authorTypes",
-                "id": str(self.author.type.id),
-                "attributes": {"name": str(self.author.type.name)},
+                "id": str(self.author.author_type.id),
+                "attributes": {"name": str(self.author.author_type.name)},
             }
         }
         self.assertEqual(resp.status_code, 200)
diff --git a/rest_framework_json_api/parsers.py b/rest_framework_json_api/parsers.py
index 61824749..f77a6501 100644
--- a/rest_framework_json_api/parsers.py
+++ b/rest_framework_json_api/parsers.py
@@ -4,7 +4,7 @@
 from rest_framework import parsers
 from rest_framework.exceptions import ParseError
 
-from rest_framework_json_api import exceptions, renderers, serializers
+from rest_framework_json_api import exceptions, renderers
 from rest_framework_json_api.utils import get_resource_name, undo_format_field_names
 
 
@@ -160,12 +160,8 @@ def parse(self, stream, media_type=None, parser_context=None):
                 )
 
         # Construct the return data
-        serializer_class = getattr(view, "serializer_class", None)
         parsed_data = {"id": data.get("id")} if "id" in data else {}
-        # TODO remove in next major version 5.0.0 see serializers.ReservedFieldNamesMixin
-        if serializer_class is not None:
-            if issubclass(serializer_class, serializers.PolymorphicModelSerializer):
-                parsed_data["type"] = data.get("type")
+        parsed_data["type"] = data.get("type")
         parsed_data.update(self.parse_attributes(data))
         parsed_data.update(self.parse_relationships(data))
         parsed_data.update(self.parse_metadata(result))
diff --git a/rest_framework_json_api/serializers.py b/rest_framework_json_api/serializers.py
index cfc6cf3f..bfdfec71 100644
--- a/rest_framework_json_api/serializers.py
+++ b/rest_framework_json_api/serializers.py
@@ -1,4 +1,3 @@
-import warnings
 from collections import OrderedDict
 from collections.abc import Mapping
 
@@ -157,7 +156,7 @@ def validate_path(serializer_class, field_path, path):
 class ReservedFieldNamesMixin:
     """Ensures that reserved field names are not used and an error raised instead."""
 
-    _reserved_field_names = {"meta", "results"}
+    _reserved_field_names = {"meta", "results", "type"}
 
     def get_fields(self):
         fields = super().get_fields()
@@ -171,18 +170,6 @@ def get_fields(self):
             f"{', '.join(sorted(found_reserved_field_names))}"
         )
 
-        if "type" in fields:
-            # see https://jsonapi.org/format/#document-resource-object-fields
-            warnings.warn(
-                DeprecationWarning(
-                    f"Field name 'type'  found in serializer class "
-                    f"{self.__class__.__module__}.{self.__class__.__qualname__} "
-                    f"which is not allowed according to the JSON:API spec and "
-                    f"won't be supported anymore in the next major DJA release. "
-                    f"Rename 'type' field to something else. "
-                )
-            )
-
         return fields
 
 
diff --git a/rest_framework_json_api/utils.py b/rest_framework_json_api/utils.py
index b15f4c6c..aba9de5a 100644
--- a/rest_framework_json_api/utils.py
+++ b/rest_framework_json_api/utils.py
@@ -1,6 +1,5 @@
 import inspect
 import operator
-import warnings
 from collections import OrderedDict
 
 import inflection
@@ -147,23 +146,14 @@ def undo_format_field_name(field_name):
     return field_name
 
 
-def format_link_segment(value, format_type=None):
+def format_link_segment(value):
     """
     Takes a string value and returns it with formatted keys as set in `format_type`
     or `JSON_API_FORMAT_RELATED_LINKS`.
 
     :format_type: Either 'dasherize', 'camelize', 'capitalize' or 'underscore'
     """
-    if format_type is None:
-        format_type = json_api_settings.FORMAT_RELATED_LINKS
-    else:
-        warnings.warn(
-            DeprecationWarning(
-                "Using `format_type` argument is deprecated."
-                "Use `format_value` instead."
-            )
-        )
-
+    format_type = json_api_settings.FORMAT_RELATED_LINKS
     return format_value(value, format_type)
 
 
@@ -179,15 +169,7 @@ def undo_format_link_segment(value):
     return value
 
 
-def format_value(value, format_type=None):
-    if format_type is None:
-        warnings.warn(
-            DeprecationWarning(
-                "Using `format_value` without passing on `format_type` argument is deprecated."
-                "Use `format_field_name` instead."
-            )
-        )
-        format_type = json_api_settings.FORMAT_FIELD_NAMES
+def format_value(value, format_type):
     if format_type == "dasherize":
         # inflection can't dasherize camelCase
         value = inflection.underscore(value)
@@ -342,17 +324,6 @@ def get_default_included_resources_from_serializer(serializer):
     return list(getattr(meta, "included_resources", []))
 
 
-def get_included_serializers(serializer):
-    warnings.warn(
-        DeprecationWarning(
-            "Using of `get_included_serializers(serializer)` function is deprecated."
-            "Use `serializer.included_serializers` instead."
-        )
-    )
-
-    return getattr(serializer, "included_serializers", dict())
-
-
 def get_relation_instance(resource_instance, source, serializer):
     try:
         relation_instance = operator.attrgetter(source)(resource_instance)
diff --git a/setup.cfg b/setup.cfg
index afe7faab..527ddd6b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -58,10 +58,6 @@ DJANGO_SETTINGS_MODULE=example.settings.test
 filterwarnings =
     error::DeprecationWarning
     error::PendingDeprecationWarning
-    # TODO remove in next major version of DJA 5.0.0
-    # this deprecation warning filter needs to be added as AuthorSerializer is used in
-    # too many tests which introduced the type field name in tests
-    ignore:Field name 'type'
 testpaths =
     example
     tests
diff --git a/tests/test_parsers.py b/tests/test_parsers.py
index f1207757..45ac5232 100644
--- a/tests/test_parsers.py
+++ b/tests/test_parsers.py
@@ -63,6 +63,7 @@ def test_parse_formats_field_names(
         result = parse(data, parser_context)
         assert result == {
             "id": "123",
+            "type": "BasicModel",
             "test_attribute": "test-value",
             "test_relationship": {"id": "123", "type": "TestRelationship"},
         }
@@ -85,7 +86,7 @@ def test_parse_with_default_arguments(self, parse):
             },
         }
         result = parse(data, None)
-        assert result == {}
+        assert result == {"type": "BasicModel"}
 
     def test_parse_preserves_json_value_field_names(
         self, settings, parse, parser_context
diff --git a/tests/test_serializers.py b/tests/test_serializers.py
index 19ee23d6..e1b14ed8 100644
--- a/tests/test_serializers.py
+++ b/tests/test_serializers.py
@@ -50,12 +50,3 @@ class ReservedFieldNamesSerializer(serializers.Serializer):
         "ReservedFieldNamesSerializer uses following reserved field name(s) which is "
         "not allowed: meta, results"
     )
-
-
-def test_serializer_fields_deprecated_field_name_type():
-    with pytest.deprecated_call():
-
-        class TypeFieldNameSerializer(serializers.Serializer):
-            type = serializers.CharField()
-
-        TypeFieldNameSerializer().fields
diff --git a/tests/test_utils.py b/tests/test_utils.py
index d8810c0b..efb1325d 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,5 +1,4 @@
 import pytest
-from django.db import models
 from rest_framework import status
 from rest_framework.fields import Field
 from rest_framework.generics import GenericAPIView
@@ -13,7 +12,6 @@
     format_link_segment,
     format_resource_type,
     format_value,
-    get_included_serializers,
     get_related_resource_type,
     get_resource_name,
     get_resource_type_from_serializer,
@@ -23,13 +21,12 @@
 )
 from tests.models import (
     BasicModel,
-    DJAModel,
     ForeignKeySource,
     ForeignKeyTarget,
     ManyToManySource,
     ManyToManyTarget,
 )
-from tests.serializers import BasicModelSerializer, ManyToManyTargetSerializer
+from tests.serializers import BasicModelSerializer
 
 
 def test_get_resource_name_no_view():
@@ -256,11 +253,6 @@ def test_format_link_segment(settings, format_type, output):
     assert format_link_segment("first_Name") == output
 
 
-def test_format_link_segment_deprecates_format_type_argument():
-    with pytest.deprecated_call():
-        assert "first-name" == format_link_segment("first_name", "dasherize")
-
-
 @pytest.mark.parametrize(
     "format_links,output",
     [
@@ -289,11 +281,6 @@ def test_format_value(settings, format_type, output):
     assert format_value("first_name", format_type) == output
 
 
-def test_format_value_deprecates_default_format_type_argument():
-    with pytest.deprecated_call():
-        assert "first_name" == format_value("first_name")
-
-
 @pytest.mark.parametrize(
     "resource_type,pluralize,output",
     [
@@ -347,40 +334,6 @@ class PlainRelatedResourceTypeSerializer(serializers.Serializer):
     assert get_related_resource_type(field) == output
 
 
-def test_get_included_serializers():
-    class DeprecatedIncludedSerializersModel(DJAModel):
-        self = models.ForeignKey("self", on_delete=models.CASCADE)
-        target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE)
-        other_target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE)
-
-        class Meta:
-            app_label = "tests"
-
-    class DeprecatedIncludedSerializersSerializer(serializers.ModelSerializer):
-        included_serializers = {
-            "self": "self",
-            "target": ManyToManyTargetSerializer,
-            "other_target": "tests.serializers.ManyToManyTargetSerializer",
-        }
-
-        class Meta:
-            model = DeprecatedIncludedSerializersModel
-            fields = ("self", "other_target", "target")
-
-    with pytest.deprecated_call():
-        included_serializers = get_included_serializers(
-            DeprecatedIncludedSerializersSerializer
-        )
-
-    expected_included_serializers = {
-        "self": DeprecatedIncludedSerializersSerializer,
-        "target": ManyToManyTargetSerializer,
-        "other_target": ManyToManyTargetSerializer,
-    }
-
-    assert included_serializers == expected_included_serializers
-
-
 def test_get_resource_type_from_serializer_without_resource_name_raises_error():
     class SerializerWithoutResourceName(serializers.Serializer):
         something = Field()