Получение 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 запрос
- Это должно быть действие модели DETAIL, что означает, что оно выглядит как
- Затем реализуйте следующую логику:
- Получение модели экзамена
- Затем получить вопросы для этого экзамена, используя
"?"
, чтобы упорядочить их случайным образом и взять только несколько .
- Затем сериализуем экземпляры вопросов
- И возвращаем данные
Вот как это может выглядеть:
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)