python - 使用 __getattr__ 更改 Django 模型的行为

标签 python django class

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

cache.part_number  
vs  
cache.product.part_number

我试过如下覆盖 __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

我做错了什么?

最佳答案

考虑您的 __getattr__ 方法中的代码:

value = getattr(self.product, name, None)

尝试猜测调用 self.product 时会发生什么。我会给你一个线索:它涉及对 __getattr__ 的调用。 documentation有详细信息:

Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.

您是否想知道 self.product 如何解析为正确的 Product 实例,即使您没有在任何地方设置它?

Note that if the attribute is found through the normal mechanism, __getattr__() is not called.

Django 做了一些涉及拦截的魔法,您猜对了,__getattr__。因此 self 自动以属性 product 结束。由于您正在覆盖 __getattr__ 方法,Django 的魔力将停止工作并使用您的版本。由于 self.product 不是实例属性,__getattr__ 会被再次调用,如此反复,导致无限循环。

你最好使用 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)

关于python - 使用 __getattr__ 更改 Django 模型的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3757951/

相关文章:

python - 无法使用 pip 安装 scitools-iris : ImportError No module named target_pkg (in pyke)

python - 替换用小写字母括起来的字符

python - 如何在 Python 中定义类似 int.from_bytes() 的方法?

java - “找不到或加载主类”是什么意思?

python - mysqldb编程错误

javascript - Django 模型选择字段提交

html - 在 Django 的 css 文件中加载静态文件

django - 将用户名分配给 models.py 表中的字段

管理面板的 Django CSRF 验证失败

android,如何获取包名?