I have a model that uses a Django choices field, like this:
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.
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?
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
Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
The ModelSerializer class is the same as a regular Serializer class, except that: It will automatically generate a set of fields for you, based on the model. It will automatically generate validators for the serializer, such as unique_together validators. It includes simple default implementations of .
HyperlinkedModelSerializer is a layer of abstraction over the default serializer that allows to quickly create a serializer for a model in Django. Django REST Framework is a wrapper over default Django Framework, basically used to create APIs of various kinds.
In function based views we can pass extra context to serializer with "context" parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with "self. context". From example, to get "exclude_email_list" we just used code 'exclude_email_list = self.
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?
# 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
:
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}
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.
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": {
"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.
To get more familiar with this topic you can read: Metadata
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With