【问题标题】:Django custom reverse manager using a through model使用直通模型的 Django 自定义反向管理器
【发布时间】:2019-08-22 03:34:06
【问题描述】:

我有一组模型,其中 Student 通过另一个模型 ClassStudentMapping 分配多个 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() 获得一个学生的所有课程。我希望能够使用自定义管理器调用自定义查询以获取学生当天所有课程

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' object has no attribute 'today_classes'。

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

但奇怪的是,它返回了每个结果的倍数 - 两个用于只在星期三发生的课程,9 (!) 用于一周中所有 7 天发生的课程——并且它禁用了默认的 Class.objects.all() 调用,我仍然需要能够调用它。

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

有什么想法吗?

【问题讨论】:

    标签: django django-models django-managers


    【解决方案1】:

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

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

    Django docs 应该对此有更多的了解。它还在文档中提到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()...

    【讨论】:

    • 谢谢,这得到了我正在寻找的东西,并以一种简单的方式完成它。
    • 只是指出‘objects’使用的是特殊字符而不是''单引号
    【解决方案2】:

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

    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
    

    【讨论】:

    • 谢谢,但我不希望这个逻辑存在于Student 模型中。使用自定义管理器允许我使用它正在操作的适当模型定义自定义逻辑。
    猜你喜欢
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 2011-04-20
    • 2011-11-28
    • 2021-06-07
    • 1970-01-01
    • 2013-05-12
    相关资源
    最近更新 更多