diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..c2b66bc4
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,9 @@
+version: 2
+updates:
+  - package-ecosystem: "pip"
+    directory: "/requirements"
+    schedule:
+      interval: "monthly"
+    groups:
+      all-deps:
+        patterns: ["*"]
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index 8faf1a2f..00000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,74 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
-  push:
-    branches: [ "main" ]
-  pull_request:
-    # The branches below must be a subset of the branches above
-    branches: [ "main" ]
-  schedule:
-    - cron: '39 4 * * 2'
-
-jobs:
-  analyze:
-    name: Analyze
-    runs-on: ubuntu-latest
-    permissions:
-      actions: read
-      contents: read
-      security-events: write
-
-    strategy:
-      fail-fast: false
-      matrix:
-        language: [ 'python' ]
-        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
-        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
-
-    steps:
-    - name: Checkout repository
-      uses: actions/checkout@v3
-
-    # Initializes the CodeQL tools for scanning.
-    - name: Initialize CodeQL
-      uses: github/codeql-action/init@v2
-      with:
-        languages: ${{ matrix.language }}
-        # If you wish to specify custom queries, you can do so here or in a config file.
-        # By default, queries listed here will override any specified in a config file.
-        # Prefix the list here with "+" to use these queries and those in the config file.
-
-        # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
-        # queries: security-extended,security-and-quality
-
-
-    # Autobuild attempts to build any compiled languages  (C/C++, C#, Go, or Java).
-    # If this step fails, then you should remove it and run the build manually (see below)
-    - name: Autobuild
-      uses: github/codeql-action/autobuild@v2
-
-    # ℹ️ Command-line programs to run using the OS shell.
-    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
-
-    #   If the Autobuild fails above, remove it and uncomment the following three lines.
-    #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
-
-    # - run: |
-    #   echo "Run, Build Application using script"
-    #   ./location_of_script_within_repo/buildscript.sh
-
-    - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@v2
-      with:
-        category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 2f966adc..03a1db78 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,4 +1,6 @@
 name: Tests
+permissions:
+  contents: read
 on:
   push:
     branches: ["main"]
@@ -6,7 +8,6 @@ on:
     branches: ["main"]
   schedule:
     - cron: '0 4 * * *'
-
 jobs:
   test:
     name: Run test
@@ -14,7 +15,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
     env:
       PYTHON: ${{ matrix.python-version }}
     steps:
@@ -28,7 +29,7 @@ jobs:
           python -m pip install --upgrade pip
           pip install tox
       - name: Run tox targets for ${{ matrix.python-version }}
-        run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .)
+        run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d . | cut -f 1 -d '-')
       - name: Upload coverage report
         uses: codecov/codecov-action@v4
         with:
@@ -43,10 +44,10 @@ jobs:
         tox-env: ["black", "lint", "docs"]
     steps:
       - uses: actions/checkout@v2
-      - name: Set up Python 3.9
+      - name: Set up Python 3.10
         uses: actions/setup-python@v2
         with:
-          python-version: 3.9
+          python-version: "3.10"
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
diff --git a/.pyup.yml b/.pyup.yml
deleted file mode 100644
index 7ee57975..00000000
--- a/.pyup.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-search: False
-schedule: "every two weeks"
-requirements:
-  - requirements/requirements-codestyle.txt:
-      update: all
-      pin: True
-  - requirements/requirements-documentation.txt:
-      update: all
-      pin: True
-  - requirements/requirements-optionals.txt:
-      update: all
-      pin: True
-  - requirements/requirements-packaging.txt:
-      update: all
-      pin: True
-  - requirements/requirements-testing.txt:
-      update: all
-      pin: True
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 997afe3f..fa503604 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -3,7 +3,7 @@ version: 2
 build:
   os: "ubuntu-22.04"
   tools:
-    python: "3.9"
+    python: "3.10"
 
 sphinx:
   configuration: docs/conf.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fb30d1b..fe7c9e14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,11 +8,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 Note that in line with [Django REST framework policy](https://www.django-rest-framework.org/topics/release-notes/),
 any parts of the framework not mentioned in the documentation should generally be considered private API, and may be subject to change.
 
-## [Unreleased]
+## [8.0.0] - 2025-07-24
+
+### Added
+
+* Added support for Django REST framework 3.16.
+* Added support for Django 5.2.
+
+### Fixed
+
+* Ensured that compound documents' `include` query parameter fully support `JSON_API_FORMAT_FIELD_NAMES`.
+* Ensured that sparse fieldset's `fields` query parameter fully supports `JSON_API_FORMAT_FIELD_NAMES`.
+
+### Changed
+
+* Set minimum required version of optional Polymorphic Models for Django to 4.0.0.
+
+### Removed
+
+* Removed support for Python 3.8.
+* Removed support for Django REST framework 3.14.
+* Removed support for Django 5.0.
+* Removed built-in support for generating OpenAPI schema. Use [drf-spectacular-json-api](https://github.com/jokiefer/drf-spectacular-json-api/) instead.
+
+## [7.1.0] - 2024-10-25
+
+This is the last release supporting Python 3.8, Django 5.0 and Django REST framework 3.14.
 
 ### Fixed
 
 * Handled zero as a valid ID for resource (regression since 6.1.0)
+* Ensured that patching a To-Many relationship with the `RelationshipView` correctly raises request error when passing in `None`.
+  For emptying a To-Many relationship an empty array should be used as per [JSON:API spec](https://jsonapi.org/format/#crud-updating-to-many-relationships)
+
+### Added
+
+* Added support for Django 5.1
+* Added support for Python 3.13
+
+### Deprecated
+
+* Deprecated built-in support for generating OpenAPI schema. Use [drf-spectacular-json-api](https://github.com/jokiefer/drf-spectacular-json-api/) instead.
 
 ## [7.0.2] - 2024-06-28
 
diff --git a/README.rst b/README.rst
index fd340af6..05c01c04 100644
--- a/README.rst
+++ b/README.rst
@@ -92,9 +92,9 @@ As a Django REST framework JSON:API (short DJA) we are trying to address followi
 Requirements
 ------------
 
-1. Python (3.8, 3.9, 3.10, 3.11, 3.12)
-2. Django (4.2, 5.0)
-3. Django REST framework (3.14, 3.15)
+1. Python (3.9, 3.10, 3.11, 3.12, 3.13)
+2. Django (4.2, 5.1, 5.2)
+3. Django REST framework (3.15, 3.16)
 
 We **highly** recommend and only officially support the latest patch release of each Python, Django and REST framework series.
 
@@ -114,7 +114,6 @@ Install using ``pip``...
     $ # for optional package integrations
     $ pip install djangorestframework-jsonapi['django-filter']
     $ pip install djangorestframework-jsonapi['django-polymorphic']
-    $ pip install djangorestframework-jsonapi['openapi']
 
 
 or from source...
@@ -149,15 +148,13 @@ installed and activated:
     $ git clone https://github.com/django-json-api/django-rest-framework-json-api.git
     $ cd django-rest-framework-json-api
     $ pip install -Ur requirements.txt
-    $ django-admin migrate --settings=example.settings
-    $ django-admin loaddata drf_example --settings=example.settings
-    $ django-admin runserver --settings=example.settings
+    $ django-admin migrate --settings=example.settings --pythonpath .
+    $ django-admin loaddata drf_example --settings=example.settings --pythonpath .
+    $ django-admin runserver --settings=example.settings --pythonpath .
 
 Browse to
 
 * http://localhost:8000 for the list of available collections (in a non-JSON:API format!),
-* http://localhost:8000/swagger-ui/ for a Swagger user interface to the dynamic schema view, or
-* http://localhost:8000/openapi for the schema view's OpenAPI specification document.
 
 
 -----
diff --git a/docs/getting-started.md b/docs/getting-started.md
index fd70c79a..81040e8e 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -51,9 +51,9 @@ like the following:
 
 ## Requirements
 
-1. Python (3.8, 3.9, 3.10, 3.11, 3.12)
-2. Django (4.2, 5.0)
-3. Django REST framework (3.14, 3.15)
+1. Python (3.9, 3.10, 3.11, 3.12, 3.13)
+2. Django (4.2, 5.1, 5.2)
+3. Django REST framework (3.15, 3.16)
 
 We **highly** recommend and only officially support the latest patch release of each Python, Django and REST framework series.
 
@@ -69,7 +69,6 @@ Install using `pip`...
     # for optional package integrations
     pip install djangorestframework-jsonapi['django-filter']
     pip install djangorestframework-jsonapi['django-polymorphic']
-    pip install djangorestframework-jsonapi['openapi']
 
 or from source...
 
@@ -100,8 +99,6 @@ and add `rest_framework_json_api` to your `INSTALLED_APPS` setting below `rest_f
 
 Browse to
 * [http://localhost:8000](http://localhost:8000) for the list of available collections (in a non-JSON:API format!),
-* [http://localhost:8000/swagger-ui/](http://localhost:8000/swagger-ui/) for a Swagger user interface to the dynamic schema view, or
-* [http://localhost:8000/openapi](http://localhost:8000/openapi) for the schema view's OpenAPI specification document.
 
 ## Running Tests
 
diff --git a/docs/usage.md b/docs/usage.md
index a50a97f7..00c12ee8 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -31,7 +31,6 @@ REST_FRAMEWORK = {
         'rest_framework_json_api.renderers.BrowsableAPIRenderer'
     ),
     'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata',
-    'DEFAULT_SCHEMA_CLASS': 'rest_framework_json_api.schemas.openapi.AutoSchema',
     'DEFAULT_FILTER_BACKENDS': (
         'rest_framework_json_api.filters.QueryParameterValidationFilter',
         'rest_framework_json_api.filters.OrderingFilter',
@@ -1055,125 +1054,6 @@ The `prefetch_related` case will issue 4 queries, but they will be small and fas
 ### Errors
 -->
 
-## Generating an OpenAPI Specification (OAS) 3.0 schema document
-
-DRF has a [OAS schema functionality](https://www.django-rest-framework.org/api-guide/schemas/) to generate an
-[OAS 3.0 schema](https://www.openapis.org/) as a YAML or JSON file.
-
-DJA extends DRF's schema support to generate an OAS schema in the JSON:API format.
-
-### AutoSchema Settings
-
-In order to produce an OAS schema that properly represents the JSON:API structure
-you have to either add a `schema` attribute to each view class or set the `REST_FRAMEWORK['DEFAULT_SCHEMA_CLASS']`
-to DJA's version of AutoSchema.
-
-#### View-based
-
-```python
-from rest_framework_json_api.schemas.openapi import AutoSchema
-
-class MyViewset(ModelViewSet):
-    schema = AutoSchema
-    ...
-```
-
-#### Default schema class
-
-```python
-REST_FRAMEWORK = {
-    # ...
-    'DEFAULT_SCHEMA_CLASS': 'rest_framework_json_api.schemas.openapi.AutoSchema',
-}
-```
-
-### Adding additional OAS schema content
-
-You can extend the OAS schema document by subclassing
-[`SchemaGenerator`](https://www.django-rest-framework.org/api-guide/schemas/#schemagenerator)
-and extending `get_schema`.
-
-
-Here's an example that adds OAS `info` and `servers` objects.
-
-```python
-from rest_framework_json_api.schemas.openapi import SchemaGenerator as JSONAPISchemaGenerator
-
-
-class MySchemaGenerator(JSONAPISchemaGenerator):
-    """
-    Describe my OAS schema info in detail (overriding what DRF put in) and list the servers where it can be found.
-    """
-    def get_schema(self, request, public):
-        schema = super().get_schema(request, public)
-        schema['info'] = {
-            'version': '1.0',
-            'title': 'my demo API',
-            'description': 'A demonstration of [OAS 3.0](https://www.openapis.org)',
-            'contact': {
-                'name': 'my name'
-            },
-            'license': {
-                'name': 'BSD 2 clause',
-                'url': 'https://github.com/django-json-api/django-rest-framework-json-api/blob/main/LICENSE',
-            }
-        }
-        schema['servers'] = [
-            {'url': 'http://localhost/v1', 'description': 'local docker'},
-            {'url': 'http://localhost:8000/v1', 'description': 'local dev'},
-            {'url': 'https://api.example.com/v1', 'description': 'demo server'},
-            {'url': '{serverURL}', 'description': 'provide your server URL',
-             'variables': {'serverURL': {'default': 'http://localhost:8000/v1'}}}
-        ]
-        return schema
-```
-
-### Generate a Static Schema on Command Line
-
-See [DRF documentation for generateschema](https://www.django-rest-framework.org/api-guide/schemas/#generating-a-static-schema-with-the-generateschema-management-command)
-To generate a static OAS schema document, using the `generateschema` management command, you **must override DRF's default** `generator_class` with the DJA-specific version:
-
-```text
-$ ./manage.py generateschema --generator_class rest_framework_json_api.schemas.openapi.SchemaGenerator
-```
-
-You can then use any number of OAS tools such as
-[swagger-ui-watcher](https://www.npmjs.com/package/swagger-ui-watcher)
-to render the schema:
-```text
-$ swagger-ui-watcher myschema.yaml
-```
-
-Note: Swagger-ui-watcher will complain that "DELETE operations cannot have a requestBody"
-but it will still work. This [error](https://github.com/OAI/OpenAPI-Specification/pull/2117)
-in the OAS specification will be fixed when [OAS 3.1.0](https://www.openapis.org/blog/2020/06/18/openapi-3-1-0-rc0-its-here)
-is published.
-
-([swagger-ui](https://www.npmjs.com/package/swagger-ui) will work silently.)
-
-### Generate a Dynamic Schema in a View
-
-See [DRF documentation for a Dynamic Schema](https://www.django-rest-framework.org/api-guide/schemas/#generating-a-dynamic-schema-with-schemaview).
-
-```python
-from rest_framework.schemas import get_schema_view
-
-urlpatterns = [
-    ...
-    path('openapi', get_schema_view(
-        title="Example API",
-        description="API for all things …",
-        version="1.0.0",
-        generator_class=MySchemaGenerator,
-    ), name='openapi-schema'),
-    path('swagger-ui/', TemplateView.as_view(
-        template_name='swagger-ui.html',
-        extra_context={'schema_url': 'openapi-schema'}
-    ), name='swagger-ui'),
-    ...
-]
-```
-
 ## Third Party Packages
 
 ### About Third Party Packages
@@ -1187,3 +1067,8 @@ We aim to make creating third party packages as easy as possible, whilst keeping
 To submit new content, [open an issue](https://github.com/django-json-api/django-rest-framework-json-api/issues/new/choose) or [create a pull request](https://github.com/django-json-api/django-rest-framework-json-api/compare).
 
 * [drf-yasg-json-api](https://github.com/glowka/drf-yasg-json-api) - Automated generation of Swagger/OpenAPI 2.0 from Django REST framework JSON:API endpoints.
+* [drf-spectacular-json-api] - OpenAPI 3 schema generator for Django REST framework JSON:API based on drf-spectacular.
+
+
+
+[drf-spectacular-json-api]: https://github.com/jokiefer/drf-spectacular-json-api/
diff --git a/example/requirements.txt b/example/requirements.txt
deleted file mode 100644
index 2f4213ee..00000000
--- a/example/requirements.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-# Requirements specifically for the example app
-Django>=1.11
-django-debug-toolbar
-django-polymorphic>=2.0
-djangorestframework
-inflection
-pluggy
-py
-pyparsing
-pytz
-sqlparse
-django-filter>=2.0
diff --git a/example/settings/dev.py b/example/settings/dev.py
index 7b40e61f..05cab4d1 100644
--- a/example/settings/dev.py
+++ b/example/settings/dev.py
@@ -83,7 +83,6 @@
         "rest_framework_json_api.renderers.BrowsableAPIRenderer",
     ),
     "DEFAULT_METADATA_CLASS": "rest_framework_json_api.metadata.JSONAPIMetadata",
-    "DEFAULT_SCHEMA_CLASS": "rest_framework_json_api.schemas.openapi.AutoSchema",
     "DEFAULT_FILTER_BACKENDS": (
         "rest_framework_json_api.filters.OrderingFilter",
         "rest_framework_json_api.django_filters.DjangoFilterBackend",
diff --git a/example/templates/swagger-ui.html b/example/templates/swagger-ui.html
deleted file mode 100644
index 29776491..00000000
--- a/example/templates/swagger-ui.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>Swagger</title>
-    <meta charset="utf-8"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
-  </head>
-  <body>
-    <div id="swagger-ui"></div>
-    <script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
-    <script>
-    const ui = SwaggerUIBundle({
-        url: "{% url schema_url %}",
-        dom_id: '#swagger-ui',
-        presets: [
-          SwaggerUIBundle.presets.apis,
-          SwaggerUIBundle.SwaggerUIStandalonePreset
-        ],
-        layout: "BaseLayout",
-        requestInterceptor: (request) => {
-          request.headers['X-CSRFToken'] = "{{ csrf_token }}"
-          return request;
-        }
-      })
-    </script>
-  </body>
-</html>
\ No newline at end of file
diff --git a/example/tests/__snapshots__/test_openapi.ambr b/example/tests/__snapshots__/test_openapi.ambr
deleted file mode 100644
index f72c6ff8..00000000
--- a/example/tests/__snapshots__/test_openapi.ambr
+++ /dev/null
@@ -1,1414 +0,0 @@
-# serializer version: 1
-# name: test_delete_request
-  '''
-  {
-    "description": "",
-    "operationId": "destroy/authors/{id}",
-    "parameters": [
-      {
-        "description": "A unique integer value identifying this author.",
-        "in": "path",
-        "name": "id",
-        "required": true,
-        "schema": {
-          "type": "string"
-        }
-      }
-    ],
-    "responses": {
-      "200": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/onlymeta"
-            }
-          }
-        },
-        "description": "[OK](https://jsonapi.org/format/#crud-deleting-responses-200)"
-      },
-      "202": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/datum"
-            }
-          }
-        },
-        "description": "Accepted for [asynchronous processing](https://jsonapi.org/recommendations/#asynchronous-processing)"
-      },
-      "204": {
-        "description": "[no content](https://jsonapi.org/format/#crud-deleting-responses-204)"
-      },
-      "400": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "bad request"
-      },
-      "401": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "not authorized"
-      },
-      "404": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "[Resource does not exist](https://jsonapi.org/format/#crud-deleting-responses-404)"
-      },
-      "429": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "too many requests"
-      }
-    },
-    "tags": [
-      "authors"
-    ]
-  }
-  '''
-# ---
-# name: test_patch_request
-  '''
-  {
-    "description": "",
-    "operationId": "update/authors/{id}",
-    "parameters": [
-      {
-        "description": "A unique integer value identifying this author.",
-        "in": "path",
-        "name": "id",
-        "required": true,
-        "schema": {
-          "type": "string"
-        }
-      }
-    ],
-    "requestBody": {
-      "content": {
-        "application/vnd.api+json": {
-          "schema": {
-            "properties": {
-              "data": {
-                "additionalProperties": false,
-                "properties": {
-                  "attributes": {
-                    "properties": {
-                      "defaults": {
-                        "default": "default",
-                        "description": "help for defaults",
-                        "maxLength": 20,
-                        "minLength": 3,
-                        "type": "string",
-                        "writeOnly": true
-                      },
-                      "email": {
-                        "format": "email",
-                        "maxLength": 254,
-                        "type": "string"
-                      },
-                      "fullName": {
-                        "maxLength": 50,
-                        "type": "string"
-                      },
-                      "name": {
-                        "maxLength": 50,
-                        "type": "string"
-                      }
-                    },
-                    "type": "object"
-                  },
-                  "id": {
-                    "$ref": "#/components/schemas/id"
-                  },
-                  "links": {
-                    "properties": {
-                      "self": {
-                        "$ref": "#/components/schemas/link"
-                      }
-                    },
-                    "type": "object"
-                  },
-                  "relationships": {
-                    "properties": {
-                      "authorType": {
-                        "$ref": "#/components/schemas/reltoone"
-                      },
-                      "bio": {
-                        "$ref": "#/components/schemas/reltoone"
-                      },
-                      "comments": {
-                        "$ref": "#/components/schemas/reltomany"
-                      },
-                      "entries": {
-                        "$ref": "#/components/schemas/reltomany"
-                      },
-                      "firstEntry": {
-                        "$ref": "#/components/schemas/reltoone"
-                      }
-                    },
-                    "type": "object"
-                  },
-                  "type": {
-                    "$ref": "#/components/schemas/type"
-                  }
-                },
-                "required": [
-                  "type",
-                  "id"
-                ],
-                "type": "object"
-              }
-            },
-            "required": [
-              "data"
-            ]
-          }
-        }
-      }
-    },
-    "responses": {
-      "200": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "properties": {
-                "data": {
-                  "$ref": "#/components/schemas/Author"
-                },
-                "included": {
-                  "items": {
-                    "$ref": "#/components/schemas/include"
-                  },
-                  "type": "array",
-                  "uniqueItems": true
-                },
-                "jsonapi": {
-                  "$ref": "#/components/schemas/jsonapi"
-                },
-                "links": {
-                  "allOf": [
-                    {
-                      "$ref": "#/components/schemas/links"
-                    },
-                    {
-                      "$ref": "#/components/schemas/pagination"
-                    }
-                  ],
-                  "description": "Link members related to primary data"
-                }
-              },
-              "required": [
-                "data"
-              ],
-              "type": "object"
-            }
-          }
-        },
-        "description": "update/authors/{id}"
-      },
-      "400": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "bad request"
-      },
-      "401": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "not authorized"
-      },
-      "403": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "[Forbidden](https://jsonapi.org/format/#crud-updating-responses-403)"
-      },
-      "404": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "[Related resource does not exist](https://jsonapi.org/format/#crud-updating-responses-404)"
-      },
-      "409": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "[Conflict]([Conflict](https://jsonapi.org/format/#crud-updating-responses-409)"
-      },
-      "429": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "too many requests"
-      }
-    },
-    "tags": [
-      "authors"
-    ]
-  }
-  '''
-# ---
-# name: test_path_with_id_parameter
-  '''
-  {
-    "description": "",
-    "operationId": "retrieve/authors/{id}/",
-    "parameters": [
-      {
-        "description": "A unique integer value identifying this author.",
-        "in": "path",
-        "name": "id",
-        "required": true,
-        "schema": {
-          "type": "string"
-        }
-      },
-      {
-        "$ref": "#/components/parameters/include"
-      },
-      {
-        "$ref": "#/components/parameters/fields"
-      },
-      {
-        "description": "[list of fields to sort by](https://jsonapi.org/format/#fetching-sorting)",
-        "in": "query",
-        "name": "sort",
-        "required": false,
-        "schema": {
-          "type": "string"
-        }
-      },
-      {
-        "description": "author_type",
-        "in": "query",
-        "name": "filter[authorType]",
-        "required": false,
-        "schema": {
-          "type": "string"
-        }
-      },
-      {
-        "description": "name",
-        "in": "query",
-        "name": "filter[name]",
-        "required": false,
-        "schema": {
-          "type": "string"
-        }
-      },
-      {
-        "description": "A search term.",
-        "in": "query",
-        "name": "filter[search]",
-        "required": false,
-        "schema": {
-          "type": "string"
-        }
-      }
-    ],
-    "responses": {
-      "200": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "properties": {
-                "data": {
-                  "$ref": "#/components/schemas/AuthorDetail"
-                },
-                "included": {
-                  "items": {
-                    "$ref": "#/components/schemas/include"
-                  },
-                  "type": "array",
-                  "uniqueItems": true
-                },
-                "jsonapi": {
-                  "$ref": "#/components/schemas/jsonapi"
-                },
-                "links": {
-                  "allOf": [
-                    {
-                      "$ref": "#/components/schemas/links"
-                    },
-                    {
-                      "$ref": "#/components/schemas/pagination"
-                    }
-                  ],
-                  "description": "Link members related to primary data"
-                }
-              },
-              "required": [
-                "data"
-              ],
-              "type": "object"
-            }
-          }
-        },
-        "description": "retrieve/authors/{id}/"
-      },
-      "400": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "bad request"
-      },
-      "401": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "not authorized"
-      },
-      "404": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "not found"
-      },
-      "429": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "too many requests"
-      }
-    },
-    "tags": [
-      "authors"
-    ]
-  }
-  '''
-# ---
-# name: test_path_without_parameters
-  '''
-  {
-    "description": "",
-    "operationId": "List/authors/",
-    "parameters": [
-      {
-        "$ref": "#/components/parameters/include"
-      },
-      {
-        "$ref": "#/components/parameters/fields"
-      },
-      {
-        "description": "A page number within the paginated result set.",
-        "in": "query",
-        "name": "page[number]",
-        "required": false,
-        "schema": {
-          "type": "integer"
-        }
-      },
-      {
-        "description": "Number of results to return per page.",
-        "in": "query",
-        "name": "page[size]",
-        "required": false,
-        "schema": {
-          "type": "integer"
-        }
-      },
-      {
-        "description": "[list of fields to sort by](https://jsonapi.org/format/#fetching-sorting)",
-        "in": "query",
-        "name": "sort",
-        "required": false,
-        "schema": {
-          "type": "string"
-        }
-      },
-      {
-        "description": "author_type",
-        "in": "query",
-        "name": "filter[authorType]",
-        "required": false,
-        "schema": {
-          "type": "string"
-        }
-      },
-      {
-        "description": "name",
-        "in": "query",
-        "name": "filter[name]",
-        "required": false,
-        "schema": {
-          "type": "string"
-        }
-      },
-      {
-        "description": "A search term.",
-        "in": "query",
-        "name": "filter[search]",
-        "required": false,
-        "schema": {
-          "type": "string"
-        }
-      }
-    ],
-    "responses": {
-      "200": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "properties": {
-                "data": {
-                  "items": {
-                    "$ref": "#/components/schemas/AuthorList"
-                  },
-                  "type": "array"
-                },
-                "included": {
-                  "items": {
-                    "$ref": "#/components/schemas/include"
-                  },
-                  "type": "array",
-                  "uniqueItems": true
-                },
-                "jsonapi": {
-                  "$ref": "#/components/schemas/jsonapi"
-                },
-                "links": {
-                  "allOf": [
-                    {
-                      "$ref": "#/components/schemas/links"
-                    },
-                    {
-                      "$ref": "#/components/schemas/pagination"
-                    }
-                  ],
-                  "description": "Link members related to primary data"
-                }
-              },
-              "required": [
-                "data"
-              ],
-              "type": "object"
-            }
-          }
-        },
-        "description": "List/authors/"
-      },
-      "400": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "bad request"
-      },
-      "401": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "not authorized"
-      },
-      "404": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "not found"
-      },
-      "429": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "too many requests"
-      }
-    },
-    "tags": [
-      "authors"
-    ]
-  }
-  '''
-# ---
-# name: test_post_request
-  '''
-  {
-    "description": "",
-    "operationId": "create/authors/",
-    "parameters": [],
-    "requestBody": {
-      "content": {
-        "application/vnd.api+json": {
-          "schema": {
-            "properties": {
-              "data": {
-                "additionalProperties": false,
-                "properties": {
-                  "attributes": {
-                    "properties": {
-                      "defaults": {
-                        "default": "default",
-                        "description": "help for defaults",
-                        "maxLength": 20,
-                        "minLength": 3,
-                        "type": "string",
-                        "writeOnly": true
-                      },
-                      "email": {
-                        "format": "email",
-                        "maxLength": 254,
-                        "type": "string"
-                      },
-                      "fullName": {
-                        "maxLength": 50,
-                        "type": "string"
-                      },
-                      "name": {
-                        "maxLength": 50,
-                        "type": "string"
-                      }
-                    },
-                    "required": [
-                      "name",
-                      "fullName",
-                      "email"
-                    ],
-                    "type": "object"
-                  },
-                  "id": {
-                    "$ref": "#/components/schemas/id"
-                  },
-                  "links": {
-                    "properties": {
-                      "self": {
-                        "$ref": "#/components/schemas/link"
-                      }
-                    },
-                    "type": "object"
-                  },
-                  "relationships": {
-                    "properties": {
-                      "authorType": {
-                        "$ref": "#/components/schemas/reltoone"
-                      },
-                      "bio": {
-                        "$ref": "#/components/schemas/reltoone"
-                      },
-                      "comments": {
-                        "$ref": "#/components/schemas/reltomany"
-                      },
-                      "entries": {
-                        "$ref": "#/components/schemas/reltomany"
-                      },
-                      "firstEntry": {
-                        "$ref": "#/components/schemas/reltoone"
-                      }
-                    },
-                    "required": [
-                      "bio",
-                      "entries",
-                      "comments"
-                    ],
-                    "type": "object"
-                  },
-                  "type": {
-                    "$ref": "#/components/schemas/type"
-                  }
-                },
-                "required": [
-                  "type"
-                ],
-                "type": "object"
-              }
-            },
-            "required": [
-              "data"
-            ]
-          }
-        }
-      }
-    },
-    "responses": {
-      "201": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "properties": {
-                "data": {
-                  "$ref": "#/components/schemas/Author"
-                },
-                "included": {
-                  "items": {
-                    "$ref": "#/components/schemas/include"
-                  },
-                  "type": "array",
-                  "uniqueItems": true
-                },
-                "jsonapi": {
-                  "$ref": "#/components/schemas/jsonapi"
-                },
-                "links": {
-                  "allOf": [
-                    {
-                      "$ref": "#/components/schemas/links"
-                    },
-                    {
-                      "$ref": "#/components/schemas/pagination"
-                    }
-                  ],
-                  "description": "Link members related to primary data"
-                }
-              },
-              "required": [
-                "data"
-              ],
-              "type": "object"
-            }
-          }
-        },
-        "description": "[Created](https://jsonapi.org/format/#crud-creating-responses-201). Assigned `id` and/or any other changes are in this response."
-      },
-      "202": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/datum"
-            }
-          }
-        },
-        "description": "Accepted for [asynchronous processing](https://jsonapi.org/recommendations/#asynchronous-processing)"
-      },
-      "204": {
-        "description": "[Created](https://jsonapi.org/format/#crud-creating-responses-204) with the supplied `id`. No other changes from what was POSTed."
-      },
-      "400": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "bad request"
-      },
-      "401": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "not authorized"
-      },
-      "403": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "[Forbidden](https://jsonapi.org/format/#crud-creating-responses-403)"
-      },
-      "404": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "[Related resource does not exist](https://jsonapi.org/format/#crud-creating-responses-404)"
-      },
-      "409": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "[Conflict](https://jsonapi.org/format/#crud-creating-responses-409)"
-      },
-      "429": {
-        "content": {
-          "application/vnd.api+json": {
-            "schema": {
-              "$ref": "#/components/schemas/failure"
-            }
-          }
-        },
-        "description": "too many requests"
-      }
-    },
-    "tags": [
-      "authors"
-    ]
-  }
-  '''
-# ---
-# name: test_schema_construction
-  '''
-  {
-    "components": {
-      "parameters": {
-        "fields": {
-          "description": "[sparse fieldsets](https://jsonapi.org/format/#fetching-sparse-fieldsets).\nUse fields[\\<typename\\>]=field1,field2,...,fieldN",
-          "explode": true,
-          "in": "query",
-          "name": "fields",
-          "required": false,
-          "schema": {
-            "type": "object"
-          },
-          "style": "deepObject"
-        },
-        "include": {
-          "description": "[list of included related resources](https://jsonapi.org/format/#fetching-includes)",
-          "in": "query",
-          "name": "include",
-          "required": false,
-          "schema": {
-            "type": "string"
-          },
-          "style": "form"
-        }
-      },
-      "schemas": {
-        "AuthorList": {
-          "additionalProperties": false,
-          "properties": {
-            "attributes": {
-              "properties": {
-                "defaults": {
-                  "default": "default",
-                  "description": "help for defaults",
-                  "maxLength": 20,
-                  "minLength": 3,
-                  "type": "string",
-                  "writeOnly": true
-                },
-                "email": {
-                  "format": "email",
-                  "maxLength": 254,
-                  "type": "string"
-                },
-                "fullName": {
-                  "maxLength": 50,
-                  "type": "string"
-                },
-                "initials": {
-                  "readOnly": true,
-                  "type": "string"
-                },
-                "name": {
-                  "maxLength": 50,
-                  "type": "string"
-                }
-              },
-              "required": [
-                "name",
-                "fullName",
-                "email"
-              ],
-              "type": "object"
-            },
-            "id": {
-              "$ref": "#/components/schemas/id"
-            },
-            "links": {
-              "properties": {
-                "self": {
-                  "$ref": "#/components/schemas/link"
-                }
-              },
-              "type": "object"
-            },
-            "relationships": {
-              "properties": {
-                "authorType": {
-                  "$ref": "#/components/schemas/reltoone"
-                },
-                "bio": {
-                  "$ref": "#/components/schemas/reltoone"
-                },
-                "comments": {
-                  "$ref": "#/components/schemas/reltomany"
-                },
-                "entries": {
-                  "$ref": "#/components/schemas/reltomany"
-                },
-                "firstEntry": {
-                  "$ref": "#/components/schemas/reltoone"
-                }
-              },
-              "required": [
-                "bio",
-                "entries",
-                "comments"
-              ],
-              "type": "object"
-            },
-            "type": {
-              "$ref": "#/components/schemas/type"
-            }
-          },
-          "required": [
-            "type",
-            "id"
-          ],
-          "type": "object"
-        },
-        "ResourceIdentifierObject": {
-          "oneOf": [
-            {
-              "$ref": "#/components/schemas/relationshipToOne"
-            },
-            {
-              "$ref": "#/components/schemas/relationshipToMany"
-            }
-          ]
-        },
-        "datum": {
-          "description": "singular item",
-          "properties": {
-            "data": {
-              "$ref": "#/components/schemas/resource"
-            }
-          }
-        },
-        "error": {
-          "additionalProperties": false,
-          "properties": {
-            "code": {
-              "type": "string"
-            },
-            "detail": {
-              "type": "string"
-            },
-            "id": {
-              "type": "string"
-            },
-            "links": {
-              "$ref": "#/components/schemas/links"
-            },
-            "source": {
-              "properties": {
-                "meta": {
-                  "$ref": "#/components/schemas/meta"
-                },
-                "parameter": {
-                  "description": "A string indicating which query parameter caused the error.",
-                  "type": "string"
-                },
-                "pointer": {
-                  "description": "A [JSON Pointer](https://tools.ietf.org/html/rfc6901) to the associated entity in the request document [e.g. `/data` for a primary data object, or `/data/attributes/title` for a specific attribute.",
-                  "type": "string"
-                }
-              },
-              "type": "object"
-            },
-            "status": {
-              "type": "string"
-            },
-            "title": {
-              "type": "string"
-            }
-          },
-          "type": "object"
-        },
-        "errors": {
-          "items": {
-            "$ref": "#/components/schemas/error"
-          },
-          "type": "array",
-          "uniqueItems": true
-        },
-        "failure": {
-          "properties": {
-            "errors": {
-              "$ref": "#/components/schemas/errors"
-            },
-            "jsonapi": {
-              "$ref": "#/components/schemas/jsonapi"
-            },
-            "links": {
-              "$ref": "#/components/schemas/links"
-            },
-            "meta": {
-              "$ref": "#/components/schemas/meta"
-            }
-          },
-          "required": [
-            "errors"
-          ],
-          "type": "object"
-        },
-        "id": {
-          "description": "Each resource object\u2019s type and id pair MUST [identify](https://jsonapi.org/format/#document-resource-object-identification) a single, unique resource.",
-          "type": "string"
-        },
-        "include": {
-          "additionalProperties": false,
-          "properties": {
-            "attributes": {
-              "additionalProperties": true,
-              "type": "object"
-            },
-            "id": {
-              "$ref": "#/components/schemas/id"
-            },
-            "links": {
-              "$ref": "#/components/schemas/links"
-            },
-            "meta": {
-              "$ref": "#/components/schemas/meta"
-            },
-            "relationships": {
-              "additionalProperties": true,
-              "type": "object"
-            },
-            "type": {
-              "$ref": "#/components/schemas/type"
-            }
-          },
-          "required": [
-            "type",
-            "id"
-          ],
-          "type": "object"
-        },
-        "jsonapi": {
-          "additionalProperties": false,
-          "description": "The server's implementation",
-          "properties": {
-            "meta": {
-              "$ref": "#/components/schemas/meta"
-            },
-            "version": {
-              "type": "string"
-            }
-          },
-          "type": "object"
-        },
-        "link": {
-          "oneOf": [
-            {
-              "description": "a string containing the link's URL",
-              "format": "uri-reference",
-              "type": "string"
-            },
-            {
-              "properties": {
-                "href": {
-                  "description": "a string containing the link's URL",
-                  "format": "uri-reference",
-                  "type": "string"
-                },
-                "meta": {
-                  "$ref": "#/components/schemas/meta"
-                }
-              },
-              "required": [
-                "href"
-              ],
-              "type": "object"
-            }
-          ]
-        },
-        "linkage": {
-          "description": "the 'type' and 'id'",
-          "properties": {
-            "id": {
-              "$ref": "#/components/schemas/id"
-            },
-            "meta": {
-              "$ref": "#/components/schemas/meta"
-            },
-            "type": {
-              "$ref": "#/components/schemas/type"
-            }
-          },
-          "required": [
-            "type",
-            "id"
-          ],
-          "type": "object"
-        },
-        "links": {
-          "additionalProperties": {
-            "$ref": "#/components/schemas/link"
-          },
-          "type": "object"
-        },
-        "meta": {
-          "additionalProperties": true,
-          "type": "object"
-        },
-        "nulltype": {
-          "default": null,
-          "nullable": true,
-          "type": "object"
-        },
-        "onlymeta": {
-          "additionalProperties": false,
-          "properties": {
-            "meta": {
-              "$ref": "#/components/schemas/meta"
-            }
-          }
-        },
-        "pageref": {
-          "oneOf": [
-            {
-              "format": "uri-reference",
-              "type": "string"
-            },
-            {
-              "$ref": "#/components/schemas/nulltype"
-            }
-          ]
-        },
-        "pagination": {
-          "properties": {
-            "first": {
-              "$ref": "#/components/schemas/pageref"
-            },
-            "last": {
-              "$ref": "#/components/schemas/pageref"
-            },
-            "next": {
-              "$ref": "#/components/schemas/pageref"
-            },
-            "prev": {
-              "$ref": "#/components/schemas/pageref"
-            }
-          },
-          "type": "object"
-        },
-        "relationshipLinks": {
-          "additionalProperties": true,
-          "description": "optional references to other resource objects",
-          "properties": {
-            "related": {
-              "$ref": "#/components/schemas/link"
-            },
-            "self": {
-              "$ref": "#/components/schemas/link"
-            }
-          },
-          "type": "object"
-        },
-        "relationshipToMany": {
-          "description": "An array of objects each containing the 'type' and 'id' for to-many relationships",
-          "items": {
-            "$ref": "#/components/schemas/linkage"
-          },
-          "type": "array",
-          "uniqueItems": true
-        },
-        "relationshipToOne": {
-          "anyOf": [
-            {
-              "$ref": "#/components/schemas/nulltype"
-            },
-            {
-              "$ref": "#/components/schemas/linkage"
-            }
-          ],
-          "description": "reference to other resource in a to-one relationship"
-        },
-        "reltomany": {
-          "description": "a multiple 'to-many' relationship",
-          "properties": {
-            "data": {
-              "$ref": "#/components/schemas/relationshipToMany"
-            },
-            "links": {
-              "$ref": "#/components/schemas/relationshipLinks"
-            },
-            "meta": {
-              "$ref": "#/components/schemas/meta"
-            }
-          },
-          "type": "object"
-        },
-        "reltoone": {
-          "description": "a singular 'to-one' relationship",
-          "properties": {
-            "data": {
-              "$ref": "#/components/schemas/relationshipToOne"
-            },
-            "links": {
-              "$ref": "#/components/schemas/relationshipLinks"
-            },
-            "meta": {
-              "$ref": "#/components/schemas/meta"
-            }
-          },
-          "type": "object"
-        },
-        "resource": {
-          "additionalProperties": false,
-          "properties": {
-            "attributes": {
-              "type": "object"
-            },
-            "id": {
-              "$ref": "#/components/schemas/id"
-            },
-            "links": {
-              "$ref": "#/components/schemas/links"
-            },
-            "meta": {
-              "$ref": "#/components/schemas/meta"
-            },
-            "relationships": {
-              "type": "object"
-            },
-            "type": {
-              "$ref": "#/components/schemas/type"
-            }
-          },
-          "required": [
-            "type",
-            "id"
-          ],
-          "type": "object"
-        },
-        "type": {
-          "description": "The [type](https://jsonapi.org/format/#document-resource-object-identification) member is used to describe resource objects that share common attributes and relationships.",
-          "type": "string"
-        }
-      }
-    },
-    "info": {
-      "title": "",
-      "version": ""
-    },
-    "openapi": "3.0.2",
-    "paths": {
-      "/authors/": {
-        "get": {
-          "description": "",
-          "operationId": "List/authors/",
-          "parameters": [
-            {
-              "$ref": "#/components/parameters/include"
-            },
-            {
-              "$ref": "#/components/parameters/fields"
-            },
-            {
-              "description": "A page number within the paginated result set.",
-              "in": "query",
-              "name": "page[number]",
-              "required": false,
-              "schema": {
-                "type": "integer"
-              }
-            },
-            {
-              "description": "Number of results to return per page.",
-              "in": "query",
-              "name": "page[size]",
-              "required": false,
-              "schema": {
-                "type": "integer"
-              }
-            },
-            {
-              "description": "[list of fields to sort by](https://jsonapi.org/format/#fetching-sorting)",
-              "in": "query",
-              "name": "sort",
-              "required": false,
-              "schema": {
-                "type": "string"
-              }
-            },
-            {
-              "description": "author_type",
-              "in": "query",
-              "name": "filter[authorType]",
-              "required": false,
-              "schema": {
-                "type": "string"
-              }
-            },
-            {
-              "description": "name",
-              "in": "query",
-              "name": "filter[name]",
-              "required": false,
-              "schema": {
-                "type": "string"
-              }
-            },
-            {
-              "description": "A search term.",
-              "in": "query",
-              "name": "filter[search]",
-              "required": false,
-              "schema": {
-                "type": "string"
-              }
-            }
-          ],
-          "responses": {
-            "200": {
-              "content": {
-                "application/vnd.api+json": {
-                  "schema": {
-                    "properties": {
-                      "data": {
-                        "items": {
-                          "$ref": "#/components/schemas/AuthorList"
-                        },
-                        "type": "array"
-                      },
-                      "included": {
-                        "items": {
-                          "$ref": "#/components/schemas/include"
-                        },
-                        "type": "array",
-                        "uniqueItems": true
-                      },
-                      "jsonapi": {
-                        "$ref": "#/components/schemas/jsonapi"
-                      },
-                      "links": {
-                        "allOf": [
-                          {
-                            "$ref": "#/components/schemas/links"
-                          },
-                          {
-                            "$ref": "#/components/schemas/pagination"
-                          }
-                        ],
-                        "description": "Link members related to primary data"
-                      }
-                    },
-                    "required": [
-                      "data"
-                    ],
-                    "type": "object"
-                  }
-                }
-              },
-              "description": "List/authors/"
-            },
-            "400": {
-              "content": {
-                "application/vnd.api+json": {
-                  "schema": {
-                    "$ref": "#/components/schemas/failure"
-                  }
-                }
-              },
-              "description": "bad request"
-            },
-            "401": {
-              "content": {
-                "application/vnd.api+json": {
-                  "schema": {
-                    "$ref": "#/components/schemas/failure"
-                  }
-                }
-              },
-              "description": "not authorized"
-            },
-            "404": {
-              "content": {
-                "application/vnd.api+json": {
-                  "schema": {
-                    "$ref": "#/components/schemas/failure"
-                  }
-                }
-              },
-              "description": "not found"
-            },
-            "429": {
-              "content": {
-                "application/vnd.api+json": {
-                  "schema": {
-                    "$ref": "#/components/schemas/failure"
-                  }
-                }
-              },
-              "description": "too many requests"
-            }
-          },
-          "tags": [
-            "authors"
-          ]
-        }
-      }
-    }
-  }
-  '''
-# ---
diff --git a/example/tests/test_openapi.py b/example/tests/test_openapi.py
deleted file mode 100644
index 2333dd6a..00000000
--- a/example/tests/test_openapi.py
+++ /dev/null
@@ -1,227 +0,0 @@
-# largely based on DRF's test_openapi
-import json
-
-from django.test import RequestFactory, override_settings
-from django.urls import re_path
-from rest_framework.request import Request
-
-from rest_framework_json_api.schemas.openapi import AutoSchema, SchemaGenerator
-
-from example import views
-
-
-def create_request(path):
-    factory = RequestFactory()
-    request = Request(factory.get(path))
-    return request
-
-
-def create_view_with_kw(view_cls, method, request, initkwargs):
-    generator = SchemaGenerator()
-    view = generator.create_view(view_cls.as_view(initkwargs), method, request)
-    return view
-
-
-def test_path_without_parameters(snapshot):
-    path = "/authors/"
-    method = "GET"
-
-    view = create_view_with_kw(
-        views.AuthorViewSet, method, create_request(path), {"get": "list"}
-    )
-    inspector = AutoSchema()
-    inspector.view = view
-
-    operation = inspector.get_operation(path, method)
-    assert snapshot == json.dumps(operation, indent=2, sort_keys=True)
-
-
-def test_path_with_id_parameter(snapshot):
-    path = "/authors/{id}/"
-    method = "GET"
-
-    view = create_view_with_kw(
-        views.AuthorViewSet, method, create_request(path), {"get": "retrieve"}
-    )
-    inspector = AutoSchema()
-    inspector.view = view
-
-    operation = inspector.get_operation(path, method)
-    assert snapshot == json.dumps(operation, indent=2, sort_keys=True)
-
-
-def test_post_request(snapshot):
-    method = "POST"
-    path = "/authors/"
-
-    view = create_view_with_kw(
-        views.AuthorViewSet, method, create_request(path), {"post": "create"}
-    )
-    inspector = AutoSchema()
-    inspector.view = view
-
-    operation = inspector.get_operation(path, method)
-    assert snapshot == json.dumps(operation, indent=2, sort_keys=True)
-
-
-def test_patch_request(snapshot):
-    method = "PATCH"
-    path = "/authors/{id}"
-
-    view = create_view_with_kw(
-        views.AuthorViewSet, method, create_request(path), {"patch": "update"}
-    )
-    inspector = AutoSchema()
-    inspector.view = view
-
-    operation = inspector.get_operation(path, method)
-    assert snapshot == json.dumps(operation, indent=2, sort_keys=True)
-
-
-def test_delete_request(snapshot):
-    method = "DELETE"
-    path = "/authors/{id}"
-
-    view = create_view_with_kw(
-        views.AuthorViewSet, method, create_request(path), {"delete": "delete"}
-    )
-    inspector = AutoSchema()
-    inspector.view = view
-
-    operation = inspector.get_operation(path, method)
-    assert snapshot == json.dumps(operation, indent=2, sort_keys=True)
-
-
-@override_settings(
-    REST_FRAMEWORK={
-        "DEFAULT_SCHEMA_CLASS": "rest_framework_json_api.schemas.openapi.AutoSchema"
-    }
-)
-def test_schema_construction(snapshot):
-    """Construction of the top level dictionary."""
-    patterns = [
-        re_path("^authors/?$", views.AuthorViewSet.as_view({"get": "list"})),
-    ]
-    generator = SchemaGenerator(patterns=patterns)
-
-    request = create_request("/")
-    schema = generator.get_schema(request=request)
-
-    assert snapshot == json.dumps(schema, indent=2, sort_keys=True)
-
-
-def test_schema_id_field():
-    """ID field is only included in the root, not the attributes."""
-    patterns = [
-        re_path("^companies/?$", views.CompanyViewset.as_view({"get": "list"})),
-    ]
-    generator = SchemaGenerator(patterns=patterns)
-
-    request = create_request("/")
-    schema = generator.get_schema(request=request)
-
-    company_properties = schema["components"]["schemas"]["Company"]["properties"]
-    assert company_properties["id"] == {"$ref": "#/components/schemas/id"}
-    assert "id" not in company_properties["attributes"]["properties"]
-
-
-def test_schema_subserializers():
-    """Schema for child Serializers reflects the actual response structure."""
-    patterns = [
-        re_path(
-            "^questionnaires/?$", views.QuestionnaireViewset.as_view({"get": "list"})
-        ),
-    ]
-    generator = SchemaGenerator(patterns=patterns)
-
-    request = create_request("/")
-    schema = generator.get_schema(request=request)
-
-    assert {
-        "type": "object",
-        "properties": {
-            "metadata": {
-                "type": "object",
-                "properties": {
-                    "author": {"type": "string"},
-                    "producer": {"type": "string"},
-                },
-                "required": ["author"],
-            },
-            "questions": {
-                "type": "array",
-                "items": {
-                    "type": "object",
-                    "properties": {
-                        "text": {"type": "string"},
-                        "required": {"type": "boolean", "default": False},
-                    },
-                    "required": ["text"],
-                },
-            },
-            "name": {"type": "string", "maxLength": 100},
-        },
-        "required": ["name", "questions", "metadata"],
-    } == schema["components"]["schemas"]["Questionnaire"]["properties"]["attributes"]
-
-
-def test_schema_parameters_include():
-    """Include paramater is only used when serializer defines included_serializers."""
-    patterns = [
-        re_path("^authors/?$", views.AuthorViewSet.as_view({"get": "list"})),
-        re_path("^project-types/?$", views.ProjectTypeViewset.as_view({"get": "list"})),
-    ]
-    generator = SchemaGenerator(patterns=patterns)
-
-    request = create_request("/")
-    schema = generator.get_schema(request=request)
-
-    include_ref = {"$ref": "#/components/parameters/include"}
-    assert include_ref in schema["paths"]["/authors/"]["get"]["parameters"]
-    assert include_ref not in schema["paths"]["/project-types/"]["get"]["parameters"]
-
-
-def test_schema_serializer_method_resource_related_field():
-    """SerializerMethodResourceRelatedField fieds have the correct relation ref."""
-    patterns = [
-        re_path("^entries/?$", views.EntryViewSet.as_view({"get": "list"})),
-    ]
-    generator = SchemaGenerator(patterns=patterns)
-
-    request = Request(RequestFactory().get("/", {"include": "featured"}))
-    schema = generator.get_schema(request=request)
-
-    entry_schema = schema["components"]["schemas"]["Entry"]
-    entry_relationships = entry_schema["properties"]["relationships"]["properties"]
-
-    rel_to_many_ref = {"$ref": "#/components/schemas/reltomany"}
-    assert entry_relationships["suggested"] == rel_to_many_ref
-    assert entry_relationships["suggestedHyperlinked"] == rel_to_many_ref
-
-    rel_to_one_ref = {"$ref": "#/components/schemas/reltoone"}
-    assert entry_relationships["featured"] == rel_to_one_ref
-    assert entry_relationships["featuredHyperlinked"] == rel_to_one_ref
-
-
-def test_schema_related_serializers():
-    """
-    Confirm that paths are generated for related fields. For example:
-        /authors/{pk}/{related_field>}
-        /authors/{id}/comments/
-        /authors/{id}/entries/
-        /authors/{id}/first_entry/
-    and confirm that the schema for the related field is properly rendered
-    """
-    generator = SchemaGenerator()
-    request = create_request("/")
-    schema = generator.get_schema(request=request)
-    # make sure the path's relationship and related {related_field}'s got expanded
-    assert "/authors/{id}/relationships/{related_field}" in schema["paths"]
-    assert "/authors/{id}/comments/" in schema["paths"]
-    assert "/authors/{id}/entries/" in schema["paths"]
-    assert "/authors/{id}/first_entry/" in schema["paths"]
-    first_get = schema["paths"]["/authors/{id}/first_entry/"]["get"]["responses"]["200"]
-    first_schema = first_get["content"]["application/vnd.api+json"]["schema"]
-    first_props = first_schema["properties"]["data"]
-    assert "$ref" in first_props
-    assert first_props["$ref"] == "#/components/schemas/Entry"
diff --git a/example/tests/test_views.py b/example/tests/test_views.py
index ad3fe124..47d4adb1 100644
--- a/example/tests/test_views.py
+++ b/example/tests/test_views.py
@@ -2,6 +2,7 @@
 
 from django.test import RequestFactory, override_settings
 from django.utils import timezone
+from rest_framework import status
 from rest_framework.exceptions import NotFound
 from rest_framework.request import Request
 from rest_framework.reverse import reverse
@@ -174,16 +175,30 @@ def test_patch_one_to_many_relationship(self):
         response = self.client.get(url)
         assert response.data == request_data["data"]
 
-    def test_patch_one_to_many_relaitonship_with_none(self):
+    def test_patch_one_to_many_relaitonship_with_empty(self):
         url = f"/blogs/{self.first_entry.id}/relationships/entry_set"
-        request_data = {"data": None}
+
+        request_data = {"data": []}
         response = self.client.patch(url, data=request_data)
-        assert response.status_code == 200, response.content.decode()
+        assert response.status_code == status.HTTP_200_OK
         assert response.data == []
 
         response = self.client.get(url)
         assert response.data == []
 
+    def test_patch_one_to_many_relaitonship_with_none(self):
+        """
+        None for a to many relationship is invalid and should return a request error.
+
+        see https://jsonapi.org/format/#crud-updating-to-many-relationships
+        """
+
+        url = f"/blogs/{self.first_entry.id}/relationships/entry_set"
+
+        request_data = {"data": None}
+        response = self.client.patch(url, data=request_data)
+        assert response.status_code == status.HTTP_400_BAD_REQUEST
+
     def test_patch_many_to_many_relationship(self):
         url = f"/entries/{self.first_entry.id}/relationships/authors"
         request_data = {
diff --git a/example/tests/unit/test_filter_schema_params.py b/example/tests/unit/test_filter_schema_params.py
deleted file mode 100644
index d7cb4fb8..00000000
--- a/example/tests/unit/test_filter_schema_params.py
+++ /dev/null
@@ -1,107 +0,0 @@
-from rest_framework import filters as drf_filters
-
-from rest_framework_json_api import filters as dja_filters
-from rest_framework_json_api.django_filters import backends
-
-from example.views import EntryViewSet
-
-
-class DummyEntryViewSet(EntryViewSet):
-    filter_backends = (
-        dja_filters.QueryParameterValidationFilter,
-        dja_filters.OrderingFilter,
-        backends.DjangoFilterBackend,
-        drf_filters.SearchFilter,
-    )
-    filterset_fields = {
-        "id": ("exact",),
-        "headline": ("exact", "contains"),
-        "blog__name": ("contains",),
-    }
-
-    def __init__(self, **kwargs):
-        # dummy up self.request since PreloadIncludesMixin expects it to be defined
-        self.request = None
-        super().__init__(**kwargs)
-
-
-def test_filters_get_schema_params():
-    """
-    test all my filters for `get_schema_operation_parameters()`
-    """
-    # list of tuples: (filter, expected result)
-    filters = [
-        (dja_filters.QueryParameterValidationFilter, []),
-        (
-            backends.DjangoFilterBackend,
-            [
-                {
-                    "name": "filter[id]",
-                    "required": False,
-                    "in": "query",
-                    "description": "id",
-                    "schema": {"type": "string"},
-                },
-                {
-                    "name": "filter[headline]",
-                    "required": False,
-                    "in": "query",
-                    "description": "headline",
-                    "schema": {"type": "string"},
-                },
-                {
-                    "name": "filter[headline.contains]",
-                    "required": False,
-                    "in": "query",
-                    "description": "headline__contains",
-                    "schema": {"type": "string"},
-                },
-                {
-                    "name": "filter[blog.name.contains]",
-                    "required": False,
-                    "in": "query",
-                    "description": "blog__name__contains",
-                    "schema": {"type": "string"},
-                },
-            ],
-        ),
-        (
-            dja_filters.OrderingFilter,
-            [
-                {
-                    "name": "sort",
-                    "required": False,
-                    "in": "query",
-                    "description": "[list of fields to sort by]"
-                    "(https://jsonapi.org/format/#fetching-sorting)",
-                    "schema": {"type": "string"},
-                }
-            ],
-        ),
-        (
-            drf_filters.SearchFilter,
-            [
-                {
-                    "name": "filter[search]",
-                    "required": False,
-                    "in": "query",
-                    "description": "A search term.",
-                    "schema": {"type": "string"},
-                }
-            ],
-        ),
-    ]
-    view = DummyEntryViewSet()
-
-    for c, expected in filters:
-        f = c()
-        result = f.get_schema_operation_parameters(view)
-        assert len(result) == len(expected)
-        if len(result) == 0:
-            continue
-        # py35: the result list/dict ordering isn't guaranteed
-        for res_item in result:
-            assert "name" in res_item
-            for exp_item in expected:
-                if res_item["name"] == exp_item["name"]:
-                    assert res_item == exp_item
diff --git a/example/urls.py b/example/urls.py
index 413d058d..471fbe81 100644
--- a/example/urls.py
+++ b/example/urls.py
@@ -1,9 +1,5 @@
 from django.urls import include, path, re_path
-from django.views.generic import TemplateView
 from rest_framework import routers
-from rest_framework.schemas import get_schema_view
-
-from rest_framework_json_api.schemas.openapi import SchemaGenerator
 
 from example.views import (
     AuthorRelationshipView,
@@ -87,22 +83,4 @@
         AuthorRelationshipView.as_view(),
         name="author-relationships",
     ),
-    path(
-        "openapi",
-        get_schema_view(
-            title="Example API",
-            description="API for all things …",
-            version="1.0.0",
-            generator_class=SchemaGenerator,
-        ),
-        name="openapi-schema",
-    ),
-    path(
-        "swagger-ui/",
-        TemplateView.as_view(
-            template_name="swagger-ui.html",
-            extra_context={"schema_url": "openapi-schema"},
-        ),
-        name="swagger-ui",
-    ),
 ]
diff --git a/requirements/requirements-codestyle.txt b/requirements/requirements-codestyle.txt
index d826f918..707bddb9 100644
--- a/requirements/requirements-codestyle.txt
+++ b/requirements/requirements-codestyle.txt
@@ -1,5 +1,5 @@
-black==24.8.0
-flake8==7.1.1
-flake8-bugbear==24.4.26
-flake8-isort==6.1.1
-isort==5.13.2
+black==25.1.0
+flake8==7.3.0
+flake8-bugbear==24.12.12
+flake8-isort==6.1.2
+isort==6.0.1
diff --git a/requirements/requirements-documentation.txt b/requirements/requirements-documentation.txt
index ea5200ab..76f3bf9a 100644
--- a/requirements/requirements-documentation.txt
+++ b/requirements/requirements-documentation.txt
@@ -1,3 +1,3 @@
 recommonmark==0.7.1
-Sphinx==7.4.7
-sphinx_rtd_theme==2.0.0
+Sphinx==8.1.3
+sphinx_rtd_theme==3.0.2
diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt
index df0ef24d..4eb517ff 100644
--- a/requirements/requirements-optionals.txt
+++ b/requirements/requirements-optionals.txt
@@ -1,7 +1,2 @@
-django-filter==24.3
-# once next version has been released (>3.1.0) this
-# should be set to pinned version again
-# see https://github.com/django-polymorphic/django-polymorphic/pull/541
-django-polymorphic@git+https://github.com/django-polymorphic/django-polymorphic@master  # pyup: ignore
-pyyaml==6.0.1
-uritemplate==4.1.1
+django-filter==25.1
+django-polymorphic==4.1.0
diff --git a/requirements/requirements-packaging.txt b/requirements/requirements-packaging.txt
index e957043a..54882779 100644
--- a/requirements/requirements-packaging.txt
+++ b/requirements/requirements-packaging.txt
@@ -1 +1 @@
-twine==5.1.1
+twine==6.1.0
diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt
index 9c7ebb66..8fe1e366 100644
--- a/requirements/requirements-testing.txt
+++ b/requirements/requirements-testing.txt
@@ -1,7 +1,7 @@
-factory-boy==3.3.0
-Faker==26.1.0
-pytest==8.3.2
-pytest-cov==5.0.0
-pytest-django==4.8.0
-pytest-factoryboy==2.7.0
-syrupy==4.6.1
+factory-boy==3.3.3
+Faker==37.5.3
+pytest==8.4.1
+pytest-cov==6.2.1
+pytest-django==4.11.1
+pytest-factoryboy==2.8.1
+syrupy==4.9.1
diff --git a/rest_framework_json_api/__init__.py b/rest_framework_json_api/__init__.py
index 3008837c..23f6b8f9 100644
--- a/rest_framework_json_api/__init__.py
+++ b/rest_framework_json_api/__init__.py
@@ -1,5 +1,5 @@
 __title__ = "djangorestframework-jsonapi"
-__version__ = "7.0.2"
+__version__ = "8.0.0"
 __author__ = ""
 __license__ = "BSD"
 __copyright__ = ""
diff --git a/rest_framework_json_api/django_filters/backends.py b/rest_framework_json_api/django_filters/backends.py
index c0044839..70e543c1 100644
--- a/rest_framework_json_api/django_filters/backends.py
+++ b/rest_framework_json_api/django_filters/backends.py
@@ -4,7 +4,7 @@
 from rest_framework.exceptions import ValidationError
 from rest_framework.settings import api_settings
 
-from rest_framework_json_api.utils import format_field_name, undo_format_field_name
+from rest_framework_json_api.utils import undo_format_field_name
 
 
 class DjangoFilterBackend(DjangoFilterBackend):
@@ -129,18 +129,3 @@ def get_filterset_kwargs(self, request, queryset, view):
             "request": request,
             "filter_keys": filter_keys,
         }
-
-    def get_schema_operation_parameters(self, view):
-        """
-        Convert backend filter `name` to JSON:API-style `filter[name]`.
-        For filters that are relationship paths, rewrite ORM-style `__` to our preferred `.`.
-        For example: `blog__name__contains` becomes `filter[blog.name.contains]`.
-
-        This is basically the reverse of `get_filterset_kwargs` above.
-        """
-        result = super().get_schema_operation_parameters(view)
-        for res in result:
-            if "name" in res:
-                name = format_field_name(res["name"].replace("__", "."))
-                res["name"] = f"filter[{name}]"
-        return result
diff --git a/rest_framework_json_api/parsers.py b/rest_framework_json_api/parsers.py
index a0a2aeb2..8940c653 100644
--- a/rest_framework_json_api/parsers.py
+++ b/rest_framework_json_api/parsers.py
@@ -98,7 +98,7 @@ def parse_data(self, result, parser_context):
                             "Received data contains one or more malformed JSON:API "
                             "Resource Identifier Object(s)"
                         )
-            elif not (data.get("id") and data.get("type")):
+            elif isinstance(data, dict) and not (data.get("id") and data.get("type")):
                 raise ParseError(
                     "Received data is not a valid JSON:API Resource Identifier Object"
                 )
diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py
index 8c19934f..b670338f 100644
--- a/rest_framework_json_api/renderers.py
+++ b/rest_framework_json_api/renderers.py
@@ -6,7 +6,6 @@
 from collections import defaultdict
 from collections.abc import Iterable
 
-import inflection
 from django.db.models import Manager
 from django.template import loader
 from django.utils.encoding import force_str
@@ -277,9 +276,6 @@ def extract_included(
             current_serializer, "included_serializers", dict()
         )
         included_resources = copy.copy(included_resources)
-        included_resources = [
-            inflection.underscore(value) for value in included_resources
-        ]
 
         for field_name, field in iter(fields.items()):
             # Skip URL field
diff --git a/rest_framework_json_api/schemas/openapi.py b/rest_framework_json_api/schemas/openapi.py
deleted file mode 100644
index b44ce7a4..00000000
--- a/rest_framework_json_api/schemas/openapi.py
+++ /dev/null
@@ -1,895 +0,0 @@
-import warnings
-from urllib.parse import urljoin
-
-from rest_framework.fields import empty
-from rest_framework.relations import ManyRelatedField
-from rest_framework.schemas import openapi as drf_openapi
-from rest_framework.schemas.utils import is_list_view
-
-from rest_framework_json_api import serializers, views
-from rest_framework_json_api.relations import ManySerializerMethodResourceRelatedField
-from rest_framework_json_api.utils import format_field_name
-
-
-class SchemaGenerator(drf_openapi.SchemaGenerator):
-    """
-    Extend DRF's SchemaGenerator to implement JSON:API flavored generateschema command.
-    """
-
-    #: These JSON:API component definitions are referenced by the generated OAS schema.
-    #: If you need to add more or change these static component definitions, extend this dict.
-    jsonapi_components = {
-        "schemas": {
-            "jsonapi": {
-                "type": "object",
-                "description": "The server's implementation",
-                "properties": {
-                    "version": {"type": "string"},
-                    "meta": {"$ref": "#/components/schemas/meta"},
-                },
-                "additionalProperties": False,
-            },
-            "resource": {
-                "type": "object",
-                "required": ["type", "id"],
-                "additionalProperties": False,
-                "properties": {
-                    "type": {"$ref": "#/components/schemas/type"},
-                    "id": {"$ref": "#/components/schemas/id"},
-                    "attributes": {
-                        "type": "object",
-                        # ...
-                    },
-                    "relationships": {
-                        "type": "object",
-                        # ...
-                    },
-                    "links": {"$ref": "#/components/schemas/links"},
-                    "meta": {"$ref": "#/components/schemas/meta"},
-                },
-            },
-            "include": {
-                "type": "object",
-                "required": ["type", "id"],
-                "additionalProperties": False,
-                "properties": {
-                    "type": {"$ref": "#/components/schemas/type"},
-                    "id": {"$ref": "#/components/schemas/id"},
-                    "attributes": {
-                        "type": "object",
-                        "additionalProperties": True,
-                        # ...
-                    },
-                    "relationships": {
-                        "type": "object",
-                        "additionalProperties": True,
-                        # ...
-                    },
-                    "links": {"$ref": "#/components/schemas/links"},
-                    "meta": {"$ref": "#/components/schemas/meta"},
-                },
-            },
-            "link": {
-                "oneOf": [
-                    {
-                        "description": "a string containing the link's URL",
-                        "type": "string",
-                        "format": "uri-reference",
-                    },
-                    {
-                        "type": "object",
-                        "required": ["href"],
-                        "properties": {
-                            "href": {
-                                "description": "a string containing the link's URL",
-                                "type": "string",
-                                "format": "uri-reference",
-                            },
-                            "meta": {"$ref": "#/components/schemas/meta"},
-                        },
-                    },
-                ]
-            },
-            "links": {
-                "type": "object",
-                "additionalProperties": {"$ref": "#/components/schemas/link"},
-            },
-            "reltoone": {
-                "description": "a singular 'to-one' relationship",
-                "type": "object",
-                "properties": {
-                    "links": {"$ref": "#/components/schemas/relationshipLinks"},
-                    "data": {"$ref": "#/components/schemas/relationshipToOne"},
-                    "meta": {"$ref": "#/components/schemas/meta"},
-                },
-            },
-            "relationshipToOne": {
-                "description": "reference to other resource in a to-one relationship",
-                "anyOf": [
-                    {"$ref": "#/components/schemas/nulltype"},
-                    {"$ref": "#/components/schemas/linkage"},
-                ],
-            },
-            "reltomany": {
-                "description": "a multiple 'to-many' relationship",
-                "type": "object",
-                "properties": {
-                    "links": {"$ref": "#/components/schemas/relationshipLinks"},
-                    "data": {"$ref": "#/components/schemas/relationshipToMany"},
-                    "meta": {"$ref": "#/components/schemas/meta"},
-                },
-            },
-            "relationshipLinks": {
-                "description": "optional references to other resource objects",
-                "type": "object",
-                "additionalProperties": True,
-                "properties": {
-                    "self": {"$ref": "#/components/schemas/link"},
-                    "related": {"$ref": "#/components/schemas/link"},
-                },
-            },
-            "relationshipToMany": {
-                "description": "An array of objects each containing the "
-                "'type' and 'id' for to-many relationships",
-                "type": "array",
-                "items": {"$ref": "#/components/schemas/linkage"},
-                "uniqueItems": True,
-            },
-            # A RelationshipView uses a ResourceIdentifierObjectSerializer (hence the name
-            # ResourceIdentifierObject returned by get_component_name()) which serializes type
-            # and id. These can be lists or individual items depending on whether the
-            # relationship is toMany or toOne so offer both options since we are not iterating
-            # over all the possible {related_field}'s but rather rendering one path schema
-            # which may represent toMany and toOne relationships.
-            "ResourceIdentifierObject": {
-                "oneOf": [
-                    {"$ref": "#/components/schemas/relationshipToOne"},
-                    {"$ref": "#/components/schemas/relationshipToMany"},
-                ]
-            },
-            "linkage": {
-                "type": "object",
-                "description": "the 'type' and 'id'",
-                "required": ["type", "id"],
-                "properties": {
-                    "type": {"$ref": "#/components/schemas/type"},
-                    "id": {"$ref": "#/components/schemas/id"},
-                    "meta": {"$ref": "#/components/schemas/meta"},
-                },
-            },
-            "pagination": {
-                "type": "object",
-                "properties": {
-                    "first": {"$ref": "#/components/schemas/pageref"},
-                    "last": {"$ref": "#/components/schemas/pageref"},
-                    "prev": {"$ref": "#/components/schemas/pageref"},
-                    "next": {"$ref": "#/components/schemas/pageref"},
-                },
-            },
-            "pageref": {
-                "oneOf": [
-                    {"type": "string", "format": "uri-reference"},
-                    {"$ref": "#/components/schemas/nulltype"},
-                ]
-            },
-            "failure": {
-                "type": "object",
-                "required": ["errors"],
-                "properties": {
-                    "errors": {"$ref": "#/components/schemas/errors"},
-                    "meta": {"$ref": "#/components/schemas/meta"},
-                    "jsonapi": {"$ref": "#/components/schemas/jsonapi"},
-                    "links": {"$ref": "#/components/schemas/links"},
-                },
-            },
-            "errors": {
-                "type": "array",
-                "items": {"$ref": "#/components/schemas/error"},
-                "uniqueItems": True,
-            },
-            "error": {
-                "type": "object",
-                "additionalProperties": False,
-                "properties": {
-                    "id": {"type": "string"},
-                    "status": {"type": "string"},
-                    "links": {"$ref": "#/components/schemas/links"},
-                    "code": {"type": "string"},
-                    "title": {"type": "string"},
-                    "detail": {"type": "string"},
-                    "source": {
-                        "type": "object",
-                        "properties": {
-                            "pointer": {
-                                "type": "string",
-                                "description": (
-                                    "A [JSON Pointer](https://tools.ietf.org/html/rfc6901) "
-                                    "to the associated entity in the request document "
-                                    "[e.g. `/data` for a primary data object, or "
-                                    "`/data/attributes/title` for a specific attribute."
-                                ),
-                            },
-                            "parameter": {
-                                "type": "string",
-                                "description": "A string indicating which query parameter "
-                                "caused the error.",
-                            },
-                            "meta": {"$ref": "#/components/schemas/meta"},
-                        },
-                    },
-                },
-            },
-            "onlymeta": {
-                "additionalProperties": False,
-                "properties": {"meta": {"$ref": "#/components/schemas/meta"}},
-            },
-            "meta": {"type": "object", "additionalProperties": True},
-            "datum": {
-                "description": "singular item",
-                "properties": {"data": {"$ref": "#/components/schemas/resource"}},
-            },
-            "nulltype": {"type": "object", "nullable": True, "default": None},
-            "type": {
-                "type": "string",
-                "description": "The [type]"
-                "(https://jsonapi.org/format/#document-resource-object-identification) "
-                "member is used to describe resource objects that share common attributes "
-                "and relationships.",
-            },
-            "id": {
-                "type": "string",
-                "description": "Each resource object’s type and id pair MUST "
-                "[identify]"
-                "(https://jsonapi.org/format/#document-resource-object-identification) "
-                "a single, unique resource.",
-            },
-        },
-        "parameters": {
-            "include": {
-                "name": "include",
-                "in": "query",
-                "description": "[list of included related resources]"
-                "(https://jsonapi.org/format/#fetching-includes)",
-                "required": False,
-                "style": "form",
-                "schema": {"type": "string"},
-            },
-            # TODO: deepObject not well defined/supported:
-            #       https://github.com/OAI/OpenAPI-Specification/issues/1706
-            "fields": {
-                "name": "fields",
-                "in": "query",
-                "description": "[sparse fieldsets]"
-                "(https://jsonapi.org/format/#fetching-sparse-fieldsets).\n"
-                "Use fields[\\<typename\\>]=field1,field2,...,fieldN",
-                "required": False,
-                "style": "deepObject",
-                "schema": {
-                    "type": "object",
-                },
-                "explode": True,
-            },
-        },
-    }
-
-    def get_schema(self, request=None, public=False):
-        """
-        Generate a JSON:API OpenAPI schema.
-        Overrides upstream DRF's get_schema.
-        """
-        # TODO: avoid copying so much of upstream get_schema()
-        schema = super().get_schema(request, public)
-
-        components_schemas = {}
-
-        # Iterate endpoints generating per method path operations.
-        paths = {}
-        _, view_endpoints = self._get_paths_and_endpoints(None if public else request)
-
-        #: `expanded_endpoints` is like view_endpoints with one extra field tacked on:
-        #: - 'action' copy of current view.action (list/fetch) as this gets reset for
-        # each request.
-        expanded_endpoints = []
-        for path, method, view in view_endpoints:
-            if hasattr(view, "action") and view.action == "retrieve_related":
-                expanded_endpoints += self._expand_related(
-                    path, method, view, view_endpoints
-                )
-            else:
-                expanded_endpoints.append(
-                    (path, method, view, getattr(view, "action", None))
-                )
-
-        for path, method, view, action in expanded_endpoints:
-            if not self.has_view_permissions(path, method, view):
-                continue
-            # kludge to preserve view.action as it is 'list' for the parent ViewSet
-            # but the related viewset that was expanded may be either 'fetch' (to_one) or 'list'
-            # (to_many). This patches the view.action appropriately so that
-            # view.schema.get_operation() "does the right thing" for fetch vs. list.
-            current_action = None
-            if hasattr(view, "action"):
-                current_action = view.action
-                view.action = action
-            operation = view.schema.get_operation(path, method)
-            components = view.schema.get_components(path, method)
-            for k in components.keys():
-                if k not in components_schemas:
-                    continue
-                if components_schemas[k] == components[k]:
-                    continue
-                warnings.warn(
-                    f'Schema component "{k}" has been overriden with a different value.',
-                    stacklevel=1,
-                )
-
-            components_schemas.update(components)
-
-            if hasattr(view, "action"):
-                view.action = current_action
-            # Normalise path for any provided mount url.
-            if path.startswith("/"):
-                path = path[1:]
-            path = urljoin(self.url or "/", path)
-
-            paths.setdefault(path, {})
-            paths[path][method.lower()] = operation
-
-        self.check_duplicate_operation_id(paths)
-
-        # Compile final schema, overriding stuff from super class.
-        schema["paths"] = paths
-        schema["components"] = self.jsonapi_components
-        schema["components"]["schemas"].update(components_schemas)
-
-        return schema
-
-    def _expand_related(self, path, method, view, view_endpoints):
-        """
-        Expand path containing .../{id}/{related_field} into list of related fields
-        and **their** views, making sure toOne relationship's views are a 'fetch' and toMany
-        relationship's are a 'list'.
-        :param path
-        :param method
-        :param view
-        :param view_endpoints
-        :return:list[tuple(path, method, view, action)]
-        """
-        result = []
-        serializer = view.get_serializer()
-        # It's not obvious if it's allowed to have both included_ and related_ serializers,
-        # so just merge both dicts.
-        serializers = {}
-        if hasattr(serializer, "included_serializers"):
-            serializers = {**serializers, **serializer.included_serializers}
-        if hasattr(serializer, "related_serializers"):
-            serializers = {**serializers, **serializer.related_serializers}
-        related_fields = [fs for fs in serializers.items()]
-
-        for field, related_serializer in related_fields:
-            related_view = self._find_related_view(
-                view_endpoints, related_serializer, view
-            )
-            if related_view:
-                action = self._field_is_one_or_many(field, view)
-                result.append(
-                    (
-                        path.replace("{related_field}", field),
-                        method,
-                        related_view,
-                        action,
-                    )
-                )
-
-        return result
-
-    def _find_related_view(self, view_endpoints, related_serializer, parent_view):
-        """
-        For a given related_serializer, try to find it's "parent" view instance.
-
-        :param view_endpoints: list of all view endpoints
-        :param related_serializer: the related serializer for a given related field
-        :param parent_view: the parent view (used to find toMany vs. toOne).
-               TODO: not actually used.
-        :return:view
-        """
-        for _path, _method, view in view_endpoints:
-            view_serializer = view.get_serializer()
-            if isinstance(view_serializer, related_serializer):
-                return view
-
-        return None
-
-    def _field_is_one_or_many(self, field, view):
-        serializer = view.get_serializer()
-        if isinstance(serializer.fields[field], ManyRelatedField):
-            return "list"
-        else:
-            return "fetch"
-
-
-class AutoSchema(drf_openapi.AutoSchema):
-    """
-    Extend DRF's openapi.AutoSchema for JSON:API serialization.
-    """
-
-    #: ignore all the media types and only generate a JSON:API schema.
-    content_types = ["application/vnd.api+json"]
-
-    def get_operation(self, path, method):
-        """
-        JSON:API adds some standard fields to the API response that are not in upstream DRF:
-        - some that only apply to GET/HEAD methods.
-        - collections
-        - special handling for POST, PATCH, DELETE
-        """
-        operation = {}
-        operation["operationId"] = self.get_operation_id(path, method)
-        operation["description"] = self.get_description(path, method)
-
-        serializer = self.get_response_serializer(path, method)
-
-        parameters = []
-        parameters += self.get_path_parameters(path, method)
-        # pagination, filters only apply to GET/HEAD of collections and items
-        if method in ["GET", "HEAD"]:
-            parameters += self._get_include_parameters(path, method, serializer)
-            parameters += self._get_fields_parameters(path, method)
-            parameters += self.get_pagination_parameters(path, method)
-            parameters += self.get_filter_parameters(path, method)
-        operation["parameters"] = parameters
-        operation["tags"] = self.get_tags(path, method)
-
-        # get request and response code schemas
-        if method == "GET":
-            if is_list_view(path, method, self.view):
-                self._add_get_collection_response(operation, path)
-            else:
-                self._add_get_item_response(operation, path)
-        elif method == "POST":
-            self._add_post_item_response(operation, path)
-        elif method == "PATCH":
-            self._add_patch_item_response(operation, path)
-        elif method == "DELETE":
-            # should only allow deleting a resource, not a collection
-            # TODO: implement delete of a relationship in future release.
-            self._add_delete_item_response(operation, path)
-        return operation
-
-    def get_operation_id(self, path, method):
-        """
-        The upstream DRF version creates non-unique operationIDs, because the same view is
-        used for the main path as well as such as related and relationships.
-        This concatenates the (mapped) method name and path as the spec allows most any
-        """
-        method_name = getattr(self.view, "action", method.lower())
-        if is_list_view(path, method, self.view):
-            action = "List"
-        elif method_name not in self.method_mapping:
-            action = method_name
-        else:
-            action = self.method_mapping[method.lower()]
-        return action + path
-
-    def _get_include_parameters(self, path, method, serializer):
-        """
-        includes parameter: https://jsonapi.org/format/#fetching-includes
-        """
-        if getattr(serializer, "included_serializers", {}):
-            return [{"$ref": "#/components/parameters/include"}]
-        return []
-
-    def _get_fields_parameters(self, path, method):
-        """
-        sparse fieldsets https://jsonapi.org/format/#fetching-sparse-fieldsets
-        """
-        # TODO: See if able to identify the specific types for fields[type]=... and return this:
-        # name: fields
-        # in: query
-        # description: '[sparse fieldsets](https://jsonapi.org/format/#fetching-sparse-fieldsets)'  # noqa: B950
-        # required: true
-        # style: deepObject
-        # schema:
-        #   type: object
-        #   properties:
-        #     hello:
-        #       type: string  # noqa F821
-        #     world:
-        #       type: string  # noqa F821
-        # explode: true
-        return [{"$ref": "#/components/parameters/fields"}]
-
-    def _add_get_collection_response(self, operation, path):
-        """
-        Add GET 200 response for a collection to operation
-        """
-        operation["responses"] = {
-            "200": self._get_toplevel_200_response(
-                operation, path, "GET", collection=True
-            )
-        }
-        self._add_get_4xx_responses(operation)
-
-    def _add_get_item_response(self, operation, path):
-        """
-        add GET 200 response for an item to operation
-        """
-        operation["responses"] = {
-            "200": self._get_toplevel_200_response(
-                operation, path, "GET", collection=False
-            )
-        }
-        self._add_get_4xx_responses(operation)
-
-    def _get_toplevel_200_response(self, operation, path, method, collection=True):
-        """
-        return top-level JSON:API GET 200 response
-
-        :param collection: True for collections; False for individual items.
-
-        Uses a $ref to the components.schemas.<Name> component definition.
-        """
-        if collection:
-            data = {
-                "type": "array",
-                "items": self.get_reference(self.get_response_serializer(path, method)),
-            }
-        else:
-            data = self.get_reference(self.get_response_serializer(path, method))
-
-        return {
-            "description": operation["operationId"],
-            "content": {
-                "application/vnd.api+json": {
-                    "schema": {
-                        "type": "object",
-                        "required": ["data"],
-                        "properties": {
-                            "data": data,
-                            "included": {
-                                "type": "array",
-                                "uniqueItems": True,
-                                "items": {"$ref": "#/components/schemas/include"},
-                            },
-                            "links": {
-                                "description": "Link members related to primary data",
-                                "allOf": [
-                                    {"$ref": "#/components/schemas/links"},
-                                    {"$ref": "#/components/schemas/pagination"},
-                                ],
-                            },
-                            "jsonapi": {"$ref": "#/components/schemas/jsonapi"},
-                        },
-                    }
-                }
-            },
-        }
-
-    def _add_post_item_response(self, operation, path):
-        """
-        add response for POST of an item to operation
-        """
-        operation["requestBody"] = self.get_request_body(path, "POST")
-        operation["responses"] = {
-            "201": self._get_toplevel_200_response(
-                operation, path, "POST", collection=False
-            )
-        }
-        operation["responses"]["201"]["description"] = (
-            "[Created](https://jsonapi.org/format/#crud-creating-responses-201). "
-            "Assigned `id` and/or any other changes are in this response."
-        )
-        self._add_async_response(operation)
-        operation["responses"]["204"] = {
-            "description": "[Created](https://jsonapi.org/format/#crud-creating-responses-204) "
-            "with the supplied `id`. No other changes from what was POSTed."
-        }
-        self._add_post_4xx_responses(operation)
-
-    def _add_patch_item_response(self, operation, path):
-        """
-        Add PATCH response for an item to operation
-        """
-        operation["requestBody"] = self.get_request_body(path, "PATCH")
-        operation["responses"] = {
-            "200": self._get_toplevel_200_response(
-                operation, path, "PATCH", collection=False
-            )
-        }
-        self._add_patch_4xx_responses(operation)
-
-    def _add_delete_item_response(self, operation, path):
-        """
-        add DELETE response for item or relationship(s) to operation
-        """
-        # Only DELETE of relationships has a requestBody
-        if isinstance(self.view, views.RelationshipView):
-            operation["requestBody"] = self.get_request_body(path, "DELETE")
-        self._add_delete_responses(operation)
-
-    def get_request_body(self, path, method):
-        """
-        A request body is required by JSON:API for POST, PATCH, and DELETE methods.
-        """
-        serializer = self.get_request_serializer(path, method)
-        if not isinstance(serializer, (serializers.BaseSerializer,)):
-            return {}
-        is_relationship = isinstance(self.view, views.RelationshipView)
-
-        # DRF uses a $ref to the component schema definition, but this
-        # doesn't work for JSON:API due to the different required fields based on
-        # the method, so make those changes and inline another copy of the schema.
-
-        # TODO: A future improvement could make this DRYer with multiple component schemas:
-        # A base schema for each viewset that has no required fields
-        # One subclassed from the base that requires some fields (`type` but not `id` for POST)
-        # Another subclassed from base with required type/id but no required attributes (PATCH)
-
-        if is_relationship:
-            item_schema = {"$ref": "#/components/schemas/ResourceIdentifierObject"}
-        else:
-            item_schema = self.map_serializer(serializer)
-            if method == "POST":
-                # 'type' and 'id' are both required for:
-                # - all relationship operations
-                # - regular PATCH or DELETE
-                # Only 'type' is required for POST: system may assign the 'id'.
-                item_schema["required"] = ["type"]
-
-        if "properties" in item_schema and "attributes" in item_schema["properties"]:
-            # No required attributes for PATCH
-            if (
-                method in ["PATCH", "PUT"]
-                and "required" in item_schema["properties"]["attributes"]
-            ):
-                del item_schema["properties"]["attributes"]["required"]
-            # No read_only fields for request.
-            for name, schema in (
-                item_schema["properties"]["attributes"]["properties"].copy().items()
-            ):  # noqa E501
-                if "readOnly" in schema:
-                    del item_schema["properties"]["attributes"]["properties"][name]
-
-        if "properties" in item_schema and "relationships" in item_schema["properties"]:
-            # No required relationships for PATCH
-            if (
-                method in ["PATCH", "PUT"]
-                and "required" in item_schema["properties"]["relationships"]
-            ):
-                del item_schema["properties"]["relationships"]["required"]
-
-        return {
-            "content": {
-                ct: {
-                    "schema": {
-                        "required": ["data"],
-                        "properties": {"data": item_schema},
-                    }
-                }
-                for ct in self.content_types
-            }
-        }
-
-    def map_serializer(self, serializer):
-        """
-        Custom map_serializer that serializes the schema using the JSON:API spec.
-
-        Non-attributes like related and identity fields, are moved to 'relationships'
-        and 'links'.
-        """
-        # TODO: remove attributes, etc. for relationshipView??
-        if isinstance(
-            serializer.parent, (serializers.ListField, serializers.BaseSerializer)
-        ):
-            # Return plain non-JSON:API serializer schema for serializers nested inside
-            # a Serializer or a ListField, as those don't use the full JSON:API
-            # serializer schemas.
-            return super().map_serializer(serializer)
-
-        required = []
-        attributes = {}
-        relationships_required = []
-        relationships = {}
-
-        for field in serializer.fields.values():
-            if isinstance(field, serializers.HyperlinkedIdentityField):
-                # the 'url' is not an attribute but rather a self.link, so don't map it here.
-                continue
-            if isinstance(field, serializers.HiddenField):
-                continue
-            if isinstance(
-                field,
-                (
-                    serializers.ManyRelatedField,
-                    ManySerializerMethodResourceRelatedField,
-                ),
-            ):
-                if field.required:
-                    relationships_required.append(format_field_name(field.field_name))
-                relationships[format_field_name(field.field_name)] = {
-                    "$ref": "#/components/schemas/reltomany"
-                }
-                continue
-            if isinstance(field, serializers.RelatedField):
-                if field.required:
-                    relationships_required.append(format_field_name(field.field_name))
-                relationships[format_field_name(field.field_name)] = {
-                    "$ref": "#/components/schemas/reltoone"
-                }
-                continue
-            if field.field_name == "id":
-                # ID is always provided in the root of JSON:API and removed from the
-                # attributes in JSONRenderer.
-                continue
-
-            if field.required:
-                required.append(format_field_name(field.field_name))
-
-            schema = self.map_field(field)
-            if field.read_only:
-                schema["readOnly"] = True
-            if field.write_only:
-                schema["writeOnly"] = True
-            if field.allow_null:
-                schema["nullable"] = True
-            if field.default and field.default != empty and not callable(field.default):
-                schema["default"] = field.default
-            if field.help_text:
-                # Ensure django gettext_lazy is rendered correctly
-                schema["description"] = str(field.help_text)
-            self.map_field_validators(field, schema)
-
-            attributes[format_field_name(field.field_name)] = schema
-
-        result = {
-            "type": "object",
-            "required": ["type", "id"],
-            "additionalProperties": False,
-            "properties": {
-                "type": {"$ref": "#/components/schemas/type"},
-                "id": {"$ref": "#/components/schemas/id"},
-                "links": {
-                    "type": "object",
-                    "properties": {"self": {"$ref": "#/components/schemas/link"}},
-                },
-            },
-        }
-        if attributes:
-            result["properties"]["attributes"] = {
-                "type": "object",
-                "properties": attributes,
-            }
-            if required:
-                result["properties"]["attributes"]["required"] = required
-
-        if relationships:
-            result["properties"]["relationships"] = {
-                "type": "object",
-                "properties": relationships,
-            }
-            if relationships_required:
-                result["properties"]["relationships"][
-                    "required"
-                ] = relationships_required
-        return result
-
-    def _add_async_response(self, operation):
-        """
-        Add async response to operation
-        """
-        operation["responses"]["202"] = {
-            "description": "Accepted for [asynchronous processing]"
-            "(https://jsonapi.org/recommendations/#asynchronous-processing)",
-            "content": {
-                "application/vnd.api+json": {
-                    "schema": {"$ref": "#/components/schemas/datum"}
-                }
-            },
-        }
-
-    def _failure_response(self, reason):
-        """
-        Return failure response reason as the description
-        """
-        return {
-            "description": reason,
-            "content": {
-                "application/vnd.api+json": {
-                    "schema": {"$ref": "#/components/schemas/failure"}
-                }
-            },
-        }
-
-    def _add_generic_failure_responses(self, operation):
-        """
-        Add generic failure response(s) to operation
-        """
-        for code, reason in [
-            ("400", "bad request"),
-            ("401", "not authorized"),
-            ("429", "too many requests"),
-        ]:
-            operation["responses"][code] = self._failure_response(reason)
-
-    def _add_get_4xx_responses(self, operation):
-        """
-        Add generic 4xx GET responses to operation
-        """
-        self._add_generic_failure_responses(operation)
-        for code, reason in [("404", "not found")]:
-            operation["responses"][code] = self._failure_response(reason)
-
-    def _add_post_4xx_responses(self, operation):
-        """
-        Add POST 4xx error responses to operation
-        """
-        self._add_generic_failure_responses(operation)
-        for code, reason in [
-            (
-                "403",
-                "[Forbidden](https://jsonapi.org/format/#crud-creating-responses-403)",
-            ),
-            (
-                "404",
-                "[Related resource does not exist]"
-                "(https://jsonapi.org/format/#crud-creating-responses-404)",
-            ),
-            (
-                "409",
-                "[Conflict](https://jsonapi.org/format/#crud-creating-responses-409)",
-            ),
-        ]:
-            operation["responses"][code] = self._failure_response(reason)
-
-    def _add_patch_4xx_responses(self, operation):
-        """
-        Add PATCH 4xx error responses to operation
-        """
-        self._add_generic_failure_responses(operation)
-        for code, reason in [
-            (
-                "403",
-                "[Forbidden](https://jsonapi.org/format/#crud-updating-responses-403)",
-            ),
-            (
-                "404",
-                "[Related resource does not exist]"
-                "(https://jsonapi.org/format/#crud-updating-responses-404)",
-            ),
-            (
-                "409",
-                "[Conflict]([Conflict]"
-                "(https://jsonapi.org/format/#crud-updating-responses-409)",
-            ),
-        ]:
-            operation["responses"][code] = self._failure_response(reason)
-
-    def _add_delete_responses(self, operation):
-        """
-        Add generic DELETE responses to operation
-        """
-        # the 2xx statuses:
-        operation["responses"] = {
-            "200": {
-                "description": "[OK](https://jsonapi.org/format/#crud-deleting-responses-200)",
-                "content": {
-                    "application/vnd.api+json": {
-                        "schema": {"$ref": "#/components/schemas/onlymeta"}
-                    }
-                },
-            }
-        }
-        self._add_async_response(operation)
-        operation["responses"]["204"] = {
-            "description": "[no content](https://jsonapi.org/format/#crud-deleting-responses-204)",  # noqa: B950
-        }
-        # the 4xx errors:
-        self._add_generic_failure_responses(operation)
-        for code, reason in [
-            (
-                "404",
-                "[Resource does not exist]"
-                "(https://jsonapi.org/format/#crud-deleting-responses-404)",
-            ),
-        ]:
-            operation["responses"][code] = self._failure_response(reason)
diff --git a/rest_framework_json_api/serializers.py b/rest_framework_json_api/serializers.py
index d59dbd88..26f6b02e 100644
--- a/rest_framework_json_api/serializers.py
+++ b/rest_framework_json_api/serializers.py
@@ -1,6 +1,5 @@
 from collections.abc import Mapping
 
-import inflection
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models.query import QuerySet
 from django.utils.module_loading import import_string as import_class_from_dotted_path
@@ -27,6 +26,7 @@
     get_resource_type_from_instance,
     get_resource_type_from_model,
     get_resource_type_from_serializer,
+    undo_format_field_name,
 )
 
 
@@ -90,7 +90,10 @@ def _readable_fields(self):
                     sparse_fieldset_query_param
                 )
                 if sparse_fieldset_value is not None:
-                    sparse_fields = sparse_fieldset_value.split(",")
+                    sparse_fields = [
+                        undo_format_field_name(sparse_field)
+                        for sparse_field in sparse_fieldset_value.split(",")
+                    ]
                     return (
                         field
                         for field in readable_fields
@@ -129,7 +132,7 @@ def validate_path(serializer_class, field_path, path):
             serializers = getattr(serializer_class, "included_serializers", None)
             if serializers is None:
                 raise ParseError("This endpoint does not support the include parameter")
-            this_field_name = inflection.underscore(field_path[0])
+            this_field_name = field_path[0]
             this_included_serializer = serializers.get(this_field_name)
             if this_included_serializer is None:
                 raise ParseError(
diff --git a/rest_framework_json_api/utils.py b/rest_framework_json_api/utils.py
index 805f5f09..2dd79677 100644
--- a/rest_framework_json_api/utils.py
+++ b/rest_framework_json_api/utils.py
@@ -316,10 +316,18 @@ def get_resource_id(resource_instance, resource):
 
 
 def get_included_resources(request, serializer=None):
-    """Build a list of included resources."""
+    """
+    Build a list of included resources.
+
+    This method ensures that returned includes are in Python internally used
+    format.
+    """
     include_resources_param = request.query_params.get("include") if request else None
     if include_resources_param:
-        return include_resources_param.split(",")
+        return [
+            undo_format_field_name(include)
+            for include in include_resources_param.split(",")
+        ]
     else:
         return get_default_included_resources_from_serializer(serializer)
 
diff --git a/setup.cfg b/setup.cfg
index 4230dcbb..92606700 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -63,9 +63,6 @@ DJANGO_SETTINGS_MODULE=example.settings.test
 filterwarnings =
     error::DeprecationWarning
     error::PendingDeprecationWarning
-    # Django filter schema generation. Can be removed once we remove
-    # schema support
-    ignore:Built-in schema generation is deprecated.
 testpaths =
     example
     tests
diff --git a/setup.py b/setup.py
index 95d5ca0b..2a2a28dd 100755
--- a/setup.py
+++ b/setup.py
@@ -86,11 +86,11 @@ def get_package_data(package):
         "Operating System :: OS Independent",
         "Programming Language :: Python",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: 3.12",
+        "Programming Language :: Python :: 3.13",
         "Topic :: Internet :: WWW/HTTP",
         "Topic :: Software Development :: Libraries :: Application Frameworks",
         "Topic :: Software Development :: Libraries :: Python Modules",
@@ -106,15 +106,14 @@ def get_package_data(package):
     },
     install_requires=[
         "inflection>=0.5.0",
-        "djangorestframework>=3.14",
+        "djangorestframework>=3.15",
         "django>=4.2",
     ],
     extras_require={
-        "django-polymorphic": ["django-polymorphic>=3.0"],
+        "django-polymorphic": ["django-polymorphic>=4.0.0"],
         "django-filter": ["django-filter>=2.4"],
-        "openapi": ["pyyaml>=5.4", "uritemplate>=3.0.1"],
     },
     setup_requires=wheel,
-    python_requires=">=3.8",
+    python_requires=">=3.9",
     zip_safe=False,
 )
diff --git a/tests/conftest.py b/tests/conftest.py
index 865244e0..77b3676b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,5 +1,5 @@
 import pytest
-from rest_framework.test import APIClient
+from rest_framework.test import APIClient, APIRequestFactory
 
 from tests.models import (
     BasicModel,
@@ -98,3 +98,8 @@ def nested_related_source(
 @pytest.fixture
 def client():
     return APIClient()
+
+
+@pytest.fixture
+def rf():
+    return APIRequestFactory()
diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py
deleted file mode 100644
index 427f18fc..00000000
--- a/tests/schemas/test_openapi.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from rest_framework_json_api.schemas.openapi import AutoSchema
-from tests.serializers import CallableDefaultSerializer
-
-
-class TestAutoSchema:
-    def test_schema_callable_default(self):
-        inspector = AutoSchema()
-        result = inspector.map_serializer(CallableDefaultSerializer())
-        assert result["properties"]["attributes"]["properties"]["field"] == {
-            "type": "string",
-        }
diff --git a/tests/test_serializers.py b/tests/test_serializers.py
index 9d4200a3..98cb2850 100644
--- a/tests/test_serializers.py
+++ b/tests/test_serializers.py
@@ -1,5 +1,6 @@
 import pytest
 from django.db import models
+from rest_framework.request import Request
 from rest_framework.utils import model_meta
 
 from rest_framework_json_api import serializers
@@ -84,3 +85,22 @@ class Meta:
         "verified",
         "uuid",
     ]
+
+
+def test_readable_fields_with_sparse_fields(client, rf, settings):
+    class TestSerializer(serializers.Serializer):
+        name = serializers.CharField()
+        value = serializers.CharField()
+        multi_part_name = serializers.CharField()
+
+        class Meta:
+            resource_name = "test"
+
+    settings.JSON_API_FORMAT_FIELD_NAMES = "camelize"
+    request = Request(rf.get("/test/", {"fields[test]": "value,multiPartName"}))
+    context = {"request": request}
+    serializer = TestSerializer(context=context)
+    assert [field.field_name for field in serializer._readable_fields] == [
+        "value",
+        "multi_part_name",
+    ]
diff --git a/tests/test_utils.py b/tests/test_utils.py
index a3beb12e..08e36b6a 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -2,6 +2,7 @@
 from rest_framework import status
 from rest_framework.fields import Field
 from rest_framework.generics import GenericAPIView
+from rest_framework.request import Request
 from rest_framework.response import Response
 from rest_framework.views import APIView
 
@@ -13,6 +14,7 @@
     format_link_segment,
     format_resource_type,
     format_value,
+    get_included_resources,
     get_related_resource_type,
     get_resource_id,
     get_resource_name,
@@ -456,3 +458,22 @@ def test_get_resource_id(resource_instance, resource, expected):
 )
 def test_format_error_object(message, pointer, response, result):
     assert result == format_error_object(message, pointer, response)
+
+
+@pytest.mark.parametrize(
+    "format_type,include_param,expected_includes",
+    [
+        ("dasherize", "author-bio", ["author_bio"]),
+        ("dasherize", "author-bio,author-type", ["author_bio", "author_type"]),
+        ("dasherize", "author-bio.author-type", ["author_bio.author_type"]),
+        ("camelize", "authorBio", ["author_bio"]),
+    ],
+)
+def test_get_included_resources(
+    rf, include_param, expected_includes, format_type, settings
+):
+    settings.JSON_API_FORMAT_FIELD_NAMES = format_type
+
+    request = Request(rf.get("/test/", {"include": include_param}))
+    includes = get_included_resources(request)
+    assert includes == expected_includes
diff --git a/tox.ini b/tox.ini
index 68646fb4..8dd0e759 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,8 @@
 [tox]
 envlist =
-    py{38,39,310,311,312}-django42-drf{314,315,master},
-    py{310,311,312}-django50-drf{314,315,master},
+    py{39,310,311,312}-django42-drf{315,316,master},
+    py{310,311,312}-django{51,52}-drf{315,316,master},
+    py{313}-django{51,52}-drf{316,master},
     black,
     docs,
     lint
@@ -9,9 +10,10 @@ envlist =
 [testenv]
 deps =
     django42: Django>=4.2,<4.3
-    django50: Django>=5.0,<5.1
-    drf314: djangorestframework>=3.14,<3.15
+    django51: Django>=5.1,<5.2
+    django52: Django>=5.2,<5.3
     drf315: djangorestframework>=3.15,<3.16
+    drf316: djangorestframework>=3.16,<3.17
     drfmaster: https://github.com/encode/django-rest-framework/archive/master.zip
     -rrequirements/requirements-testing.txt
     -rrequirements/requirements-optionals.txt
@@ -24,13 +26,13 @@ commands =
     pytest --cov --no-cov-on-fail --cov-report xml {posargs}
 
 [testenv:black]
-basepython = python3.9
+basepython = python3.10
 deps =
     -rrequirements/requirements-codestyle.txt
 commands = black --check .
 
 [testenv:lint]
-basepython = python3.9
+basepython = python3.10
 deps =
     -rrequirements/requirements-codestyle.txt
     -rrequirements/requirements-testing.txt
@@ -39,15 +41,9 @@ commands = flake8
 
 [testenv:docs]
 # keep in sync with .readthedocs.yml
-basepython = python3.9
+basepython = python3.10
 deps =
     -rrequirements/requirements-optionals.txt
     -rrequirements/requirements-documentation.txt
 commands =
     sphinx-build -W -b html -d docs/_build/doctrees docs docs/_build/html
-
-[testenv:py{38,39,310,311,312}-django42-drfmaster]
-ignore_outcome = true
-
-[testenv:py{310,311,312}-django50-drfmaster]
-ignore_outcome = true
