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