Python 3 Django Rest Framework - 如何向这个 M-1-M 模型结构添加自定义管理器?
Posted
技术标签:
【中文标题】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? 【发布时间】:2020-07-28 19:22:02 【问题描述】:我有这些模型:
组织
学生
课程
注册
学生属于组织
学生可以注册一门或多门课程
因此,注册记录基本上由给定的课程和给定的学生组成
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 并以某种方式从关系的那一侧查询/过滤:
class CourseManager(models.Manager):
def org_courses_enrolled(self, organisation):
return self.filter(enrollment__student__organisation__name=organisation).all()
这可行,但它给了我相同的 100 条注册记录 :(
我想要得到的是: 基于给定的组织 查找所有已注册的学生 然后 (DISTINCT?) 获取该组织的已注册课程列表
这是风景:
class OrganisationCoursesView(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
serializer_class = CourseSerializer
queryset = Course.objects.get_courses(1)
和网址:
# 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:无法将关键字“学生”解析为字段。选择是: 课程、创建、id、语言、级别、修改、进度
更新 2 - 越来越近了!
好的,在 @AKX 的 cmets 的帮助下:
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)
现在确实会返回课程,但它会为每个注册学生返回一份副本。所以现在我需要对它们进行分组,这样每条记录只会出现一次......
【问题讨论】:
顺便说一句,由于Organisation.name
不是唯一的,如果有多个同名组织,使用organisation__name
可能会返回意外结果。
好地方,谢谢,我已经改用 id 了
filter(enrollment_set__student__organisation_id=organisation)
是否有用?
否 - 无法解析关键字 'enrollment_set' :( 请注意,课程的外键是在 Enrollment 中定义的(学生外键也是如此)
他们仍然有相反的关系,docs.djangoproject.com/en/3.0/topics/db/queries/…
【参考方案1】:
首先您需要将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)
这应该返回students
,您不需要调用.all()
- 过滤后的qs
会返回它找到的所有对象。
编辑
试试这个:
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 过滤掉每个学生,使其出现一次。
【讨论】:
感谢您的快速回复,但这并没有完全到达那里。 “get_courses”返回学生。所以我必须使用 StudentSerializer。我有 100 名学生(100 名注册)展示,而不仅仅是 100 名学生注册的 8 门课程。我想在某个地方/不知何故我需要按课程 ID 对注册进行分组 编辑到达那里 ;)以上是关于Python 3 Django Rest Framework - 如何向这个 M-1-M 模型结构添加自定义管理器?的主要内容,如果未能解决你的问题,请参考以下文章
[python]django rest framework写POST和GET接口
Python 3 & Django Rest Framework - 如何查询 M-1-M 模型?
Python 3 Django Rest Framework - 如何向这个 M-1-M 模型结构添加自定义管理器?