Python 3 Django Rest Framework - 如何向此 M-1-M 模型结构添加自定义管理器?
Python 3 Django Rest Framework - how to add a custom manager to this M-1-M model structure?
我有这些型号:
组织
学生
课程
报名人数
一个学生属于一个组织
一个学生可以注册一门或多门课程
所以注册记录基本上由给定的课程和给定的学生组成
from django.db import models
from model_utils.models import TimeStampedModel
class Organisation(TimeStampedModel):
objects = models.Manager()
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Student(TimeStampedModel):
objects = models.Manager()
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
organisation = models.ForeignKey(to=Organisation, on_delete=models.SET_NULL, default=None, null=True)
def __str__(self):
return self.email
class Course(TimeStampedModel):
objects = models.Manager()
language = models.CharField(max_length=30)
level = models.CharField(max_length=2)
def __str__(self):
return self.language + ' ' + self.level
class Meta:
unique_together = ("language", "level")
class EnrollmentManager(models.Manager):
def org_students_enrolled(self, organisation):
return self.filter(student__organisation__name=organisation).all()
class Enrollment(TimeStampedModel):
objects = EnrollmentManager()
course = models.ForeignKey(to=Course, on_delete=models.CASCADE, default=None, null=False, related_name='enrollments')
student = models.ForeignKey(to=Student, on_delete=models.CASCADE, default=None, null=False, related_name='enrollments')
enrolled = models.DateTimeField()
last_booking = models.DateTimeField()
credits_total = models.SmallIntegerField(default=10)
credits_balance = models.DecimalField(max_digits=5, decimal_places=2)
请注意自定义 EnrollmentManager,它允许我查找从给定组织注册的所有学生。
如何添加自定义管理器以从已注册学生的给定组织中检索所有课程?
我试过的
我想创建一个 CourseManager 并以某种方式 query/filter 从关系的那一边:
class CourseManager(models.Manager):
def org_courses_enrolled(self, organisation):
return self.filter(enrollment__student__organisation__name=organisation).all()
这有效,但它给了我相同的 100 个注册记录:(
我想得到的是:
基于给定的组织
找到所有注册的学生
然后(不同?)获取该组织的注册课程列表
这是观点:
class OrganisationCoursesView(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
serializer_class = CourseSerializer
queryset = Course.objects.get_courses(1)
和url:
# The below should allow: /api/v1/organisations/1/courses/
router.register('api/v1/organisations/(?P<organisation_pk>\d+)/courses', OrganisationCoursesView, 'organisation courses')
更新 1
根据 h1dd3n 的回答,我接下来尝试了这个:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter(student__organisation_id=organisation)
但这会引发错误(如我所料):
FieldError: Cannot resolve keyword 'student' into field. Choices are:
courses, created, id, language, level, modified, progress
更新 2 - 越来越近了!
在@AKX 的评论帮助下好的:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter(courses__student__organisation_id=organisation)
现在开设 return 课程,但 return 每个注册学生都有一份副本。所以现在我需要对它们进行分组,以便每条记录只出现一次...
首先您需要将 self.filter
更改为 self.get_queryset().filter()
或在 manager
.
中创建一个单独的方法
def get_queryset(self):
return super(CourseManager, self).get_queryset()
在管理器中创建一个函数
def get_courses(self,organisation):
return self.get_queryset.filter(student__oraganisation=organisation)
这应该 return students
并且您不需要调用 .all()
- 过滤后的 qs
无论哪种方式 return 都是它找到的对象。
编辑
试试这个:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter( \
enrollments__student__organisation_id=organisation).distinct()
更新 2
你可以试试 from django.db.models import Q
https://docs.djangoproject.com/en/3.0/topics/db/queries/
或 annotate
基于这个答案 Get distinct values of Queryset by field 过滤掉每个学生,这样它就会出现一次。
我有这些型号:
组织
学生
课程
报名人数
一个学生属于一个组织
一个学生可以注册一门或多门课程
所以注册记录基本上由给定的课程和给定的学生组成
from django.db import models
from model_utils.models import TimeStampedModel
class Organisation(TimeStampedModel):
objects = models.Manager()
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Student(TimeStampedModel):
objects = models.Manager()
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
organisation = models.ForeignKey(to=Organisation, on_delete=models.SET_NULL, default=None, null=True)
def __str__(self):
return self.email
class Course(TimeStampedModel):
objects = models.Manager()
language = models.CharField(max_length=30)
level = models.CharField(max_length=2)
def __str__(self):
return self.language + ' ' + self.level
class Meta:
unique_together = ("language", "level")
class EnrollmentManager(models.Manager):
def org_students_enrolled(self, organisation):
return self.filter(student__organisation__name=organisation).all()
class Enrollment(TimeStampedModel):
objects = EnrollmentManager()
course = models.ForeignKey(to=Course, on_delete=models.CASCADE, default=None, null=False, related_name='enrollments')
student = models.ForeignKey(to=Student, on_delete=models.CASCADE, default=None, null=False, related_name='enrollments')
enrolled = models.DateTimeField()
last_booking = models.DateTimeField()
credits_total = models.SmallIntegerField(default=10)
credits_balance = models.DecimalField(max_digits=5, decimal_places=2)
请注意自定义 EnrollmentManager,它允许我查找从给定组织注册的所有学生。
如何添加自定义管理器以从已注册学生的给定组织中检索所有课程?
我试过的
我想创建一个 CourseManager 并以某种方式 query/filter 从关系的那一边:
class CourseManager(models.Manager):
def org_courses_enrolled(self, organisation):
return self.filter(enrollment__student__organisation__name=organisation).all()
这有效,但它给了我相同的 100 个注册记录:(
我想得到的是: 基于给定的组织 找到所有注册的学生 然后(不同?)获取该组织的注册课程列表
这是观点:
class OrganisationCoursesView(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
serializer_class = CourseSerializer
queryset = Course.objects.get_courses(1)
和url:
# The below should allow: /api/v1/organisations/1/courses/
router.register('api/v1/organisations/(?P<organisation_pk>\d+)/courses', OrganisationCoursesView, 'organisation courses')
更新 1
根据 h1dd3n 的回答,我接下来尝试了这个:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter(student__organisation_id=organisation)
但这会引发错误(如我所料):
FieldError: Cannot resolve keyword 'student' into field. Choices are: courses, created, id, language, level, modified, progress
更新 2 - 越来越近了!
在@AKX 的评论帮助下好的:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter(courses__student__organisation_id=organisation)
现在开设 return 课程,但 return 每个注册学生都有一份副本。所以现在我需要对它们进行分组,以便每条记录只出现一次...
首先您需要将 self.filter
更改为 self.get_queryset().filter()
或在 manager
.
def get_queryset(self):
return super(CourseManager, self).get_queryset()
在管理器中创建一个函数
def get_courses(self,organisation):
return self.get_queryset.filter(student__oraganisation=organisation)
这应该 return students
并且您不需要调用 .all()
- 过滤后的 qs
无论哪种方式 return 都是它找到的对象。
编辑
试试这个:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter( \
enrollments__student__organisation_id=organisation).distinct()
更新 2
你可以试试 from django.db.models import Q
https://docs.djangoproject.com/en/3.0/topics/db/queries/
或 annotate
基于这个答案 Get distinct values of Queryset by field 过滤掉每个学生,这样它就会出现一次。