枚举Django Rest框架序列化器中的模型选择

时间:2021-05-15 19:18:36

I have a model that uses a Django choices field, like this:

我有一个使用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)

I'm using Django Rest Framework to present this model as an API to an Angular web app. In my Angular web app, I want a combo box widget that drops down with all those choices. Not the integers, but the text choices, like "blurb", "standard question" and so on.

我正在使用Django Rest框架将这个模型作为一个API呈现给一个有棱角的web应用程序。不是整数,而是文本选项,比如“简介”、“标准问题”等等。

Now, I could hand code the combo box into the Angular app, but in the spirit of DRY, is it possible to write a DRF serializer that just returns those choices (ie the QUESTION_TYPES object), so I can populate the combo box with a ReST query?

现在,我可以将组合框手工编码到有棱角的应用程序中,但是本着枯燥的精神,是否可以编写一个DRF序列化器来返回这些选项(即QUESTION_TYPES对象),以便我可以用ReST查询填充组合框?

And by "possible", I guess I mean "simple and elegant". And maybe I also mean "ReSTful". (Is it ReSTful to do it that way?)

“可能”的意思是“简单而优雅”。也许我的意思也是“休息”。(那样做能让人放松吗?)

Just wondering . . .

只是在想…

Thanks

谢谢

John

约翰

3 个解决方案

#1


3  

I would probably try something like the following:

我可能会尝试以下方法:

# 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)

The following doesn't work as I thought it should

下面的方法并没有像我想的那样有效

(Following was my original intuition on serializing a list of objects, but it did not work. I'm leaving it in here anyway, because it seems like it should work.)

(以下是我对序列化对象列表的最初直觉,但它不起作用。不管怎样,我还是把它留在这里,因为它看起来应该是有用的。

Okay, so we have a way to access the strings on their own, now we just need to serialize them, and for that, I'd probably try to use the ListField in DRF3, which should support the source kwarg, I would think?

好了,我们有了一种单独访问字符串的方法,现在我们只需要序列化它们,为此,我可能会尝试在DRF3中使用ListField,它应该支持源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.)

The following does return a list of results

下面的代码确实返回了一个结果列表

Fallback: use a SerializerMethodField:

回退:使用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


1  

if you use a ModelViewSet in combination with a ModelSerializer, the OPTIONS request will return metadata that you can use to get the choice options.

如果您结合使用ModelViewSet和ModelSerializer,选项请求将返回您可以用来获取选项的元数据。

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

This will give you a response that includes the actions attribute, that might look something like this:

这将给您一个包含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
                },
                //...
        }
    }
}

You can iterate over the choices attribute on qtype to get all of the available choices.

您可以对qtype上的choices属性进行迭代,以获得所有可用的选项。

To get more familiar with this topic you can read: Metadata

为了更熟悉这个主题,您可以阅读:元数据。

#3


0  

I accomplished this by making an API endpoint for the choices which only use the GET verb.

我通过为只使用GET谓词的选项创建一个API端点来实现这一点。

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

#1


3  

I would probably try something like the following:

我可能会尝试以下方法:

# 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)

The following doesn't work as I thought it should

下面的方法并没有像我想的那样有效

(Following was my original intuition on serializing a list of objects, but it did not work. I'm leaving it in here anyway, because it seems like it should work.)

(以下是我对序列化对象列表的最初直觉,但它不起作用。不管怎样,我还是把它留在这里,因为它看起来应该是有用的。

Okay, so we have a way to access the strings on their own, now we just need to serialize them, and for that, I'd probably try to use the ListField in DRF3, which should support the source kwarg, I would think?

好了,我们有了一种单独访问字符串的方法,现在我们只需要序列化它们,为此,我可能会尝试在DRF3中使用ListField,它应该支持源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.)

The following does return a list of results

下面的代码确实返回了一个结果列表

Fallback: use a SerializerMethodField:

回退:使用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


1  

if you use a ModelViewSet in combination with a ModelSerializer, the OPTIONS request will return metadata that you can use to get the choice options.

如果您结合使用ModelViewSet和ModelSerializer,选项请求将返回您可以用来获取选项的元数据。

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

This will give you a response that includes the actions attribute, that might look something like this:

这将给您一个包含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
                },
                //...
        }
    }
}

You can iterate over the choices attribute on qtype to get all of the available choices.

您可以对qtype上的choices属性进行迭代,以获得所有可用的选项。

To get more familiar with this topic you can read: Metadata

为了更熟悉这个主题,您可以阅读:元数据。

#3


0  

I accomplished this by making an API endpoint for the choices which only use the GET verb.

我通过为只使用GET谓词的选项创建一个API端点来实现这一点。

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