自动注释通过ManyToMany字段检索的Django模型

时间:2016-07-07 18:05:08

标签: django django-models

我有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'

谢谢!

3 个答案:

答案 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)