为什么无法在Django模型中使用__getattr__?

9 投票
2 回答
6060 浏览
提问于 2025-04-16 09:35

我在网上看到有人用 __getattr__ 来处理 Django 模型的例子,但每次我尝试的时候都会出错。(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))

--------------------------------------------------------------------------- 类型错误
回溯(最近的调用最后)

/Users/josh/project/ 在 ()

/Users/josh/project/lib/python2.6/site-packages/django/db/models/base.pyc 在 init(self, *args, **kwargs) 338 如果有 kwargs: 339 抛出类型错误("'%s' 是这个函数的无效关键字 参数" % kwargs.keys()[0]) --> 340 signals.post_init.send(sender=self.class, instance=self) 341 342 def repr(self):

/Users/josh/project/lib/python2.6/site-packages/django/dispatch/dispatcher.pyc 在 send(self, sender, **named) 160 161 对于接收者在 self._live_receivers(_make_id(sender)): --> 162 response = receiver(signal=self, sender=sender, **named) 163 responses.append((receiver, response)) 164 返回响应

/Users/josh/project/python2.6/site-packages/photologue/models.pyc 在 add_methods(sender, instance, signal, *args, **kwargs) 728 """ 729 如果 hasattr(instance, 'add_accessor_methods'): --> 730 instance.add_accessor_methods() 731 732 # 将 add_accessor_methods 函数连接到 post_init 信号

类型错误: '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 类中做同样的修改,这听起来像是代码腐烂的开始。

2 个回答

0

Django在模型第一次初始化时会发送一些信号(比如,当你打开命令行界面时)。如果你让调用__getattr总是返回一个整数,这样就改变了Django信号原本的预期,导致它们出现问题。

如果你想这样做,可以试试下面的方法:

def __getattr__(self, attr):
  if hasattr(self, attr):
    return super(MyModel, self).__getattr__(attr)
  return 42
7

使用 __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)

撰写回答