Django如何关联查询两个模型并保留与父模型的关联?
问题需求
我有一个父模型Profile,以及另外两个模型Courses和Course_student,模型定义如下:
class Profile(models.Model): ... first_name = models.CharField(verbose_name=_('First Name'), max_length=255, null=True, blank=True) middle_name = models.CharField(verbose_name=_('Middle Name'), max_length=255, null=True, blank=True) last_name = models.CharField(verbose_name=_('Last Name'), max_length=255, null=True, blank=True) ... class Courses(models.Model): YESNO = ( ('Yes', 'Yes'), ('No', 'No'), ) profile = models.ManyToManyField('Profile', related_name='course_profile', verbose_name=_('Profile')) course_taken = models.CharField(verbose_name=_('Course'), max_length=255) name_of_school = models.CharField(verbose_name=_('Name of School'), max_length=255) school_location = models.CharField(verbose_name=_('School Location'), max_length=255) period_of_attendance_from = models.DateField(verbose_name=_('Period of Attendance (From)'), max_length=255) period_of_attendance_to = models.DateField(verbose_name=_('Period of Attendance (To)'), max_length=255) nr_students = models.IntegerField(verbose_name=_('Total Number of Students'), null=True, blank=True) created_on = models.DateTimeField(auto_now_add=True) modified_on = models.DateTimeField(auto_now=True) class Meta: verbose_name = _('Course') verbose_name_plural = _('Course') def __str__(self): return '%s' % (self.course_taken) class Course_student(models.Model): YESNO = ( ('Yes', 'Yes'), ('No', 'No'), ) course = models.ForeignKey('Courses', related_name='course_student_details', on_delete=models.DO_NOTHING, verbose_name=_('Course'), blank=True, null=True) profile = models.ForeignKey('Profile', related_name='course_student_profile', on_delete=models.DO_NOTHING, verbose_name=_('Profile'), blank=True, null=True) standing = models.IntegerField(verbose_name=_('Standing'), null=True, blank=True) grade = models.CharField(verbose_name=_('Grade'), max_length=255, null=True, blank=True) completed = models.CharField(verbose_name=_('Completed'), choices=YESNO, max_length=255) class Meta: verbose_name = _('Student Details') verbose_name_plural = _('Student Details') def __str__(self): return '%s' % (self.course)
admin.py代码如下:
class CourseUploaderInline(admin.StackedInline): model = CourseUploader extra = 1 class CourseStudentInline(admin.StackedInline): model = Course_student extra = 1 @admin.register(Courses) class CoursesAdmin(admin.ModelAdmin): inlines = [Course_studentInline, CorseUploaderInline,]
模型关联逻辑:
Course_student通过ForeignKey关联Courses和Profile,用来存储特定学生在特定课程中的成绩、完成状态等详情;Courses通过ManyToMany关联Profile,用来标记学生修读的课程;Profile存储学生基础信息。
我想实现的是:通过Profile的ID过滤,合并查询Courses和Course_student的数据,当前在view.py中尝试的代码如下:
def profile(request, pk): profile = Profile.objects.filter(profile=pk) course = Course.objects.filter(profile=pk) # 我试过在这里用select_related()
注:我是Django和Python新手,欢迎大家给出解决方案和优化建议:)
解决方案与优化建议
1. 修正基础查询错误
你的view代码存在两个明显问题:
Profile.objects.filter(profile=pk)字段名错误,应该用pk而非profile,如果确定ID存在可以用get(pk=pk),不确定则用filter(pk=pk);Course.objects.filter(profile=pk)多对多关系过滤需用profile__pk=pk,或者利用反向关联查询。
2. 合并查询Courses和Course_student数据
方式一:从Profile实例出发关联查询
先获取指定Profile,再通过反向关联拿到所有课程,同时预加载对应的学生详情,避免多次数据库查询:
def profile(request, pk): # 获取指定学生的Profile实例 profile = Profile.objects.get(pk=pk) # 预加载该学生修读的所有课程,以及每个课程对应的个人详情 courses = profile.course_profile.prefetch_related('course_student_details') # 遍历课程和个人详情示例 for course in courses: # 取出当前学生在该课程的详情(一个学生对应一门课程只有一条记录) student_detail = course.course_student_details.filter(profile=profile).first() # 可以直接访问课程和详情的字段 print(course.course_taken, student_detail.grade, student_detail.completed) # 传递数据到模板 return render(request, 'profile_detail.html', {'profile': profile, 'courses': courses})
方式二:直接从Course_student出发关联查询
如果只需要学生的课程详情数据,直接查询Course_student并关联Courses和Profile更高效:
def profile(request, pk): # 关联查询学生的所有课程详情,同时加载对应的课程和个人信息 student_course_details = Course_student.objects.filter(profile__pk=pk).select_related('course', 'profile') # 遍历示例 for detail in student_course_details: print(detail.profile.first_name, detail.course.course_taken, detail.grade) return render(request, 'profile_detail.html', {'details': student_course_details})
3. 模型结构优化建议
当前模型存在冗余:Courses与Profile的多对多关系,已经被Course_student表覆盖(Course_student本身就是关联课程和学生的中间表,还额外存储了成绩等信息)。建议去掉Courses中的profile多对多字段,避免数据不一致问题。
修改后的Courses模型:
class Courses(models.Model): YESNO = ( ('Yes', 'Yes'), ('No', 'No'), ) # 移除原有的多对多字段 course_taken = models.CharField(verbose_name=_('Course'), max_length=255) name_of_school = models.CharField(verbose_name=_('Name of School'), max_length=255) school_location = models.CharField(verbose_name=_('School Location'), max_length=255) period_of_attendance_from = models.DateField(verbose_name=_('Period of Attendance (From)')) # 注意:DateField不需要max_length参数,原写法有误 period_of_attendance_to = models.DateField(verbose_name=_('Period of Attendance (To)')) nr_students = models.IntegerField(verbose_name=_('Total Number of Students'), null=True, blank=True) created_on = models.DateTimeField(auto_now_add=True) modified_on = models.DateTimeField(auto_now=True) class Meta: verbose_name = _('Course') verbose_name_plural = _('Courses') # 复数改为Courses更符合规范 def __str__(self): return self.course_taken
优化后,查询学生课程信息仅需通过Course_student关联即可,逻辑更清晰,无重复数据。
4. Admin代码修正
你的CoursesAdmin存在拼写错误:CorseUploaderInline应为CourseUploaderInline,修正后:
class CourseUploaderInline(admin.StackedInline): model = CourseUploader extra = 1 class CourseStudentInline(admin.StackedInline): model = Course_student extra = 1 @admin.register(Courses) class CoursesAdmin(admin.ModelAdmin): inlines = [CourseStudentInline, CourseUploaderInline,]
内容的提问来源于stack exchange,提问作者Markmeplease
相关产品推荐
相关产品推荐

