自定义Manager与QuerySet结合使用时关联对象调用方法报错问题
嘿,我刚好踩过这个坑,来给你捋清楚怎么解决这个问题!
问题出在哪?
你遇到的AttributeError其实是因为:当你单独定义自定义Manager和QuerySet时,Django的反向关联管理器(就是你用的some_employee.attendancelog_set)并不会自动继承QuerySet里的方法。哪怕你的Manager的get_queryset()返回了自定义QuerySet,反向关联用的默认管理器还是没把这些QuerySet方法绑定进去,所以调用today()时就找不到这个属性了。
靠谱的解决方案
推荐两种方法,第一种是Django官方最推荐的:
方法1:用from_queryset()把Manager和QuerySet绑定起来
Django提供了BaseManager.from_queryset()方法,可以把QuerySet里的所有方法注入到你的自定义Manager中,同时反向关联的管理器也会自动继承这些方法。修改代码如下:
from django.db import models from datetime import datetime class AttendanceLogQuerySet(models.QuerySet): def today(self): # 返回to_datetime为今日的日志 return self.filter(to_datetime__date=datetime.today()) class AttendanceLogManager(models.Manager): def some_special_create_method(self, some_args): # 这里写你的自定义创建逻辑 pass # 关键一步:把QuerySet的方法合并到Manager里 AttendanceLogManager = AttendanceLogManager.from_queryset(AttendanceLogQuerySet) # 然后在模型里使用这个合并后的Manager class AttendanceLog(models.Model): from_datetime = models.DateTimeField(_('from')) to_datetime = models.DateTimeField(_('to')) employee = models.ForeignKey(Employee, on_delete=models.CASCADE, verbose_name=_('employee')) objects = AttendanceLogManager()
这样改完之后,some_employee.attendancelog_set.today()就能正常调用了,因为反向关联的管理器现在已经拥有了QuerySet里的所有方法。
方法2:直接给ForeignKey指定QuerySet
如果你不想修改Manager的结构,也可以在ForeignKey字段上直接指定queryset参数,让反向关联用你的自定义QuerySet:
class AttendanceLog(models.Model): from_datetime = models.DateTimeField(_('from')) to_datetime = models.DateTimeField(_('to')) employee = models.ForeignKey( Employee, on_delete=models.CASCADE, verbose_name=_('employee'), queryset=AttendanceLogQuerySet.as_manager() )
不过这种方法灵活性差一点,后续如果修改QuerySet或Manager,得同步更新这个参数,所以还是第一种方法更稳妥。
为啥之前用as_manager()就行?
当你直接写objects = AttendanceLogQuerySet.as_manager()时,生成的Manager是一个“全能”管理器——它不仅包含了QuerySet的所有方法,Django的反向关联也会自动使用这个管理器,所以today()方法能正常调用。但自定义Manager单独返回QuerySet时,默认的Manager并没有把这些QuerySet方法暴露给反向关联,所以就报错了。
内容的提问来源于stack exchange,提问作者emihir0

