Skip to content

Commit

Permalink
bugfix read-only many2many relation processing #79
Browse files Browse the repository at this point in the history
  • Loading branch information
tfranzel committed Jun 7, 2020
1 parent 6ea5223 commit e7c8108
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 2 deletions.
10 changes: 8 additions & 2 deletions drf_spectacular/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,18 +397,24 @@ def _map_serializer_field(self, field, direction):
schema = self._map_serializer_field(field.child_relation, direction)
# remove hand-over initkwargs applying only to outer scope
schema.pop('description', None)
schema.pop('readOnly', None)
return append_meta(build_array_type(schema), meta)

if isinstance(field, serializers.PrimaryKeyRelatedField):
# read_only fields do not have a Manager by design. go around and get field
# from parent. also avoid calling Manager. __bool__ as it might be customized
# to hit the database.
if getattr(field, 'queryset', None) is not None:
schema = self._map_model_field(field.queryset.model._meta.pk, direction)
model_field = field.queryset.model._meta.pk
else:
schema = self._map_model_field(field.parent.Meta.model._meta.pk, direction)
if isinstance(field.parent, serializers.ManyRelatedField):
model_field = field.parent.parent.Meta.model._meta.pk
else:
model_field = field.parent.Meta.model._meta.pk

# primary keys are usually non-editable (readOnly=True) and map_model_field correctly
# signals that attribute. however this does not apply in the context of relations.
schema = self._map_model_field(model_field, direction)
schema.pop('readOnly', None)
return append_meta(schema, meta)

Expand Down
26 changes: 26 additions & 0 deletions tests/test_regressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,3 +591,29 @@ class XView(generics.ListCreateAPIView):

assert schema['components']['schemas']['X']['properties']['file']['format'] == 'uri'
assert schema['components']['schemas']['XRequest']['properties']['file']['format'] == 'binary'


def test_read_only_many_related_field(no_warnings):
class ManyRelatedTargetModel(models.Model):
field = models.IntegerField()

class ManyRelatedModel(models.Model):
field_m2m = models.ManyToManyField(ManyRelatedTargetModel)
field_m2m_ro = models.ManyToManyField(ManyRelatedTargetModel)

class XSerializer(serializers.ModelSerializer):
class Meta:
model = ManyRelatedModel
fields = '__all__'
read_only_fields = ['field_m2m_ro']

class XAPIView(APIView):
@extend_schema(responses=XSerializer)
def get(self, request):
pass # pragma: no cover

schema = generate_schema('x', view=XAPIView)
assert schema['components']['schemas']['X']['properties']['field_m2m_ro']['readOnly'] is True
# readOnly only needed on outer object, not in items
assert 'readOnly' not in schema['components']['schemas']['X']['properties']['field_m2m_ro']['items']
assert 'readOnly' not in schema['components']['schemas']['X']['properties']['field_m2m']

0 comments on commit e7c8108

Please sign in to comment.