使用直通模型的 Django 自定义反向管理器

Django custom reverse manager using a through model

我有一组模型,其中 Student 通过另一个模型 ClassStudentMapping 分配了多个 Classes,它有一个字段,我可以在其中设置 classes 学生在特定的一天。

models.py

class Student (models.Model):
    ...


class Class(models.Model):
    ...
    students = models.ManyToManyField(Student, related_name='classes', through="ClassStudentMapping")


class ClassStudentMapping(models.Model):
    class = models.ForeignKey(Class)
    student = models.ForeignKey(Student)
    DAYS_OF_THE_WEEK = [
        ('0', 'Monday'),
        ('1', 'Tuesday'),
        ('2', 'Wednesday'),
        ('3', 'Thursday'),
        ('4', 'Friday'),
        ('5', 'Saturday'), 
        ('6', 'Sunday'),
    ]
    days = ArrayField(models.CharField(max_length=1, choices=DAYS_OF_THE_WEEK), size=20, default=list(range(0,7)))

因此,如果 this_studentStudent 的一个实例,那么我显然可以通过 student.classes.all() 获得学生拥有的所有 classes。我希望能够使用自定义管理器调用自定义查询以获取学生在当天当天.

拥有的所有classes
class ClassTodayManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(classstudentmapping__days__contains=[str(datetime.today().weekday())])

但我不知道该把这个管理器放在哪里,这样我就可以通过 student.today_classes.all().

之类的方式调用它

根据 Django 文档,我很天真地尝试了:

class Class(models.Model):
    today_classes = ClassTodayManager()

但这导致“AttributeError:'Student' 对象没有属性 'today_classes'。

我意识到我需要 custom reverse manager,并尝试了 student.classes(manager='today_classes')

但奇怪的是,它会返回每个结果的倍数——对于仅在星期三发生的 class 返回两个,对于一周七天都发生的 class 返回 9 (!) ——它禁用了默认的 Class.objects.all() 调用,我仍然需要能够调用它。

我认为这里有一个与 through 模型相关的并发症,我不知道如何解决,这使得我可以在网上找到的所有其他示例都无关紧要。

有什么想法吗?

除了自定义管理器,我认为如果您可以使用这样的 属性 方法会更好:

class Student (models.Model):
    ...
    @property
    def today_classes(self):
       return self.classes.filter(classstudentmapping__days__contains=[str(datetime.today().weekday())])


# Usage:
student.today_classes # will return queryset

如果您更改管理器的实现,因此您不通过覆盖 get_queryset 方法进行过滤,那么这应该将您的管理器设置为在反向关系中使用:

class Classes(model):
    ...
    objects = ClassTodayManager()
    class Meta:
        base_manager_name = 'objects'

文档中的 Django docs should shed more light on this. It also mentions somewhere 不要以任何方式覆盖 get_queryset 方法,如果您要替换基本管理器则过滤掉对象。

在你的情况下,我不会覆盖 get_queryset,而是将该逻辑移动到 today_classes 方法,如下所示:

class ClassesTodayManager(Manager):
    def today_classes(self):
        return self.get_queryset().filter(classstudentmapping__days__contains=[str(datetime.today().weekday())])

然后就可以这样访问了:student.classes.today_classes()...