Django serializer returning empty array
I have minimal experience with Python, and no experience with Django, but I have been tasked with fixing a bug in a Django app.
I have the following models
` class SurveyResponse(models.Model):
survey = models.ForeignKey(to=Survey, on_delete=models.CASCADE, related_name="responses")
# Don't want to clear out surveys if the mission is cleared. Still may be valuable
mission_run = models.ForeignKey(to=MissionRun, on_delete=models.SET_NULL, blank=True, null=True,
related_name="survey_responses")
grade = models.CharField(max_length=15, blank=True, null=True) # grade level
gender = models.CharField(max_length=15, blank=True, null=True) # gender selection
race = models.CharField(max_length=50, blank=True, null=True) # race selection
exported_survey_file = models.ForeignKey(to="SurveyVendorFile", null=True, blank=True, on_delete=models.SET_NULL,
related_name="survey_responses")
exported_evaluator_file = models.ForeignKey(to="EvaluatorFile", null=True, blank=True, on_delete=models.SET_NULL,
related_name="survey_responses")
class SurveyResponseAnswer(models.Model):
survey_response = models.ForeignKey(to=SurveyResponse, on_delete=models.CASCADE, related_name="answers")
survey_question = models.ForeignKey(to=SurveyQuestion, on_delete=models.CASCADE, related_name="answers")
answers = ArrayField(models.CharField(max_length=100), null=True, blank=True)
answer = models.CharField(max_length=100, null=True, blank=True)
@property
def reverse_coding(self):
return self.survey_question.reverse_coding`
and then this code to serialize a response to a POST request
` class SurveyResponseViewSet(mixins.CreateModelMixin, GenericViewSet):
queryset = app_models.SurveyResponse.objects.all()
permission_classes = []
serializer_class = app_serializers.SurveyResponseCreateSerializer
class SurveyResponseAnswerCreateSerializer(serializers.ModelSerializer):
survey_question_id = serializers.IntegerField()
answers = serializers.SerializerMethodField(required=False)
def get_answers(self, obj):
return obj.answers if obj.answers else []
class Meta:
model = app_models.SurveyResponseAnswer
exclude = ["survey_response", "survey_question"]
class SurveyResponseCreateSerializer(serializers.ModelSerializer):
survey_id = serializers.IntegerField()
answers = SurveyResponseAnswerCreateSerializer(many=True)
mission_run_id = serializers.IntegerField()
def create(self, validated_data):
answers = validated_data.pop("answers", [])
survey_response = app_models.SurveyResponse.objects.create(**validated_data)
for answer in answers:
app_models.SurveyResponseAnswer.objects.create(survey_response=survey_response, **answer)
return survey_response
class Meta:
model = app_models.SurveyResponse
exclude = ["survey", "mission_run"]`
My post body looks like this
` {
"missionRunId": 22,
"surveyId": 2,
"answers": [
{
"surveyQuestionId": 40,
"answer": "1",
"answers": []
},
{
"surveyQuestionId": 41,
"answer": null,
"answers": [
"1",
"4",
"3",
"5"
]
},
{
"surveyQuestionId": 43,
"answer": null,
"answers": [
"a",
"b"
]
}, ... etc`
The returned json data has all the survey questions, but for any question which contains a multiple choice "answers" in the post, I get back a empty array (answers: []) in the response.
I suspect the issue lies in the
def get_answers(self, obj): return obj.answers if obj.answers else []
code, that obj.answers is null/empty, but I am not sure why.
My google efforts so far have led me no where - I think I just lack the domain knowledge to understand it sufficiently. Likewise chatgpt has led me nowhere - again lack of domain knowledge I believe, I don't know if chatgpt is giving me garbage output or not, but nothing it has suggested has worked so far.
I'm not sure why your obj.answers
appears to be empty, but I see that you have defined a specific serializer to apply a particular treatment to this specific attribute.
In this case, you can directly define a method serializer using serializers.SerializerMethodField()
in your main serializer, which will automatically point to your getter for answers. Maybe simplifying the serializer will make things clearer.
Then you can add a debug print for give us the value returned by your serializer in this case.
class SurveyResponseCreateSerializer(serializers.ModelSerializer):
survey_id = serializers.IntegerField()
answers = serializers.SerializerMethodField()
mission_run_id = serializers.IntegerField()
@staticmethod
def get_answers(obj):
print(f"Debugging {obj.answers}")
return obj.answers if obj.answers else []
def create(self, validated_data):
answers = validated_data.pop("answers", [])
survey_response = app_models.SurveyResponse.objects.create(**validated_data)
for answer in answers:
app_models.SurveyResponseAnswer.objects.create(survey_response=survey_response, **answer)
return survey_response
class Meta:
model = app_models.SurveyResponse
exclude = ["survey", "mission_run"]`
SerializerMethodField is a readonly field.
I fixed the issue by changing
answers = serializers.SerializerMethodField(required=False)
to
answers = serializers.ListField(
child=serializers.CharField(max_length=100), required=False, allow_null=True
)