我有Student
模型通过Course
字段与ManyToMany
模型相关:
class Student(Model):
name = TextField()
class Course(Model):
name = TextField()
students = ManyToManyField(Student, through="StudentCourse")
class StudentCourse(Model):
student = ForeignKey(Student)
course = ForeignKey(Course)
section_name = TextField()
如何通过Course.students
多对多字段中的部分自动注释通过该课程中的部分检索的学生?
例如,不必在每个查询上添加显式extra
:
>>> students = course.students.extra(select={
... "section_name": "app_student_course.section_name",
... })
>>> print students[0].section_name
u'First Section'
我可以:
>>> students = course.students.all()
>>> print students[0].section_name
u'First Section'
谢谢!
答案 0 :(得分:2)
我想这里的问题与this非常相似,所以我的解决方案几乎与那里提供的解决方案相同。
我认为使用自定义Prefetch
的{{1}}更适合此类问题而不是.annotations
或.extra
条款。好处是你可以得到整个相关对象而不是它的一小部分(所以你可以使用更多元数据),并且没有性能缺点,只有更大的内存占用,如果用于大量对象,可能会导致问题
class StudenQuerySet(models.QuerySet):
def prefetch_sections_for_course(self, course):
return self.prefetch_related(models.Prefetch('studentcourse_set',
queryset=StudentCourse.objects.filter(course=course),
to_attr='course_sections'
))
class Student(Model):
name = TextField()
objects = StudenQuerySet.as_manager()
@property
def course_section_name(self):
try:
return self.course_sections[0].section_name
except IndexError:
#This student is not related with the prefetched course
return None
except AttributeError:
raise AttributeError('You forgot to prefetch course sections')
#or you can just
return None
#usage
students = course.students.all().prefetch_sections_for_course(course)
for student in students:
print student.course_section_name
答案 1 :(得分:2)
是否可以按如下方式替换关系管理器。从那里,您可以使用查询集执行任何操作:
from django.db.models.query import F
class Student(Model):
name = TextField()
class Course(Model):
name = TextField()
students = ManyToManyField(Student, through="StudentCourse")
class StudentCourse(Model):
# Set related_names so that it is easy to refer to the relation
# with the through model
student = ForeignKey(Student, related_name='student_courses')
course = ForeignKey(Course, related_name='student_courses')
section_name = TextField()
# Create a new customized manager
class StudentRelatedWithCourseManager(
Course.students.related_manager_cls
):
def get_queryset(self):
# Gets the queryset of related Students ...
qs = super(StudentRelatedWithCourseManager, self)\
.get_queryset()
# Annotate it before is handled by the ManyRelatedDescriptor
return qs.annotate(
section_name=F('student_courses__section_name')
)
# Replace the stock manager with your custom manager
Course.students.related_manager_cls = \
StudentRelatedWithCourseManager
答案 2 :(得分:0)
为什么不查询StudentCourse
模型?例如:
StudentCourse.objects.filter(course=course)
如果您正在迭代StudentCourse
个对象并查询student
属性,请使用select_related
确保您没有为每个学生执行额外的数据库查询。
student_courses = StudentCourse.objects.select_related('student').filter(course=course)
for sc in student_courses:
print '%s: %s' % (sc.student.name, sc.section_name)