Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PolymorphicSerializerExtension drops serializers without writable fields #1029

Closed
igorlitvak opened this issue Jul 17, 2023 · 1 comment
Closed
Labels
bug Something isn't working fix confirmation pending issue has been fixed and confirmation from issue reporter is pending

Comments

@igorlitvak
Copy link

igorlitvak commented Jul 17, 2023

Describe the bug
PolymorphicSerializerExtension does not recognize polymorphic child serializers without writable fields as valid serializers for write operations despite django-rest-polymorphic library adding one writable field called resourcetype.

As far as I can understand, this bug occurs in this line because child serializer does not yet have resourcetype field and is treated as an empty schema and deleted.

Library versions
Django==4.2.3
django-polymorphic==3.1.0
django-rest-polymorphic==0.1.10
djangorestframework==3.14.0
drf-spectacular==0.26.3

To Reproduce
I have an app that uses polymorphic models to create asynchonous tasks of different types. Some of those types of tasks require some input from the user to create, but some only require to provide the type of task. The latter causes the problem where drf-spectacular does not generate schema for POST request, despite it having one valid writable field (resourcetype).

The following code snippets show example of this problem:

models.py

from django.db import models

from polymorphic.models import PolymorphicModel


class TaskBase(PolymorphicModel):
    status = models.PositiveIntegerField(choices=[(0, "RUNNING"), (1, "OK"), (2, "ERROR")], editable=False)


class TaskWithoutParameter(TaskBase):
    pass


class TaskWithParameter(TaskBase):
    parameter = models.CharField(max_length=255)

serializers.py

from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer

from .models import TaskWithoutParameter, TaskWithParameter


class TaskWithoutParameterSerializer(serializers.ModelSerializer):
    class Meta:
        model = TaskWithoutParameter
        fields = ('id', 'status')
        read_only_fields = ('id', 'status')


class TaskWithParameterSerializer(serializers.ModelSerializer):
    class Meta:
        model = TaskWithParameter
        fields = ('id', 'status', 'parameter')
        read_only_fields = ('id', 'status')


class PolymorphicTasksSerializer(PolymorphicSerializer):
    model_serializer_mapping = {
        TaskWithoutParameter: TaskWithoutParameterSerializer,
        TaskWithParameter: TaskWithParameterSerializer
    }

Generated schema (note that PolymorphicTasksRequest only has one choice, while PolymorphicTasks has two)

components:
  schemas:
    PolymorphicTasks:
      oneOf:
      - $ref: '#/components/schemas/TaskWithoutParameterTyped'
      - $ref: '#/components/schemas/TaskWithParameterTyped'
      discriminator:
        propertyName: resourcetype
        mapping:
          TaskWithoutParameter: '#/components/schemas/TaskWithoutParameterTyped'
          TaskWithParameter: '#/components/schemas/TaskWithParameterTyped'
    PolymorphicTasksRequest:
      oneOf:
      - $ref: '#/components/schemas/TaskWithParameterTypedRequest'
      discriminator:
        propertyName: resourcetype
        mapping:
          TaskWithParameter: '#/components/schemas/TaskWithParameterTypedRequest'
    TaskWithParameter:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        status:
          type: integer
          readOnly: true
        parameter:
          type: string
          maxLength: 255
      required:
      - id
      - parameter
      - status
    TaskWithParameterRequest:
      type: object
      properties:
        parameter:
          type: string
          minLength: 1
          maxLength: 255
      required:
      - parameter
    TaskWithParameterTyped:
      allOf:
      - type: object
        properties:
          resourcetype:
            type: string
        required:
        - resourcetype
      - $ref: '#/components/schemas/TaskWithParameter'
    TaskWithParameterTypedRequest:
      allOf:
      - type: object
        properties:
          resourcetype:
            type: string
        required:
        - resourcetype
      - $ref: '#/components/schemas/TaskWithParameterRequest'
    TaskWithoutParameter:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        status:
          type: integer
          readOnly: true
      required:
      - id
      - status
    TaskWithoutParameterTyped:
      allOf:
      - type: object
        properties:
          resourcetype:
            type: string
        required:
        - resourcetype
      - $ref: '#/components/schemas/TaskWithoutParameter'

Expected behavior
Serializer without writable fields is added to schema with a single field resourcetype.

@tfranzel tfranzel added bug Something isn't working fix confirmation pending issue has been fixed and confirmation from issue reporter is pending labels Jul 21, 2023
@tfranzel
Copy link
Owner

tfranzel commented Jul 21, 2023

@igorlitvak good catch! PR #1033 should address your issue. Feel free to test it.

tfranzel added a commit that referenced this issue Jul 22, 2023
fix django-polymorphic empty serializer case #1029 #542
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fix confirmation pending issue has been fixed and confirmation from issue reporter is pending
Projects
None yet
Development

No branches or pull requests

2 participants