I am currently trying to familiarize myself with DRF and while going through a tutorial these serializers were used
class EmbeddedAnswerSerializer(serializers.ModelSerializer):
votes = serializers.IntegerField(read_only=True)
class Meta:
model = Answer
fields = ('id', 'text', 'votes',)
class QuestionSerializer(serializers.ModelSerializer):
answers = EmbeddedAnswerSerializer(many=True,source='answer_set')
class Meta:
model = Question
fields = ('id', 'answers', 'created_at', 'text', 'user_id',)
These are the models
class Question(models.Model):
user_id = models.CharField(max_length=36)
text = models.CharField(max_length=140)
created_at = models.DateTimeField(auto_now_add=True)
class Answer(models.Model):
question = models.ForeignKey(Question,on_delete=models.PROTECT)
text = models.CharField(max_length=25)
votes = models.IntegerField(default=0)
My question is in the statement in the Question serializer
answers = EmbeddedAnswerSerializer(many=True,source='answer_set')
what is the purpose of many = True
and source='answer_set' ?
I read from the documentation the following regarding many=True
You can also still use the many=True argument to serializer classes. It's worth noting that many=True argument transparently creates a ListSerializer instance, allowing the validation logic for list and non-list data to be cleanly separated in the REST framework codebase.
I am confused by what that means ? If I remove many=True
from the code I get the error
AttributeError at /api/quest/1/2/
Got AttributeError when attempting to get a value for field `text` on serializer `EmbeddedAnswerSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `RelatedManager` instance.
Original exception text was: 'RelatedManager' object has no attribute 'text'.
Can anyone explain what many=True
does and what source
field does ?
Adding to the answer above by @neverwalkaloner
Many = True
many=True
signals that there is more than one object (an iterable) being passed to the serializer. Passing this field in turn triggers themany_init
withinBaseSerializer
to automagically create aListSerializer
instance.Source Code Snippet:
Source = "xyz"
This tells DRF which object attribute supplies the value for the field. The default assumption is that the field name declared on the serializer is the same as the field on the object instance that supplies the value. In cases where this is not true,
source
allows you to explicitly supply the object instance where the serializer will look for the value. Here's a peek into thedef bind(self, field_name, parent)
insideserializers.fields
where this happensSource Code Snippet:
Finally the value is gotten as follows using the
source
andsource_attrs
declared inbind
:Assuming a
Question
can have multipleAnswers
, your approach is correct.The problem appears to be that the source you supplied is the
RelatedManager
instance itself, and not the queryset ofAnswer
objects. I assumed DRF resolves this accurately, can you try changing it to source='answer_set.all'.answer_set
is the defaultRelatedManager
name given by Django. It might be wise to name your backward relationship using related_name in the Django model. This can be achieved by changing:Probably not the best explanation and someone can add more details but briefly
many=True
tells to serializer that it will take list of objects for serilizing proccess. In other words it's just a trigger that allows you to specify will you serialize, well, many objects at once, or just single object.source
on the other side specify which attribute of objects should be serializing with current serializer's field.In practice this line
means that you want to serialize
answer_set
attribute ofQuestion
object withEmbeddedAnswerSerializer
. Sinceanswer_set
is list of object you should addmany=True
as argument to make serializer aware that it will work with list of objects instead of single object.