Skip to content

Commit

Permalink
add missing related serializer fields #15
Browse files Browse the repository at this point in the history
  • Loading branch information
tfranzel committed Mar 28, 2020
1 parent e3c2edb commit 887127d
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 15 deletions.
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ coverage:
status:
project:
default:
threshold: 5%
threshold: 1%
patch:
default:
threshold: 100%
12 changes: 12 additions & 0 deletions drf_spectacular/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,18 @@ def _map_serializer_field(self, method, field):
get_field_from_model(model, model.id)
)

if isinstance(field, serializers.StringRelatedField):
return build_basic_type(OpenApiTypes.STR)

if isinstance(field, serializers.SlugRelatedField):
return build_basic_type(OpenApiTypes.STR)

if isinstance(field, serializers.HyperlinkedIdentityField):
return build_basic_type(OpenApiTypes.URI)

if isinstance(field, serializers.HyperlinkedRelatedField):
return build_basic_type(OpenApiTypes.URI)

# ChoiceFields (single and multiple).
# Q:
# - Is 'type' required?
Expand Down
64 changes: 53 additions & 11 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import pytest
from django.core.files.base import ContentFile
from django.db import models
from django.urls import reverse
from rest_framework import serializers, viewsets
from rest_framework.renderers import JSONRenderer
from rest_framework.routers import SimpleRouter
from rest_framework.test import APIClient

from tests import assert_schema, generate_schema

Expand Down Expand Up @@ -62,13 +64,43 @@ class AllFields(models.Model):
def field_model_property_float(self) -> float:
return 1.337

@property
def field_list(self):
return [1.1, 2.2, 3.3]


class AllFieldsSerializer(serializers.ModelSerializer):
field_method_float = serializers.SerializerMethodField()

def get_field_method_float(self, obj) -> float:
return 1.3456

field_method_object = serializers.SerializerMethodField()

def get_field_method_object(self, obj) -> dict:
return {'key': 'value'}

field_regex = serializers.RegexField(r'^[a-zA-z0-9]{10}\-[a-z]')

# composite fields
field_list = serializers.ListField(
child=serializers.FloatField(), min_length=3, max_length=100,
)

# extra related fields
field_related_slug = serializers.SlugRelatedField(
read_only=True, source='field_foreign', slug_field='id'
)
field_related_string = serializers.StringRelatedField(
source='field_foreign'
)
field_related_hyperlink = serializers.HyperlinkedRelatedField(
read_only=True, source='field_foreign', view_name='aux-detail'
)
field_identity_hyperlink = serializers.HyperlinkedIdentityField(
read_only=True, view_name='allfields-detail'
)

# read only - model traversal
field_read_only_nav_uuid = serializers.ReadOnlyField(source='field_foreign.id')
field_read_only_nav_uuid_3steps = serializers.ReadOnlyField(
Expand All @@ -80,32 +112,44 @@ class AllFieldsSerializer(serializers.ModelSerializer):

field_model_property_float = serializers.ReadOnlyField()

def get_field_method_float(self, obj) -> float:
return 1.3456
class Meta:
fields = '__all__'
model = AllFields

def get_field_method_object(self, obj) -> dict:
return {'key': 'value'}

class AuxSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = AllFields
model = Aux


class AllFieldsModelViewset(viewsets.ReadOnlyModelViewSet):
serializer_class = AllFieldsSerializer
queryset = AllFields.objects.none()
queryset = AllFields.objects.all()

def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)


class AuxModelViewset(viewsets.ReadOnlyModelViewSet):
serializer_class = AuxSerializer
queryset = Aux.objects.all()


router = SimpleRouter()
router.register('allfields', AllFieldsModelViewset)
router.register('aux', AuxModelViewset)
urlpatterns = router.urls


def test_fields(no_warnings):
assert_schema(
generate_schema('allfields', AllFieldsModelViewset),
'tests/test_fields.yml'
)


@pytest.mark.urls(__name__)
@pytest.mark.django_db
def test_model_setup_is_valid():
aux = Aux()
Expand Down Expand Up @@ -143,7 +187,5 @@ def test_model_setup_is_valid():
m.save()
m.field_m2m.add(aux)

JSONRenderer().render(
AllFieldsSerializer(m).data,
accepted_media_type='application/json; indent=4'
).decode()
response = APIClient().get(reverse('allfields-detail', args=(m.pk,)))
assert response.status_code == 200
22 changes: 22 additions & 0 deletions tests/test_fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,27 @@ components:
field_regex:
type: string
pattern: ^[a-zA-z0-9]{10}\-[a-z]
field_list:
type: array
items:
type: number
format: float
maxItems: 100
minItems: 3
field_related_slug:
type: string
readOnly: true
field_related_string:
type: string
readOnly: true
field_related_hyperlink:
type: string
format: uri
readOnly: true
field_identity_hyperlink:
type: string
format: uri
readOnly: true
field_read_only_nav_uuid:
type: string
format: uuid
Expand Down Expand Up @@ -158,6 +179,7 @@ components:
format: uuid
required:
- field_regex
- field_list
- field_int
- field_float
- field_bool
Expand Down
5 changes: 2 additions & 3 deletions tests/test_view.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from unittest import mock

import pytest
import yaml
from django.conf.urls import url
from rest_framework.test import APIClient
Expand All @@ -10,7 +9,7 @@
urlpatterns = [url(r'^api/schema$', SpectacularAPIView.as_view(), name='schema')]


@mock.patch('tests.urls.urlpatterns', urlpatterns)
@pytest.mark.urls(__name__)
def test_spectacular_view(no_warnings):
response = APIClient().get('/api/schema')
assert response.status_code == 200
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ envlist =
skip_missing_interpreters = true

[coverage:report]
precision = 2
exclude_lines =
pragma: no cover
except ImportError
Expand Down

0 comments on commit 887127d

Please sign in to comment.