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

Я использую 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/, наGETrequest Я хочу разобрать все предыдущие вопросы, на которые ответил пользователь, и всегда отображать новые вопросы, случайным образом выбирая 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})

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

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

1 ответ

Чтобы понять, что вам нужно сделать, сначала вам нужен список идентификаторов вопросов, на которые ответил пользователь.

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

Затем вы хотите исключить этот список вопросов из набора всех вопросов.

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

С использованиемrelated_nameс., можно сделать все это за один вызов. Здесь мы следуем за questions_answered к набору ответов, где полеanswering_userявляется нашим запросчиком, и исключить этот набор.

      questions_list = Question.objects.exclude(question_answered__answering_user=answerer)

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

      questions_list = (
    Question.objects
    .exclude(question_answered__answering_user=answerer)
    .order_by('?')[:10]
    )
Другие вопросы по тегам