api/serializers/__init__.py (29 lines of code) (raw):
"""Shared API serializer code."""
from rest_framework.serializers import ErrorDetail, ValidationError
class StrictReadOnlyFieldsMixin:
"""
Raises a validation error (400) if read only fields are in the body of PUT/PATCH
requests.
This class comes from
https://github.com/encode/django-rest-framework/issues/1655#issuecomment-1197033853,
where different solutions to mitigating 200 response codes in read-only fields are
discussed.
"""
def validate(self, attrs):
# Mixins and mypy make for weird code....
attrs = getattr(super(), "validate", lambda x: x)(attrs)
if not (
hasattr(self, "initial_data")
and hasattr(self, "fields")
and hasattr(self, "Meta")
and hasattr(self.Meta, "model")
and hasattr(self.Meta, "read_only_fields")
):
return attrs
# Getting the declared read only fields and read only fields from Meta
read_only_fields = {
field_name for field_name, field in self.fields.items() if field.read_only
}.union(set(getattr(self.Meta, "read_only_fields", set())))
# Getting implicit read only fields that are in the Profile model, but were not
# defined in the serializer. By default, they won't update if put in the body
# of a request, but they still give a 200 response (which we don't want).
implicit_read_only_fields = {
field for field in vars(self.Meta.model) if field not in self.fields
}
received_read_only_fields = set(self.initial_data).intersection(
read_only_fields.union(implicit_read_only_fields)
)
if received_read_only_fields:
errors = {}
for field_name in received_read_only_fields:
errors[field_name] = ErrorDetail(
"This field is read only", code="read_only"
)
raise ValidationError(errors)
return attrs