【问题标题】:How to retrieve attributes from a foreign key related model to another foreign key related model?如何从外键相关模型检索属性到另一个外键相关模型?
【发布时间】:2022-12-10 10:45:34
【问题描述】:

我在 python 3.10.8 中使用 django 4.1.2。我有三种模型,一种用于用户管理,一种用于问题,另一种用于答案。它们描述如下:

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})

但我非常怀疑这是一种检索未回答问题或新问题的有效方法,尤其是当涉及到多个用户时。有没有更好的方法来检索用户之前回答过的所有问题,并且只显示新的或未回答的问题?

感谢您的回复。

【问题讨论】:

    标签: python-3.x django django-models django-views django-4.1


    【解决方案1】:

    要分解您需要做的事情,首先您需要用户回答的问题 ID 列表

    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_names,可以一次调用完成所有这些。在这里,我们跟随 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]
        )
    

    【讨论】:

    • 起初我删除了 questions_list 列表并将 for 循环修改为如下所示:python for ans in total_qs_answered_by_user: if questions.contains(ans.question_answered): questions = questions.exclude(id=ans.question_answered.id) 但看起来您的第一个答案将有效地执行相同的技巧。我感谢你的帮助
    • 不过我不知道order_by。不会对 db 表中的 pks 重新排序吗?
    • order_by('?') 只是使用 sql 随机返回记录集。它不影响表中 ID 的顺序,但对大结果有开销:docs.djangoproject.com/en/4.1/ref/models/querysets/#order-by。如果您有效地获取采样集,也可以使用随机样本
    • 原来我在单行尝试中误用了包含,所以我更新了答案。基于这个问题:stackoverflow.com/questions/67692881/…你可以用更简单的方式做一个 1 liner
    • 抱歉 contains - 完全错误的操作员(我在想什么?)。我认为以上是最好的方法,并为后代更新了答案。
    猜你喜欢
    • 2012-05-02
    • 2012-05-07
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 2018-04-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多