Python 3 Django Rest Framework - как добавить собственный менеджер в эту структуру модели M-1-M? - PullRequest
3 голосов
/ 15 апреля 2020

У меня есть следующие модели:

  • Организация

  • Студент

  • Курс

  • Зачисление

Студент принадлежит к организации

Студент может записаться на 1 или более курсов

Так Запись о регистрации в основном состоит из данного курса и данного студента

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)

и 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: Невозможно разрешить ключевое слово 'student' в поле. Варианты выбора: курсы, созданные, идентификатор, язык, уровень, изменение, прогресс

ОБНОВЛЕНИЕ 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)

теперь возвращает курсы, но возвращает копию для каждого зачисленного студента. Теперь мне нужно сгруппировать их, чтобы каждая запись появлялась только один раз ...

1 Ответ

1 голос
/ 18 апреля 2020

Сначала вам нужно изменить 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)

This должен вернуть students, и вам не нужно вызывать .all() - отфильтрованный qs в любом случае возвращает вам все найденные объекты.

EDIT

Попробуйте это:

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 на основе этого ответа Получите различные значения Queryset по полю , где вы отфильтровываете каждого учащегося, чтобы он отображался один раз.

...