Как получить атрибуты из модели, связанной с внешним ключом, в другую модель, связанную с внешним ключом?

Я использую django 4.1.2 с python 3.10.8. У меня есть три модели одна для управления пользователями, одна для вопросов и другая для ответов. Они описаны ниже:

class User(AbstractUser):
    phone_number = models.CharField(max_length=14, unique=True)
    first_name = models.CharField(max_length=40)
    father_name = models.CharField(max_length=40)
    email = models.EmailField(unique=True, required=True)
    age = models.CharField(max_length=3)
    username = models.CharField(max_length=8, required=True)

class Question(models.Model):
    question = models.CharField(
        max_length=500,
        null=False,
        unique=True
    )
    creating_staff = models.ForeignKey(
        User,
        null=False,
        on_delete=models.PROTECT,
        to_field="phone_number",
    )
    options = models.JSONField(null=False)
    correct_option = models.CharField(max_length=250, null=False)
    question_ts = models.DateTimeField(auto_now_add=True, null=False)

    class Meta:
        verbose_name = "Question"

    def __str__(self) -> str:
        return f"{self.question}"


class Answer(models.Model):
    answer = models.CharField(max_length=500, null=False)
    question_answered = models.ForeignKey(
        Question,
        null=False,
        on_delete=models.PROTECT,
        related_name="question_answered"
    )
    answering_user = models.ForeignKey(
        User,
        null=False,
        on_delete=models.PROTECT,
        to_field="phone_number",
        related_name="answerer"
    )
    status = models.BooleanField(null=False)
    answer_ts = models.DateTimeField(auto_now_add=True, null=False)

    class Meta:
        verbose_name = "Answer"

    def __str__(self) -> str:
        return f"{self.answer} -- {self.answering_user}"

Это urls.py файл:

from django.urls import path

from .views import (AnswerView)

app_name = "commons"

urlpatterns = [
    path("play/", AnswerView.as_view(), name="play"),
]

Я пытаюсь сделать так: каждый раз, когда пользователь вошел в систему и хочет ответить на ряд вопросов, перейдя по ссылке /commons/play/, на запрос GET я хочу разобрать все предыдущие вопросы, на которые ответил пользователь, и всегда показывать новые вопросы, выбирая случайным образом 10 из неотвеченных вопросов.

Что я сделал на данный момент:

import random
from django.shortcuts import (redirect, render)
from django.views import View
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin

from .models import (Question, Answer, User)

class AnswerView(LoginRequiredMixin, View):

    def get(self, request):
            answerer = request.user
            total_answers_by_user = Answer.objects.filter(answering_user=answerer)
            questions = Question.objects.all()
            question_list = list()
            for ans in total_answers_by_user:
                for q in questions:
                    if not ans.question_answered == q:
                        question_list.append(q)
            questions_count = question.count()
            try:
                rand_sample = random.sample(range(questions_count), 10)
            except (ValueError, Exception) as e:
                print(f"{e} population for random sample < 10, perhaps new player")
                total_questions = Question.objects.all().count()
                rand_sample = random.sample(range(total_questions), 10)
                questions_to_ask = Question.objects.filter(id__in=rand_sample)
            else:
                questions_to_ask = Question.objects.filter(id__in=rand_sample)

            return render(request, "commons/answer.html", {"questions": questions_to_ask})

Но я очень сомневаюсь, что это эффективный способ поиска вопросов без ответов или новых вопросов, особенно когда речь идет о нескольких пользователях. Есть ли лучший способ получить все вопросы, на которые пользователь уже отвечал, и отобразить только новые или неотвеченные вопросы?

Моя благодарность заранее за ваш ответ.

Вы могли бы сделать это за два обращения к базе данных - сначала получив список идентификаторов вопросов, на которые ответил пользователь

questions_answered = Answer.objects.filter(answering_user=answerer).values_list('question_answered_id', flat=True)

Затем получить список вопросов, не входящих в этот список

question_list = Question.objects.exclude(pk__in=questions_answered)

Я не тестировал следующий, но вы можете использовать related_name, чтобы исключить его за один вызов.

question_list = Questions.objects.exclude(answerer__contains=answerer)

При небольшом снижении производительности, если есть множество вопросов, вы можете даже получить случайный набор из 10 в одном вызове базы данных, например,

question_list = Question.objects.exclude(pk__in=questions_answered).order_by('?')[:10]
Вернуться на верх