Skip to content

Commit

Permalink
Merge pull request #893 from tfranzel/improve_slugrelatedfield
Browse files Browse the repository at this point in the history
Treat SlugRelatedField analog to PrimaryKeyRelatedField #854
  • Loading branch information
tfranzel authored Dec 9, 2022
2 parents 2a2c175 + 0307ff9 commit ea48b27
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 10 deletions.
16 changes: 9 additions & 7 deletions drf_spectacular/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,11 +659,13 @@ def _map_serializer_field(self, field, direction, bypass_extensions=False):
schema.pop('readOnly', None)
return append_meta(build_array_type(schema), meta)

if isinstance(field, serializers.PrimaryKeyRelatedField):
if isinstance(field, (serializers.PrimaryKeyRelatedField, serializers.SlugRelatedField)):
# SlugRelatedField is essentially a non-pk version of PrimaryKeyRelatedField.
is_slug = isinstance(field, serializers.SlugRelatedField)
# 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:
if getattr(field, 'queryset', None) is not None and not is_slug:
model_field = field.queryset.model._meta.pk
else:
if isinstance(field.parent, serializers.ManyRelatedField):
Expand All @@ -674,14 +676,17 @@ def _map_serializer_field(self, field, direction, bypass_extensions=False):
source = field.source.split('.')
else:
warn(
f'Could not derive type for under-specified PrimaryKeyRelatedField '
f'Could not derive type for under-specified {field.__class__.__name__} '
f'"{field.field_name}". The serializer has no associated model (Meta class) '
f'and this particular field has no type without a model association. Consider '
f'changing the field or adding a Meta class. defaulting to string.'
)
return append_meta(build_basic_type(OpenApiTypes.STR), meta)

# estimates the relating model field and jumps to it's target model PK field.
if is_slug:
source.append(field.slug_field)

# estimates the relating model field and jumps to its target model PK field.
# also differentiate as source can be direct (pk) or relation field (model).
model_field = follow_field_source(model, source)
if callable(model_field):
Expand All @@ -697,9 +702,6 @@ def _map_serializer_field(self, field, direction, bypass_extensions=False):
if isinstance(field, serializers.StringRelatedField):
return append_meta(build_basic_type(OpenApiTypes.STR), meta)

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

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

Expand Down
11 changes: 9 additions & 2 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
class Aux(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
field_foreign = models.ForeignKey('Aux', null=True, on_delete=models.CASCADE)
url = models.URLField(unique=True)


class AuxSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -191,7 +192,10 @@ def get_field_method_object(self, obj) -> dict:

# extra related fields
field_related_slug = serializers.SlugRelatedField(
read_only=True, source='field_foreign', slug_field='id'
read_only=True, source='field_foreign', slug_field='url',
) # type: ignore
field_related_slug_many = serializers.SlugRelatedField(
many=True, read_only=True, source='field_m2m', slug_field='url',
) # type: ignore
field_related_string = serializers.StringRelatedField(
source='field_foreign'
Expand Down Expand Up @@ -292,7 +296,10 @@ def test_fields(no_warnings):
@pytest.mark.urls(__name__)
@pytest.mark.django_db
def test_model_setup_is_valid():
aux = Aux(id='0ac6930d-87f4-40e8-8242-10a3ed31a335')
aux = Aux(
id='0ac6930d-87f4-40e8-8242-10a3ed31a335',
url='https://xkcd.com'
)
aux.save()

m = AllFields(
Expand Down
13 changes: 13 additions & 0 deletions tests/test_fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ components:
$ref: '#/components/schemas/Aux'
field_related_slug:
type: string
format: uri
readOnly: true
field_related_slug_many:
type: array
items:
type: string
format: uri
readOnly: true
field_related_string:
type: string
Expand Down Expand Up @@ -331,6 +338,7 @@ components:
- field_regex
- field_related_hyperlink
- field_related_slug
- field_related_slug_many
- field_related_string
- field_slug
- field_smallint
Expand All @@ -357,12 +365,17 @@ components:
type: string
format: uuid
readOnly: true
url:
type: string
format: uri
maxLength: 200
field_foreign:
type: string
format: uuid
nullable: true
required:
- id
- url
securitySchemes:
basicAuth:
type: http
Expand Down
6 changes: 5 additions & 1 deletion tests/test_fields_response.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@
"field_list_serializer": [
{
"id": "0ac6930d-87f4-40e8-8242-10a3ed31a335",
"url": "https://xkcd.com",
"field_foreign": null
}
],
"field_related_slug": "0ac6930d-87f4-40e8-8242-10a3ed31a335",
"field_related_slug": "https://xkcd.com",
"field_related_slug_many": [
"https://xkcd.com"
],
"field_related_string": "Aux object (0ac6930d-87f4-40e8-8242-10a3ed31a335)",
"field_related_hyperlink": "http://testserver/aux/0ac6930d-87f4-40e8-8242-10a3ed31a335/",
"field_identity_hyperlink": "http://testserver/allfields/1/",
Expand Down

0 comments on commit ea48b27

Please sign in to comment.