Jakub Strawa
Jakub Strawa

Reputation: 61

How to combine two querysets from two models? Django Rest Framework

I have two models

class Answer(models.Model):
    answer = models.TextField()
    created_at = models.DateTimeField(editable=False, default=timezone.now)
    updated_at = models.DateTimeField(default=timezone.now)
    user = models.ForeignKey('users.CustomUser', on_delete=models.PROTECT)
    question = models.ForeignKey('Question', on_delete=models.PROTECT)
    number_of_points = models.IntegerField(default=0)
    moderate_status = models.BooleanField(default=False)

and

class Question(models.Model):
    question_subject = models.TextField()
    question_text = models.TextField(default=None, null=True, blank=True)
    slug = models.SlugField(max_length=128, unique=True, null=False, editable=False)
    created_at = models.DateTimeField(editable=False, default=timezone.now)
    updated_at = models.DateTimeField(default=timezone.now)
    user = models.ForeignKey('users.CustomUser', on_delete=models.PROTECT)
    animal = models.ForeignKey('animals.Animal', on_delete=models.PROTECT)

serializers.py

class QuestionDetailSerializer(serializers.ModelSerializer):
    answers = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Question
        fields = '__all__'

views.py

class QuestionsDetailView(generics.ListAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionsSerializer

    def get_queryset(self):
        return super().get_queryset().filter(
            id=self.kwargs['pk']
        )

url.py

path('questions/<int:pk>', QuestionsDetailView.as_view()),

And i want to combine 2 queryset, one being already that filters the question by pk provided in the url, and the other queryset i'd like to give is Answer.objects.all().filter(question__id='pk'). Essentially i want to show all the questions with answers to a particular question.

Upvotes: 0

Views: 1264

Answers (2)

Jack
Jack

Reputation: 288

You won't exactly combine 2 queryset from different table. But you can use nested serializer.

class AnswerSerializer(serializers.ModelSerializer):

    class Meta:
        model = Anwser
        fields = ['id', ...]

class QuestionDetailSerializer(serializers.ModelSerializer):
    answer_set = AnswerSerializer(many=True, read_only=True)

    class Meta:
        model = Question
        fields = ['id', ..., 'answer_set']

# OR


class QuestionDetailSerializer(serializers.ModelSerializer):

    class Meta:
        model = Question
        fields = ['id']
    
    def to_representation(self, instance):
        data = super().to_representation(instance)
        data['answers'] = AnswerSerializer(many=True, instance=instance.anwser_set.all()).data
        return data

Upvotes: 1

NixonSparrow
NixonSparrow

Reputation: 6378

I strongly suggest to use relationships anywhere you can.

Add related_name to Answer.question in models.py:

class Answer(models.Model):
    ...
    question = models.ForeignKey('Question', on_delete=models.PROTECT, related_name='answers')

If id is enough in serializer, then in serializers.py:

class QuestionDetailSerializer(serializers.ModelSerializer):
    # this is not needed anymore: answers = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Question
        fields = ['question_subject ', 'question_text', ..., 'answers`]

But you can also create a function of model that you can include in similar way:

class Question(models.Model):
    ...
    def get_answers(self):
        return [answer.answer for answer in self.answers.all()]    # it will return a list of TextFields of answers

serializers.py:

class QuestionDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = Question
        fields = ['question_subject ', 'question_text', ..., 'get_answers`]

Upvotes: 1

Related Questions