Skip to content

Commit e17ea57

Browse files
authored
Verified in all fields whether reserved field names are used (#992)
In meta class only specifically declared fields in serializer are accessible. In case of `ModelSerializer` fields may be just on the model or if a user has overwritten what fields are being returned during runtime DJA won't notice. So there does not seem to be another way then simply hooking into the `get_fields` method to check for reserved field names.
1 parent ee679ee commit e17ea57

File tree

2 files changed

+26
-19
lines changed

2 files changed

+26
-19
lines changed

rest_framework_json_api/serializers.py

+24-19
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,27 @@ def validate_path(serializer_class, field_path, path):
153153
super().__init__(*args, **kwargs)
154154

155155

156+
class ReservedFieldNamesMixin:
157+
"""Ensures that reserved field names are not used and an error raised instead."""
158+
159+
_reserved_field_names = {"meta", "results"}
160+
161+
def get_fields(self):
162+
fields = super().get_fields()
163+
164+
found_reserved_field_names = self._reserved_field_names.intersection(
165+
fields.keys()
166+
)
167+
if found_reserved_field_names:
168+
raise AttributeError(
169+
f"Serializer class {self.__class__.__module__}.{self.__class__.__qualname__} "
170+
f"uses following reserved field name(s) which is not allowed: "
171+
f"{', '.join(sorted(found_reserved_field_names))}"
172+
)
173+
174+
return fields
175+
176+
156177
class LazySerializersDict(Mapping):
157178
"""
158179
A dictionary of serializers which lazily import dotted class path and self.
@@ -184,25 +205,6 @@ def __repr__(self):
184205

185206

186207
class SerializerMetaclass(SerializerMetaclass):
187-
188-
_reserved_field_names = {"meta", "results"}
189-
190-
@classmethod
191-
def _get_declared_fields(cls, bases, attrs):
192-
fields = super()._get_declared_fields(bases, attrs)
193-
194-
found_reserved_field_names = cls._reserved_field_names.intersection(
195-
fields.keys()
196-
)
197-
if found_reserved_field_names:
198-
raise AttributeError(
199-
f"Serializer class {attrs['__module__']}.{attrs['__qualname__']} uses "
200-
f"following reserved field name(s) which is not allowed: "
201-
f"{', '.join(sorted(found_reserved_field_names))}"
202-
)
203-
204-
return fields
205-
206208
def __new__(cls, name, bases, attrs):
207209
serializer = super().__new__(cls, name, bases, attrs)
208210

@@ -228,6 +230,7 @@ def __new__(cls, name, bases, attrs):
228230
class Serializer(
229231
IncludedResourcesValidationMixin,
230232
SparseFieldsetsMixin,
233+
ReservedFieldNamesMixin,
231234
Serializer,
232235
metaclass=SerializerMetaclass,
233236
):
@@ -251,6 +254,7 @@ class Serializer(
251254
class HyperlinkedModelSerializer(
252255
IncludedResourcesValidationMixin,
253256
SparseFieldsetsMixin,
257+
ReservedFieldNamesMixin,
254258
HyperlinkedModelSerializer,
255259
metaclass=SerializerMetaclass,
256260
):
@@ -271,6 +275,7 @@ class HyperlinkedModelSerializer(
271275
class ModelSerializer(
272276
IncludedResourcesValidationMixin,
273277
SparseFieldsetsMixin,
278+
ReservedFieldNamesMixin,
274279
ModelSerializer,
275280
metaclass=SerializerMetaclass,
276281
):

tests/test_serializers.py

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class ReservedFieldNamesSerializer(serializers.Serializer):
4343
meta = serializers.CharField()
4444
results = serializers.CharField()
4545

46+
ReservedFieldNamesSerializer().fields
47+
4648
assert str(e.value) == (
4749
"Serializer class tests.test_serializers.test_reserved_field_names.<locals>."
4850
"ReservedFieldNamesSerializer uses following reserved field name(s) which is "

0 commit comments

Comments
 (0)