You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中含多对多字段模型的QuerySet构建问题

嘿,针对你这个学生评教师的多对多关联QuerySet构建问题,我来给你捋捋思路!先从合理的模型结构假设入手(如果和你实际的模型有出入,随时调整就行),再给你不同场景下的解决方案:

第一步:先明确模型结构(参考示例)

我先假设你的核心模型大概是这样的,方便后续讲解:

from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=100)
    # 其他学生相关字段

class Teacher(models.Model):
    name = models.CharField(max_length=100)
    # 教师关联多个问题类别,多对多字段
    question_categories = models.ManyToManyField('QuestionCategory', related_name='teachers')
    # 其他教师相关字段

class QuestionCategory(models.Model):
    name = models.CharField(max_length=50, unique=True)  # 比如"intelligence"、"respect"、"empathy"

class Question(models.Model):
    text = models.TextField()  # 具体的评分问题内容
    # 问题属于某个类别,外键关联
    category = models.ForeignKey(QuestionCategory, on_delete=models.CASCADE, related_name='questions')

# 可选:记录学生的评分记录,用来跟踪已评/待评的问题
class Rating(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    score = models.IntegerField()  # 学生给的分数
不同场景的QuerySet构建方案

场景1:获取某学生需要评分的某教师的所有问题

比如学生打开了某教师的评分页面,需要展示该教师关联所有类别下的问题,还要排除已经评过的:

# 先拿到当前登录的学生和目标教师(实际项目里可以用request.user关联Student模型)
current_student = Student.objects.get(id=1)
target_teacher = Teacher.objects.get(id=2)

# 第一步:获取该教师所有关联类别下的问题
all_teacher_questions = Question.objects.filter(category__teachers=target_teacher)

# 第二步:排除学生已经评过的问题,避免重复评分
already_rated_ids = current_student.rating_set.filter(teacher=target_teacher).values_list('question_id', flat=True)
pending_questions = all_teacher_questions.exclude(id__in=already_rated_ids)

场景2:获取某学生需要评分的所有教师,以及每个教师对应的待评问题

这种场景下要注意优化查询性能,避免N+1问题,用prefetch_related批量加载关联数据:

from django.db.models import Prefetch

current_student = Student.objects.get(id=1)
# 先拿到学生已经评过的记录,用来筛选待评问题
already_rated_records = Rating.objects.filter(student=current_student)

# 批量获取教师,并预加载对应的待评问题
teachers_with_pending_questions = Teacher.objects.all().prefetch_related(
    Prefetch(
        # 通过教师的类别关联到问题的反向路径
        'question_categories__questions',
        # 筛选出未被该学生评分的问题
        queryset=Question.objects.exclude(rating__in=already_rated_records),
        # 给预加载的结果起个别名,方便模板里调用
        to_attr='pending_questions'
    )
)

# 之后在模板里可以这样遍历展示:
# {% for teacher in teachers_with_pending_questions %}
#   <h3>评分教师:{{ teacher.name }}</h3>
#   {% for question in teacher.pending_questions %}
#       <p>{{ question.text }}</p>
#       <!-- 这里放评分输入框 -->
#   {% endfor %}
# {% endfor %}

场景3:按问题类别分组展示给学生

如果需要把问题按类别分组展示(比如先展示“intelligence”类的所有问题,再展示“respect”类的),可以这样做:

# 方法1:用QuerySet按类别分组统计(适合只需要类别和问题数量的场景)
from django.db.models import Count

category_stats = Question.objects.filter(category__teachers=target_teacher).values('category__name').annotate(question_count=Count('id')).order_by('category__name')

# 方法2:获取带类别信息的问题,在模板里分组展示(适合需要展示具体问题的场景)
questions_with_category = Question.objects.filter(category__teachers=target_teacher).select_related('category')

# 模板里用Django的regroup标签分组:
# {% regroup questions_with_category by category as category_groups %}
# {% for group in category_groups %}
#   <h4>类别:{{ group.grouper.name }}</h4>
#   {% for question in group.list %}
#       <p>{{ question.text }}</p>
#   {% endfor %}
# {% endfor %}
关键注意点
  • 利用反向关联:比如category__teachers是因为我们给多对多字段设置了related_name='teachers',如果没设置的话,默认是teacher_set,记得对应调整。
  • 性能优化:涉及多对多和外键查询时,一定要用prefetch_related(多对多)和select_related(外键),避免频繁触发数据库查询。
  • 避免重复评分:一定要通过Rating模型做筛选,确保学生不会看到已经评过的问题。

如果你的实际模型结构(比如那个Item类的具体定义)和我假设的不一样,随时把模型代码贴出来,我再给你调整方案~

内容的提问来源于stack exchange,提问作者anon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:38:23