为啥我不能将 __getattr__ 与 Django 模型一起使用?
Posted
技术标签:
【中文标题】为啥我不能将 __getattr__ 与 Django 模型一起使用?【英文标题】:Why can't I use __getattr__ with Django models?为什么我不能将 __getattr__ 与 Django 模型一起使用? 【发布时间】:2011-06-05 13:05:42 【问题描述】:我在网上看到有人在 Django 模型中使用 __getattr__
的示例,但每当我尝试时都会出错。 (Django 1.2.3)
当我在普通对象上使用__getattr__
时没有任何问题。例如:
class Post(object):
def __getattr__(self, name):
return 42
工作得很好......
>>> from blog.models import Post >>> p = Post() >>> p.random 42
现在当我尝试使用 Django 模型时:
from django.db import models
class Post(models.Model):
def __getattr__(self, name):
return 42
并在解释器上进行测试:
>>> from blog.models import Post >>> p = Post() ERROR: An unexpected error occurred while tokenizing input The
后续回溯可能已损坏 或无效错误信息是:('EOF 在多行语句中', (6, 0))
----------------------------------- ---------------------------- 类型错误 Traceback(最近一次通话最后一次)
/用户/乔希/项目/ 在 ()
/Users/josh/project/lib/python2.6/site-packages/django/db/models/base.pyc 在 init(self, *args, **kwargs) 338如果kwargs: 第339章 此函数的参数" % kwargs.keys()[0]) --> 340 个信号.post_init.send(sender=self.class, 实例=自己) 341 342 def repr(自我):
/Users/josh/project/lib/python2.6/site-packages/django/dispatch/dispatcher.pyc 在发送(自我,发件人,**命名) 160 161 用于 self._live_receivers(_make_id(sender)) 中的接收器: --> 162 响应 = 接收者(信号 = 自我,发送者 = 发送者, **命名) 163 response.append((接收者,响应)) 164返回响应
/Users/josh/project/python2.6/site-packages/photologue/models.pyc 在 add_methods(sender, instance, 信号,*args,**kwargs) 第728章 第729章 --> 730 实例.add_accessor_methods() 731 第732章 post_init 信号
TypeError: 'int' 对象不是 可调用
有人能解释一下是怎么回事吗?
编辑:我在示例中可能过于抽象,这里有一些更接近我在网站上实际使用的代码:
class Post(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField()
date_published = models.DateTimeField()
content = RichTextField('Content', blank=True, null=True)
# Etc...
Class CuratedPost(models.Model):
post = models.ForeignKey('Post')
position = models.PositiveSmallIntegerField()
def __getattr__(self, name):
''' If the user tries to access a property of the CuratedPost, return the property of the Post instead... '''
return self.post.name
# Etc...
虽然我可以为 Post 类的每个属性创建一个属性,但这会导致大量代码重复。此外,这意味着每当我添加或编辑 Post 类的属性时,我都必须记住对 CuratedPost 类进行相同的更改,这似乎是代码腐烂的秘诀。
【问题讨论】:
真的是“返回self.post.name”吗?应该是“return getattr(self.post, name)” 确实,代码应该是这样的 在name
输入本身就是"post"
的情况下,我使用类似的模式遇到了无限递归问题。
【参考方案1】:
使用 __getattr__ 时必须小心。只拦截你知道的,让基类处理你不知道的。
第一步是,你可以使用属性来代替吗?如果你想要一个返回 42 的“随机”属性,那么这更安全:
class Post(...):
@property
def random(self):
return 42
如果你想让“random_*”(如“random_1”、“random_34”等)做某事,那么你必须像这样使用__getattr__:
class Post(...):
def __getattr__(self, name):
if name.startswith("random_"):
return name[7:]
return super(Post, self).__getattr__(name)
【讨论】:
我在帖子中使用的示例应该表明,即使是非常简单的 getattr 属性应用程序也会在我扩展 Django 模型对象时中断。我已经更新了我的原始帖子,以包含更接近我实际使用的内容。 我不相信这适用于 Django,因为 models.Model 没有__getattr__
,所以你的 super(Post, self).__getattr__(...)
应该抛出异常。
你试过了吗? Python 提供了一个默认的 getattr,所以它“应该”不是问题。【参考方案2】:
Django 在模型第一次初始化时发送某些信号(即,通过加载 shell)——通过使对 __getattr
的调用总是返回一个整数,你已经以 Django 信号的方式修改了代码'没想到(因此,他们正在打破)。
如果你想这样做,也许可以这样尝试:
def __getattr__(self, attr):
if hasattr(self, attr):
return super(MyModel, self).__getattr__(attr)
return 42
【讨论】:
这不起作用,因为 hasattr(self, attr) 最终会递归调用 __getattr__。你想要“attr in self.__dict__”。 实际上,这也行不通,因为只有在 attr 不在 __dict__ 中时才会调用 __getattr__ @Andrew:听起来你可以用__getattribute__
做到这一点,不过
@Cameron: __getattribute__ 也很棘手,只是方式不同。我怀疑@Joshmaker 应该改用属性。
“返回 42”只是一个例子,__getattr__
无论我返回什么都会中断。我已经看到使用 __getattr__
没有问题的旧 Django 项目的代码示例,但我找不到任何说明它已被弃用的文档。以上是关于为啥我不能将 __getattr__ 与 Django 模型一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
Python-魔法函数__getattr__()与__getattribute__()的区别
getattr(sys.modules[__name__], func_name)
python - getattr 与 getattribute 机制