Перечисление выбора модели в сериализаторе Django Rest Framework

У меня есть модель, которая использует поле выбора Django, например:

class Question(models.Model):
QUESTION_TYPES = (
    (10,'Blurb'),
    (20,'Group Header'),
    (21,'Group Footer'),
    (30,'Sub-Group Header'),
    (31,'Sub-Group Footer'),
    (50,'Save Button'),
    (100,'Standard Question'),
    (105,'Text-Area Question'),
    (110,'Multiple-Choice Question'),
    (120,'Standard Sub-Question'),
    (130,'Multiple-Choice Sub-Question')
)
type = models.IntegerField(default=100,choices=QUESTION_TYPES)

Я использую Django Rest Framework, чтобы представить эту модель как API для веб-приложения Angular. В моем веб-приложении Angular мне нужен виджет со списком со всеми этими вариантами. Не целые числа, а текстовые варианты, такие как "реклама", "стандартный вопрос" и т.д.

Теперь я могу скомпоновать поле со списком в приложение Angular, но в духе DRY, возможно ли написать сериализатор DRF, который просто возвращает эти варианты (то есть объект QUESTION_TYPES), поэтому я могу заполнить в поле со списком с запросом ReST?

И под "возможным", я думаю, я имею в виду "простой и элегантный". И, может быть, я также имею в виду "ReSTful". (Это ReSTful, чтобы сделать это таким образом?)

Просто интересно.,.

Спасибо

Джон

Ответ 1

Вероятно, я бы попытался сделать что-то вроде следующего:

# models.py
class Question(models.Model):
    QUESTION_NAMES = (
        'Blurb',
        'Group Header',
        'Group Footer',
        'Sub-Group Header',
        'Sub-Group Footer',
        'Save Button',
        'Standard Question',
        'Text-Area Question',
        'Multiple-Choice Question',
        'Standard Sub-Question',
        'Multiple-Choice Sub-Question')
    QUESTION_VALS = (10, 20, 21, 30,
                     31, 50, 100, 105, 110,
                     120, 130)
    QUESTION_TYPES = tuple(zip(QUESTION_VALS, QUESTION_NAMES))
    # Personal choice here: I never name attribs after Python built-ins:
    qtype = models.IntegerField(default=100,choices=QUESTION_TYPES)

Следующее не работает, поскольку я думал, что он должен

(Следующей была моя первоначальная интуиция по сериализации списка объектов, но это не сработало. Я все равно оставляю ее здесь, потому что похоже, что она должна работать.)

Итак, у нас есть способ получить доступ к строкам самостоятельно, теперь нам просто нужно их сериализовать, и для этого я, вероятно, попытаюсь использовать ListField в DRF3, который должен поддерживать source kwarg, я бы подумал?

# serializers.py
from .models import Question
class YourSerializer(ModelSerializer):
    names = serializers.ListField(
       child=serializers.CharField(max_length=40),
       source=Question.QUESTION_NAMES
    )
    class Meta:
        model = Question
        fields = ('names', etc.)

Ниже приведен список результатов

Возврат: используйте SerializerMethodField:

from .models import Question

class YourSerializer(serializers.ModelSerializer):
    ...
    names = serializers.SerializerMethodField()

    def get_names(self, obj):
        return Question.QUESTION_NAMES

    class Meta:
        model = Question

Demo:

In [1]: q = Question.objects.create()
Out[1]: <Question: Question object>  

In [2]: ser = YourSerializer(q)

In [3]: ser.data
Out[3]: {'id': 1, 'names': ['Blurb', 'Group Header', 'Group Footer', 'Sub-Group Header', 'Sub-Group Footer', 'Save Button', 'Standard Question', 'Text-Area Question', 'Multiple-Choice Question', 'Standard Sub-Question', 'Multiple-Choice Sub-Question'], 'qtype': 100}

Ответ 2

если вы используете ModelViewSet в сочетании с ModelSerializer, запрос OPTIONS возвращает метаданные, которые вы можете использовать для получения параметров выбора.

from models import Question
from rest_framework import serializers

class QuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Question


from rest_framework.viewsets import ModelViewSet
class QuestionChoicesViewSet(ModelViewSet):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

Это даст вам ответ, который включает атрибут actions, который может выглядеть примерно так:

"actions": {
    "POST": {
        "id": {
            "type": "integer",
            "required": false,
            "read_only": true,
            "label": "ID"
        },
        "qtype": {
            "type": "choice",
            "required": false,
            "read_only": false,
            "label": "Qtype",
            "choices": [
                {
                    "display_name": "Blurb",
                    "value": 10
                },
                {
                    "display_name": "Group Header",
                    "value": 20
                },
                {
                    "display_name": "Group Footer",
                    "value": 21
                },
                {
                    "display_name": "Sub-Group Header",
                    "value": 30
                },
                //...
        }
    }
}

Вы можете выполнить итерацию по атрибуту choices на qtype, чтобы получить все доступные варианты.

Чтобы больше узнать об этом, прочитайте: Metadata

Ответ 3

Я достиг этого, создав конечную точку API для выбора, в котором используется только GET-глагол.

models.py

QUESTION_TYPES = (
    (10,'Blurb'),
    (20,'Group Header'),
    (21,'Group Footer'),
    (30,'Sub-Group Header'),
    (31,'Sub-Group Footer'),
    (50,'Save Button'),
    (100,'Standard Question'),
    (105,'Text-Area Question'),
    (110,'Multiple-Choice Question'),
    (120,'Standard Sub-Question'),
    (130,'Multiple-Choice Sub-Question')
)

class Question(models.Model):
    type = models.IntegerField(default=100,choices=QUESTION_TYPES)

viewsets.py

from models import QUESTION_NAMES, Question
from rest_framework import serializers
class QuestionSerializer(serializers.ModelSerializer):
    type = serializers.ChoiceField(choices=QUESTION_NAMES, default=100)
    class Meta:
        model = Question

from rest_framework.response import Response
from rest_framework.views import APIView
class QuestionChoicesViewSet(APIView):
    def get(self, request):
        return Response(QUESTION_NAMES)

from rest_framework import viewsets
class QuestionViewSet(viewsets.ModelViewSet):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer