Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ any parts of the framework not mentioned in the documentation should generally b
* Removed support for Django 4.2.
* Removed support for Django 5.1.

### Removed

* Removed support for exposing Polymorphic resources through [django-polymorphic](https://github.com/django-commons/django-polymorphic) as JSON:API endpoints.

## [8.1.0] - 2025-10-17

This is the last release supporting Python 3.9, Django 4.2, Django 5.1, Django REST framework 3.15 and Django REST framework 3.16.
Expand All @@ -39,7 +43,7 @@ This is the last release supporting Python 3.9, Django 4.2, Django 5.1, Django R

### Deprecated

* Deprecated support for exposing Polymorphic resources through [django-polymorphic](https://github.com/jazzband/django-polymorphic) as JSON:API endpoints.
* Deprecated support for exposing Polymorphic resources through [django-polymorphic](https://github.com/django-commons/django-polymorphic) as JSON:API endpoints.
There is currently no replacement. In case you are affected of this change please
join [our discussion](https://github.com/django-json-api/django-rest-framework-json-api/discussions/1194).

Expand Down
1 change: 0 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ Install using ``pip``...
$ pip install djangorestframework-jsonapi
$ # for optional package integrations
$ pip install djangorestframework-jsonapi['django-filter']
$ pip install djangorestframework-jsonapi['django-polymorphic']


or from source...
Expand Down
1 change: 0 additions & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ Install using `pip`...
pip install djangorestframework-jsonapi
# for optional package integrations
pip install djangorestframework-jsonapi['django-filter']
pip install djangorestframework-jsonapi['django-polymorphic']

or from source...

Expand Down
68 changes: 0 additions & 68 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -874,74 +874,6 @@ field_name_mapping = {
}
```

### Working with polymorphic resources

--

**Deprecation notice:**


Support for using Polymorphic resources through [django-polymorphic](https://github.com/jazzband/django-polymorphic) is deprecated and will be removed in a future release.

There is currently no replacement. In case you are affected of this change please join [our discussion](https://github.com/django-json-api/django-rest-framework-json-api/discussions/1194).

---

Polymorphic resources allow you to use specialized subclasses without requiring
special endpoints to expose the specialized versions. For example, if you had a
`Project` that could be either an `ArtProject` or a `ResearchProject`, you can
have both kinds at the same URL.

DJA tests its polymorphic support against [django-polymorphic](https://django-polymorphic.readthedocs.io/en/stable/).
The polymorphic feature should also work with other popular libraries like
django-polymodels or django-typed-models.

As this feature depends on `django-polymorphic` you need to run

pip install djangorestframework-jsonapi['django-polymorphic']

#### Writing polymorphic resources

A polymorphic endpoint can be set up if associated with a polymorphic serializer.
A polymorphic serializer takes care of (de)serializing the correct instances types and can be defined like this:

```python
class ProjectSerializer(serializers.PolymorphicModelSerializer):
polymorphic_serializers = [ArtProjectSerializer, ResearchProjectSerializer]

class Meta:
model = models.Project
```

It must inherit from `serializers.PolymorphicModelSerializer` and define the `polymorphic_serializers` list.
This attribute defines the accepted resource types.


Polymorphic relations can also be handled with `relations.PolymorphicResourceRelatedField` like this:

```python
class CompanySerializer(serializers.ModelSerializer):
current_project = relations.PolymorphicResourceRelatedField(
ProjectSerializer, queryset=models.Project.objects.all())
future_projects = relations.PolymorphicResourceRelatedField(
ProjectSerializer, queryset=models.Project.objects.all(), many=True)

class Meta:
model = models.Company
```

They must be explicitly declared with the `polymorphic_serializer` (first positional argument) correctly defined.
It must be a subclass of `serializers.PolymorphicModelSerializer`.

<div class="warning">
<strong>Note:</strong>
Polymorphic resources are not compatible with
<code class="docutils literal">
<span class="pre">resource_name</span>
</code>
defined on the view.
</div>

### Meta

You may add metadata to the rendered json in two different ways: `meta_fields` and `get_root_meta`.
Expand Down
38 changes: 0 additions & 38 deletions example/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@
from faker import Factory as FakerFactory

from example.models import (
ArtProject,
Author,
AuthorBio,
AuthorBioMetadata,
AuthorType,
Blog,
Comment,
Company,
Entry,
ProjectType,
Questionnaire,
ResearchProject,
TaggedItem,
)

Expand Down Expand Up @@ -112,41 +109,6 @@ class Meta:
name = factory.LazyAttribute(lambda x: faker.name())


class ArtProjectFactory(factory.django.DjangoModelFactory):
class Meta:
model = ArtProject

topic = factory.LazyAttribute(lambda x: faker.catch_phrase())
artist = factory.LazyAttribute(lambda x: faker.name())
project_type = factory.SubFactory(ProjectTypeFactory)


class ResearchProjectFactory(factory.django.DjangoModelFactory):
class Meta:
model = ResearchProject

topic = factory.LazyAttribute(lambda x: faker.catch_phrase())
supervisor = factory.LazyAttribute(lambda x: faker.name())
project_type = factory.SubFactory(ProjectTypeFactory)


class CompanyFactory(factory.django.DjangoModelFactory):
class Meta:
model = Company
skip_postgeneration_save = True

name = factory.LazyAttribute(lambda x: faker.company())
current_project = factory.SubFactory(ArtProjectFactory)

@factory.post_generation
def future_projects(self, create, extracted, **kwargs):
if not create: # pragma: no cover
return
if extracted:
for project in extracted:
self.future_projects.add(project)


class QuestionnaireFactory(factory.django.DjangoModelFactory):
class Meta:
model = Questionnaire
Expand Down
56 changes: 56 additions & 0 deletions example/migrations/0015_remove_company_current_project_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 6.0.7 on 2026-10-08 02:18

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("example", "0014_questionnaire_metadata"),
]

operations = [
migrations.RemoveField(
model_name="company",
name="current_project",
),
migrations.RemoveField(
model_name="company",
name="future_projects",
),
migrations.RemoveField(
model_name="labresults",
name="author",
),
migrations.RemoveField(
model_name="labresults",
name="research_project",
),
migrations.RemoveField(
model_name="project",
name="polymorphic_ctype",
),
migrations.RemoveField(
model_name="project",
name="project_type",
),
migrations.RemoveField(
model_name="researchproject",
name="project_ptr",
),
migrations.DeleteModel(
name="ArtProject",
),
migrations.DeleteModel(
name="Company",
),
migrations.DeleteModel(
name="LabResults",
),
migrations.DeleteModel(
name="Project",
),
migrations.DeleteModel(
name="ResearchProject",
),
]
44 changes: 0 additions & 44 deletions example/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
from polymorphic.models import PolymorphicModel


class BaseModel(models.Model):
Expand Down Expand Up @@ -139,49 +138,6 @@ class Meta:
ordering = ("id",)


class Project(PolymorphicModel):
topic = models.CharField(max_length=30)
project_type = models.ForeignKey(ProjectType, null=True, on_delete=models.CASCADE)


class ArtProject(Project):
artist = models.CharField(max_length=30)
description = models.CharField(max_length=100, null=True)


class ResearchProject(Project):
supervisor = models.CharField(max_length=30)


class LabResults(models.Model):
research_project = models.ForeignKey(
ResearchProject, related_name="lab_results", on_delete=models.CASCADE
)
date = models.DateField()
measurements = models.TextField()
author = models.ForeignKey(
Author,
null=True,
blank=True,
on_delete=models.CASCADE,
related_name="lab_results",
)

class Meta:
ordering = ("id",)


class Company(models.Model):
name = models.CharField(max_length=100)
current_project = models.ForeignKey(
Project, related_name="companies", on_delete=models.CASCADE
)
future_projects = models.ManyToManyField(Project)

def __str__(self):
return self.name


class Questionnaire(models.Model):
name = models.CharField(max_length=100)
questions = models.JSONField()
Expand Down
83 changes: 0 additions & 83 deletions example/serializers.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
from datetime import datetime

from rest_framework import fields as drf_fields
from rest_framework import serializers as drf_serilazers

from rest_framework_json_api import relations, serializers

from example.models import (
ArtProject,
Author,
AuthorBio,
AuthorBioMetadata,
AuthorType,
Blog,
Comment,
Company,
Entry,
LabResults,
Project,
ProjectType,
Questionnaire,
ResearchProject,
TaggedItem,
)

Expand Down Expand Up @@ -347,83 +341,6 @@ class BaseProjectSerializer(serializers.ModelSerializer):
}


class ArtProjectSerializer(BaseProjectSerializer):
class Meta:
model = ArtProject
exclude = ("polymorphic_ctype",)


class ResearchProjectSerializer(BaseProjectSerializer):
# testing exclusive related field on inherited polymorphic model
lab_results = relations.ResourceRelatedField(many=True, read_only=True)

class Meta:
model = ResearchProject
exclude = ("polymorphic_ctype",)


class LabResultsSerializer(serializers.ModelSerializer):
included_serializers = {"author": AuthorSerializer}

class Meta:
model = LabResults
fields = ("date", "measurements", "author")


class ProjectSerializer(serializers.PolymorphicModelSerializer):
included_serializers = {
"project_type": ProjectTypeSerializer,
}
polymorphic_serializers = [ArtProjectSerializer, ResearchProjectSerializer]

class Meta:
model = Project
exclude = ("polymorphic_ctype",)


class CurrentProjectRelatedField(relations.PolymorphicResourceRelatedField):
def get_attribute(self, instance):
obj = super().get_attribute(instance)

is_art = self.field_name == "current_art_project" and isinstance(
obj, ArtProject
)
is_res = self.field_name == "current_research_project" and isinstance(
obj, ResearchProject
)

if is_art or is_res:
return obj

raise drf_fields.SkipField()


class CompanySerializer(serializers.ModelSerializer):
current_project = relations.PolymorphicResourceRelatedField(
ProjectSerializer, queryset=Project.objects.all()
)
current_art_project = CurrentProjectRelatedField(
ProjectSerializer, source="current_project", read_only=True
)
current_research_project = CurrentProjectRelatedField(
ProjectSerializer, source="current_project", read_only=True
)
future_projects = relations.PolymorphicResourceRelatedField(
ProjectSerializer, queryset=Project.objects.all(), many=True
)

included_serializers = {
"current_project": ProjectSerializer,
"future_projects": ProjectSerializer,
"current_art_project": ProjectSerializer,
"current_research_project": ProjectSerializer,
}

class Meta:
model = Company
fields = "__all__"


class QuestionSerializer(serializers.Serializer):
text = serializers.CharField()
required = serializers.BooleanField(default=False)
Expand Down
Loading
Loading