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
相关产品推荐
相关产品推荐

