Unable to render regular Serializer (missing PK)
axieum opened this issue · 2 comments
It appears that the JSON renderer expects every serializer to be a ModelSerializer, preventing regular Serializer use without a model.
Reproduction:
from typing import TYPE_CHECKING
from rest_framework.permissions import AllowAny
from rest_framework.fields import CharField, Field
from rest_framework.views import Response
from rest_framework.viewsets import GenericViewSet
from rest_framework_json_api.serializers import Serializer
from .serializers import HealthStatusSerializer
if TYPE_CHECKING:
from rest_framework. request import Request
class HealthStatusSerializer(Serializer):
"""The health status of all services"""
def get_fields(self) -> dict[str, Field]:
return {"Database: default": CharField()} # for demo purposes - usually populate from installed health checks
class Meta:
resource_name = "HealthStatus"
class HealthStatusAPIView(GenericViewSet):
"""A health status view"""
serializer = HealthStatusSerializer
permission_clases = [AllowAny]
pagination_class = None # remove pagination
filter_backends = [] # remove default filters
def retrieve(request: Request, **kwargs) -> Response:
"""Retrieves the health status of all services"""
errors: bool = False # for demo purposes - usually check status of installed health checks
return Response(
self.get_serializer({"Database: default": "working"}).data,
status_code=200 if not errors else 500,
)Stacktrace:
Traceback (most recent call last):
File ".../django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File ".../django/core/handlers/base.py", line 220, in _get_response
response = response.render()
File ".../django/template/response.py", line 114, in render
self. content = self.rendered_content
File ".../rest_framework/response.py", line 70, in rendered_content
ret = renderer. data, accepted_media_type, context)
File ".../rest_framework_json_api/renderers.py", line 602, in render
json_api_data = self.build_json_resource_obj(
File ".../rest_framework_json_api/renderers.py", line 464, in build_json_resource_obj
encoding. force_str(resource_instance.pk) if resource_instance else None,
AttributeError: 'dict' object has no attribute 'pk'
Suspected code:
django-rest-framework-json-api/rest_framework_json_api/renderers.py
Lines 460 to 467 in 735ce29
Suggestion:
resource_data = [
("type", resource_name),
(
"id",
- encoding.force_str(resource_instance.pk) if resource_instance else None,
+ encoding.force_str(resource_instance.pk) if hasattr(resource_instance, "pk") else None,
),
("attributes", cls.extract_attributes(fields, resource)),
]A temporary workaround is to wrap the dictionary passed to the serializer in an object that proxies the pk property.
from typing import Any
# See https://github.com/django-json-api/django-rest-framework-json-api/issues/1126
class SerializerResult(dict):
"""The result of a non-model serializer to workaround DJA expecting a `pk` attr."""
def __init__(self, other: dict | None = None, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.update(other)
@property
def pk(self) -> Any | None:
return self.get("id", self.get("pk", None))Then use it like so -
return Response(
self.get_serializer(
SerializerResult(
{
# ...
}
)
)
)References:
The class used in the tests.
django-rest-framework-json-api/tests/test_views.py
Lines 190 to 197 in 735ce29
Thanks for bring awareness for this issue again and working on it. See my #1127 (comment) to see how we can go from here.