简体   繁体   中英

Django Rest Framework ParentRelatedField metadata choices

Hello Everyone!

I have latest django-rest-framework and I'm trying to make serializer show possible choices for each field on OPTIONS request.

Here's part of my model

# models.py
class Task(models.Model):
    parent = models.ForeignKey('self',
                           blank=True, null=True, related_name='child_tasks')
    title = models.CharField(max_length=128)
    status = models.CharField(max_length=16, choices=STATUS_CHOISES, default='new')
    priority = models.CharField(max_length=16, choices=PRIORITY_CHOISES, default='1')
    chief = models.ForeignKey('users.SystemUser', related_name='tasks',
                           blank=True, null=True)

And here's serializer

# serializers.py
class ParentRelatedField(serializers.PrimaryKeyRelatedField):
    def get_queryset(self):
        obj = self.context['view'].get_object()
        return Task.objects.exclude(pk=obj.pk)


def get_user_choices():
    return tuple([(i.id, i.system_name) for i in SystemUser.objects.all()])


class TaskDetailSerializer(serializers.Serializer):
    title = serializers.CharField()
    parent = ParentRelatedField(
        required=False, allow_null=True
    )
    status = serializers.ChoiceField(choices=STATUS_CHOISES)
    priority = serializers.ChoiceField(choices=PRIORITY_CHOISES)
    chief = serializers.ChoiceField(choices=get_user_choices(), required=False)

I achieved that for chief field using get_user_choices function, so i get:

"chief": {
    "type": "choice",
    "required": false,
    "read_only": false,
    "label": "Chief",
    "choices": [
        {
            "value": 1,
            "display_name": "First User Name"
        }
    ]
}

ParentRelatedField works great for validation, but not for metadata:

"parent": {
    "type": "field",
    "required": false,
    "read_only": false,
    "label": "Parent"
}

I can't use ChoiceField with function (like in chief ) for that because parent choice must exclude current Task object.

Solved the problem.

Solution was found at drf 3.4 announcement and drf issue comments .

I changed my field to make it more universal

class SelfExcludingRelatedField(serializers.PrimaryKeyRelatedField):
    def get_queryset(self):
        obj = self.context['view'].get_object()
        return self.queryset.exclude(pk=obj.pk)

Then wrote custom metadata class (copied from github).

class CustomMetadata(SimpleMetadata):
    def get_field_info(self, field):
        field_info = super().get_field_info(field)

        if (not field_info.get('read_only') and
                isinstance(field, SelfExcludingRelatedField) and
                hasattr(field, 'choices')):
            field_info['choices'] = [
                {
                    'value': choice_value,
                    'display_name': choice_name
                }
                for choice_value, choice_name in field.choices.items()
            ]

        return field_info

And added it to settings :

REST_FRAMEWORK = {
    'DEFAULT_METADATA_CLASS': 'api_v0.metadata.CustomMetadata',
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM