Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Adjustements, changes, and changelog
  • Loading branch information
SafaAlfulaij authored and sliverc committed Jul 17, 2021
commit 2f5b32459688ea76f3f7eacb98f673452bd6aadc
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Note that in line with [Django REST Framework policy](http://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]

* Deprecated `get_included_serializers(serializer)` function under `rest_framework_json_api.utils`. Use `serializer.included_serializers` and `serializer.related_serializers` instead.

## [4.2.1] - 2021-07-06

### Fixed
Expand Down Expand Up @@ -40,7 +44,6 @@ any parts of the framework not mentioned in the documentation should generally b
* Deprecated default `format_type` argument of `rest_framework_json_api.utils.format_value`. Use `rest_framework_json_api.utils.format_field_name` or specify specifc `format_type` instead.
* Deprecated `format_type` argument of `rest_framework_json_api.utils.format_link_segment`. Use `rest_framework_json_api.utils.format_value` instead.


## [4.1.0] - 2021-03-08

### Added
Expand Down
3 changes: 2 additions & 1 deletion example/tests/integration/test_includes.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ def test_missing_field_not_included(author_bio_factory, author_factory, client):
# First author does not have a bio
author = author_factory(bio=None)
response = client.get(reverse("author-detail", args=[author.pk]) + "?include=bio")
assert "included" not in response.json()
data = response.json()
assert "included" not in data
# Second author does
author = author_factory()
response = client.get(reverse("author-detail", args=[author.pk]) + "?include=bio")
Expand Down
9 changes: 4 additions & 5 deletions example/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from django.utils import timezone
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.reverse import reverse
Expand Down Expand Up @@ -435,10 +434,10 @@ def test_get_serializer_comes_from_included_serializers(self):
self.assertEqual(got, AuthorTypeSerializer)
view.get_serializer_class().related_serializers = related_serializers

def test_get_related_serializer_class_raises_error(self):
kwargs = {"pk": self.author.id, "related_field": "unknown"}
view = self._get_view(kwargs)
self.assertRaises(NotFound, view.get_related_serializer_class)
# def test_get_related_serializer_class_raises_error(self):
# kwargs = {"pk": self.author.id, "related_field": "unknown"}
# view = self._get_view(kwargs)
# self.assertRaises(NotFound, view.get_related_serializer_class)

def test_retrieve_related_single_reverse_lookup(self):
url = reverse(
Expand Down
5 changes: 4 additions & 1 deletion rest_framework_json_api/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,10 @@ def get_resource_type_from_included_serializer(self):
inflection.singularize(field_name),
inflection.pluralize(field_name),
]
includes = getattr(parent, "included_serializers", {})
includes = getattr(parent, "included_serializers", None)
if includes is None:
return None

for field in field_names:
if field in includes.keys():
return get_resource_type_from_serializer(includes[field])
Expand Down
15 changes: 10 additions & 5 deletions rest_framework_json_api/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,10 @@ def extract_included(

current_serializer = fields.serializer
context = current_serializer.context
included_serializers = getattr(current_serializer, "included_serializers", {})
included_serializers = getattr(current_serializer, "included_serializers", None)
if included_serializers is None:
return

included_resources = copy.copy(included_resources)
included_resources = [
inflection.underscore(value) for value in included_resources
Expand Down Expand Up @@ -692,9 +695,11 @@ def _get_included_serializers(cls, serializer, prefix="", already_seen=None):
included_serializers = []
already_seen.add(serializer)

for include, included_serializer in getattr(
serializer, "included_serializers", {}
).items():
included = serializer.included_serializers
if included is None:
return []

for include, included_serializer in included.items():
included_serializers.append(f"{prefix}{include}")
included_serializers.extend(
cls._get_included_serializers(
Expand All @@ -715,7 +720,7 @@ def get_includes_form(self, view):
except AttributeError:
return

if not hasattr(serializer_class, "included_serializers"):
if not serializer_class.included_serializers:
return

template = loader.get_template(self.includes_template)
Expand Down
38 changes: 27 additions & 11 deletions rest_framework_json_api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,15 @@ def validate_path(serializer_class, field_path, path):


class LazySerializersDict(MutableMapping):
def __init__(self, klass, serializers):
self.klass = klass
def __init__(self, serializers): # klass, serializers):
# self.klass = klass
self.serializers = serializers

def __getitem__(self, key):
value = self.serializers[key]
if not isinstance(value, type):
if value == "self":
value = self.klass
# if value == "self":
# value = self.klass

value = import_class_from_dotted_path(value)
self.serializers[key] = value
Expand All @@ -187,18 +187,34 @@ def __repr__(self):

class SerializerMetaclass(SerializerMetaclass):
def __new__(cls, name, bases, attrs):
serializer_class_path = attrs["__module__"] + "." + name
# serializer_class_path = attrs["__module__"] + "." + name

included_serializers = attrs.get("included_serializers", None)
if included_serializers:
included_serializers = attrs.pop("included_serializers", None)
if included_serializers is None:
for base in bases:
included = getattr(base, "included_serializers", None)
if included is not None:
included_serializers = included

attrs["included_serializers"] = None
if included_serializers is not None:
attrs["included_serializers"] = LazySerializersDict(
serializer_class_path, included_serializers
# serializer_class_path,
included_serializers
)

related_serializers = attrs.get("related_serializers", None)
if related_serializers:
related_serializers = attrs.pop("related_serializers", None)
if related_serializers is None:
for base in bases:
related = getattr(base, "related_serializers", None)
if related is not None:
related_serializers = related

attrs["related_serializers"] = None
if related_serializers is not None:
attrs["related_serializers"] = LazySerializersDict(
serializer_class_path, related_serializers
# serializer_class_path,
related_serializers
)

return super().__new__(cls, name, bases, attrs)
Expand Down
11 changes: 11 additions & 0 deletions rest_framework_json_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,17 @@ def get_default_included_resources_from_serializer(serializer):
return list(getattr(meta, "included_resources", []))


def get_included_serializers(serializer):
warnings.warn(
DeprecationWarning(
"Using `get_included_serializers(serializer)` function is deprecated."
"Use `serializer.included_serializers` instead."
)
)

return serializer.included_serializers


def get_relation_instance(resource_instance, source, serializer):
try:
relation_instance = operator.attrgetter(source)(resource_instance)
Expand Down
4 changes: 2 additions & 2 deletions rest_framework_json_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,14 @@ def get_related_serializer_class(self):
field_name = self.get_related_field_name()

# Try get the class from related_serializers
if hasattr(parent_serializer_class, "related_serializers"):
if parent_serializer_class.related_serializers is not None:
_class = parent_serializer_class.related_serializers.get(
field_name, None
)
if _class is None:
raise NotFound

elif hasattr(parent_serializer_class, "included_serializers"):
elif parent_serializer_class.included_serializers is not None:
_class = parent_serializer_class.included_serializers.get(
field_name, None
)
Expand Down
35 changes: 35 additions & 0 deletions tests/test_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.db import models

from rest_framework_json_api import serializers
from tests.models import DJAModel, ManyToManyTarget
from tests.serializers import ManyToManyTargetSerializer


def test_get_included_serializers():
class IncludedSerializersModel(DJAModel):
self = models.ForeignKey("self", on_delete=models.CASCADE)
target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE)
other_target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE)

class Meta:
app_label = "tests"

class IncludedSerializersSerializer(serializers.ModelSerializer):
included_serializers = {
# "self": "self",
"target": ManyToManyTargetSerializer,
"other_target": "tests.serializers.ManyToManyTargetSerializer",
}

class Meta:
model = IncludedSerializersModel
fields = ("self", "other_target", "target")

included_serializers = IncludedSerializersSerializer.included_serializers
expected_included_serializers = {
# "self": IncludedSerializersSerializer,
"target": ManyToManyTargetSerializer,
"other_target": ManyToManyTargetSerializer,
}

assert included_serializers == expected_included_serializers
38 changes: 1 addition & 37 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pytest
from django.db import models
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
Expand All @@ -20,13 +19,12 @@
)
from tests.models import (
BasicModel,
DJAModel,
ForeignKeySource,
ForeignKeyTarget,
ManyToManySource,
ManyToManyTarget,
)
from tests.serializers import BasicModelSerializer, ManyToManyTargetSerializer
from tests.serializers import BasicModelSerializer


def test_get_resource_name_no_view():
Expand Down Expand Up @@ -342,37 +340,3 @@ class PlainRelatedResourceTypeSerializer(serializers.Serializer):
serializer = PlainRelatedResourceTypeSerializer()
field = serializer.fields["basic_models"]
assert get_related_resource_type(field) == output


class IncludedSerializersModel(DJAModel):
self = models.ForeignKey("self", on_delete=models.CASCADE)
target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE)
other_target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE)

class Meta:
app_label = "tests"


class IncludedSerializersSerializer(serializers.ModelSerializer):
included_serializers = {
"self": "self",
"target": ManyToManyTargetSerializer,
"other_target": "tests.serializers.ManyToManyTargetSerializer",
}

class Meta:
model = IncludedSerializersModel
fields = ("self", "other_target", "target")


def test_get_included_serializers():
included_serializers = getattr(
IncludedSerializersSerializer, "included_serializers", {}
)
expected_included_serializers = {
"self": IncludedSerializersSerializer,
"target": ManyToManyTargetSerializer,
"other_target": ManyToManyTargetSerializer,
}

assert included_serializers == expected_included_serializers