Получение n-ного количества случайных записей с помощью вложенных сериализаторов Django REST framework

Я пытаюсь получить случайное 'n' количество записей из моделей, связанных с внешним ключом. Предположим, у меня есть две модели с именами Exam и Questions. Мне нужна конечная точка API для получения n-ного количества вопросов по одному предмету (например, по математике, n случайных вопросов по математике). Конечная точка работает хорошо, получая все вопросы по определенному предмету.

models.py

class Exam(models.Model):
   
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name

class Question(models.Model):
    
    exam = models.ForeignKey(Exam, on_delete=models.CASCADE)
    question = models.CharField(max_length=255)

    def __str__(self):
        return '{} - {}'.format(self.question)

serializers.py

class QuestionSerializer(serializers.ModelSerializer):
    
    questions = serializers.CharField(read_only=True)
    answer = serializers.CharField(read_only=True)

    class Meta:
        model = Question
        fields = '__all__'


class ExamSerializer(serializers.ModelSerializer):
    
    name = serializers.CharField(read_only=True)
    questions = QuestionSerializer(many=True, read_only=True, source='question_set')

    class Meta:
        model = Exam
        fields = '__all__'

api_views.py

class ExamQuestionRetrieveAPIView(generics.RetrieveAPIView):

    authentication_classes = [JWTTokenUserAuthentication]

    serializer_class = ExamSerializer
    queryset = Exam.objects.all()
    lookup_field = 'name'

После изучения документации я попытался отфильтровать и получить случайные записи, используя метод to_representation(), но не смог. Любая помощь будет высоко оценена.

Если вам нужно N случайных вопросов из 1 экзамена, я бы сделал следующее:

  • Создайте пользовательское действие в наборе представлений (или пользовательском представлении)
    • Это должно быть действие модели DETAIL, что означает, что оно выглядит как exams/3/your-action-name/
    • .
    • Это должен быть GET запрос
  • Затем реализуйте следующую логику:
    • Получение модели экзамена
    • Затем получить вопросы для этого экзамена, используя "?", чтобы упорядочить их случайным образом и взять только несколько
    • .
    • Затем сериализуем экземпляры вопросов
    • И возвращаем данные

Вот как это может выглядеть:

def get_random_questions(self, request, pk=None):
    exam = self.get_object()
    questions = Question.objects.filter(exam=exam).order_by("?")[:5] # Update with your desired number
    serializer = QuestionSerializer(questions, many=True)
    return Reponse(serializer.data)

Наконец-то нашел способ без изменения реализации, предыдущая попытка использовать to_representation() была сделана в неправильном сериализаторе. В любом случае удалось вернуть 'n' случайных записей с помощью np.random.choice() вот как я это сделал. В сериализаторе Question,

def to_representation(self, instance):
    representation = super().to_representation(instance)
    representation['questions'] = np.random.choice(
         representation['questions'],
         10, # n(=10) number of records
         replace=False
    )

    return representation

Но использование to_representation() означает, что будут загружены все объекты вопросов, а затем из этого списка np.random.choice() будет получено n количество случайных записей. Что не так уж и здорово.


Показалось, что ответ @Jordan гораздо удобнее, поэтому я использовал APIView вместо generics.RetrieveAPIView и без использования вложенной сериализации.

class ExamQuestionAPIView(APIView):

    def get_object(self, name):
        try:
            return Quiz.objects.get(name=name)
        except Quiz.DoesNotExist:
            raise Http404

    def get(self, request, name, format=None):
        """
            Return 10 random questions.
        """

        questions = QuizQuestion.objects.filter(
            quiz=self.get_object(name)
        ).order_by('?')[:10]

        questions_serialized = QuizQuestionSerializer(questions, many=True)

        return Response(questions_serialized.data)
Вернуться на верх