使用__getattr__更改Django模型的行为

drj*_*eep 5 python django class

我正在尝试更改Django模型的行为,以允许我直接从父级访问外键的属性,例如

cache.part_number  
vs  
cache.product.part_number
Run Code Online (Sandbox Code Playgroud)

我尝试重写该__getattr__方法如下,但当我尝试访问外键的属性时,我收到一个递归错误

class Product(models.Model):
    part_number = models.CharField(max_length=10)
    ...

class Cache(models.Model):
    product = models.ForeignKey(Product)
    ...

    def __getattr__(self, name):
        value = getattr(self.product, name, None)
        if value:
            return value
        else:
            raise AttributeError
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Man*_*dan 9

考虑__getattr__方法中的代码:

value = getattr(self.product, name, None)
Run Code Online (Sandbox Code Playgroud)

尝试猜测self.product调用时会发生什么.我会给你一个线索:它涉及到一个电话__getattr__.该文件有详细介绍:

当属性查找未在通常位置找到属性时调用(即,它不是实例属性,也不是在类树中找到自己).name是属性名称.此方法应返回(计算)属性值或引发AttributeError异常.

你有没有想过如何self.product解决正确的Product实例,即使你没有把它设置在任何地方?

请注意,如果通过常规机制找到属性,__getattr__()则不会调用该属性.

Django做了一些涉及拦截的魔法,你猜对了__getattr__.从而self自动以一个属性结束product.由于你重写了这个__getattr__方法,Django的魔法停止工作,你的版本被使用了.因为self.product不是实例属性,__getattr__所以再次调用,再次调用,依此类推,导致无限循环.

你最好用a property来实现这个目标.

class Cache(models.Model):
    product = models.ForeignKey(Product)
    ...

    def _get_part_number(self):
        part_number = self.product.part_number
        if not part_number:
            raise AttributeError
        return part_number
    part_number = property(_get_part_number)
Run Code Online (Sandbox Code Playgroud)