Serializing Foreign Key Objects with Django-HVAD TranslatedFields

149 Views Asked by At

I see there are several questions on SO about serialization on Django already, but I'm having additional complexity because my fields are translated (using django-hvad).

What I have kinda works but I imagine it's horribly inefficient, and since I'm new to Django, am wondering if there's a better way.

What I'm doing now is getting each one of the three models I need (Survey, SurveyQuestion, SurveyAnswer), serializing each individually, and then knitting them together before converting to JSON.

The effect of this is working, as seen in the django shell:

>>> qs = models.SurveyQuestion.objects.language('en').all().filter(survey=1)
>>> for q in qs:
...   res.append({'question': q, 'answers' : models.SurveyAnswer.objects.language('en').all().filter(question=q.pk)})
...
>>> res = []
>>> res
[{'question': <SurveyQuestion: Who is the best Beatle?>, 'answers': [<SurveyAnswer: Paul McCartney>, <SurveyAnswer: George Harrison>, <SurveyAnswer: Ringo Starr>]}, {'question': <SurveyQuestion: Which album from The Beatles was the best?>, 'answers': [<SurveyAnswer: Yellow Submarine>, <SurveyAnswer: Revolver>, <SurveyAnswer: The White Album>]}]

Here's the relevant code. Let me know if you need to see more:

views.py

class SurveyDetail(APIView):
    """
    Retrieve, update or delete a survey instance.
    """
    def get_object(self, pk):
        try:
            user_language = self.request.GET.get('language')
            return models.Survey.objects.language(user_language).get(pk=pk)
        except models.Survey.DoesNotExist:
            return HttpResponse(status=status.HTTP_404_NOT_FOUND)

    def get_related_questions(self, pk):
        try:
            user_language = self.request.GET.get('language')
            return models.SurveyQuestion.objects.language(user_language).all().filter(survey=pk)
        except models.SurveyAnswer.DoesNotExist:
            return HttpResponse(status=status.HTTP_404_NOT_FOUND)

    def get_related_answers(self, pk):
        try:
            user_language = self.request.GET.get('language')
            return models.SurveyAnswer.objects.language(user_language).all().filter(question=pk)
        except models.SurveyAnswer.DoesNotExist:
            return HttpResponse(status=status.HTTP_404_NOT_FOUND)

    def get(self, request, pk, format=None):
        survey = self.get_object(pk)
        questions = self.get_related_questions(pk)
        res = []
        for q in questions:
            res.append({
                'question': SurveyQuestionSerializer(q).data,
                'answers' : SurveyAnswerSerializer(self.get_related_answers(q.pk), many=True).data
                })
        resp_obj = {
            'survey' : SurveySerializer(survey).data,
            'data' : res
            }

        return JSONResponse(resp_obj)

    def put(self, request, pk, format=None):
        survey = self.get_object(pk)
        serializer = SurveySerializer(survey, data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        survey = self.get_object(pk)
        survey.delete()
        return HttpResponse(status=status.HTTP_204_NO_CONTENT)

serializers.py

class SurveySerializer(TranslatableModelSerializer):
    class Meta:
        model = models.Survey
        fields = ['pk', 'title', 'description', 'slug']


class SurveyQuestionSerializer(TranslatableModelSerializer):
    # survey = SurveySerializer(required=False)
    class Meta:
        model = models.SurveyQuestion
        fields = ['pk', 'title', 'content', 'slug', 'survey', 'is_multi_select', 'has_other_field', 'required']


class SurveyAnswerSerializer(TranslatableModelSerializer):
    # question = SurveyQuestionSerializer(required=False)
    class Meta:
        model = models.SurveyAnswer
        fields = ['pk', 'title', 'slug', 'question']
1

There are 1 best solutions below

0
On

I figured it out myself. Apparently, it's no different than on untranslated models, I just didn't have it right before.

Below is the corrected code which works properly. Note the order of each of the serializers and also the inclusion of the reverse foreign key fields as serializers.

serializers.py

class SurveyAnswerSerializer(TranslatableModelSerializer):
    class Meta:
        model = models.SurveyAnswer
        fields = ['pk', 'title', 'slug', 'question']

class SurveyQuestionSerializer(TranslatableModelSerializer):
    answers = SurveyAnswerSerializer(many=True)

    class Meta:
        model = models.SurveyQuestion
        fields = ['pk', 'title', 'content', 'slug', 'survey', 'is_multi_select', 'type', 'scale_min','scale_max', 'has_other_field', 'required', 'answers']

class SurveySerializer(TranslatableModelSerializer):
    questions = SurveyQuestionSerializer(many=True)

    class Meta:
        model = models.Survey
        fields = ['pk', 'title', 'description', 'slug', 'questions']

views.py

class SurveyDetail(APIView):
    ...
    def get(self, request, pk, format=None):
        survey = self.get_object(pk)
        serializer = SurveySerializer(survey)
        return JSONResponse(serializer.data)

Edit:

turns out this doesn't completely work. In this scenario, the Survey objects are correctly translated, but the objects referenced by the foreign keys, like SurveyQuestion and SurveyAnswer, are not translated. This means you get a mix of translated and untranslated results.