Как получить атрибуты из модели, связанной с внешним ключом, в другую модель, связанную с внешним ключом?
Я использую 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]