为什么我不能将 __getattr__ 与 Django 模型一起使用?

发布于 2024-10-11 02:54:57 字数 2433 浏览 6 评论 0原文

我在网上看到过人们将 __getattr__ 与 Django 模型一起使用的示例,但每当我尝试时都会出错。 (Django 1.2.3)

当我在普通对象上使用 __getattr__ 时没有任何问题。例如:

class Post(object):
     def __getattr__(self, name):
         return 42

工作得很好...

<前><代码>>>>>从 blog.models 导入帖子 >>>>> p = 帖子() >>>>> p.随机 42

现在,当我使用 Django 模型尝试它时:

from django.db import models
class Post(models.Model):
     def __getattr__(self, name):
         return 42

并在解释器上测试它:

<前><代码>>>>>从 blog.models 导入帖子 >>>>> p = 帖子() 错误:标记化输入时发生意外错误

以下回溯可能已损坏 或无效 错误消息为:('EOF 在多行语句中', (6, 0))

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

/用户/乔什/项目/ 在()中

/Users/josh/project/lib/python2.6/site-packages/django/db/models/base.pyc 在 init(self, *args, **kwargs) 第338章 第339章 该函数的参数" % kwargs.keys()[0]) --> [第 340 章] 实例=自身) 第341章 342 def 代表(自我):

/Users/josh/project/lib/python2.6/site-packages/django/dispatch/dispatcher.pyc 在发送(自我,发件人,**命名) 160 161 self._live_receivers(_make_id(sender))中的接收者: --> 162 响应=接收者(信号=自身,发送者=发送者, **命名) 163 响应.append((接收者,响应)) 164 返回响应

/Users/josh/project/python2.6/site-packages/photologue/models.pyc 在add_methods(发件人,实例, 信号,*args,**kwargs) 第728章 第729章 -->第730章 第731章 第732章 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 类进行相同的更改,这似乎是代码腐烂的秘诀。

I've seen examples online of people using __getattr__ with Django models, but whenever I try I get errors. (Django 1.2.3)

I don't have any problems when I am using __getattr__ on normal objects. For example:

class Post(object):
     def __getattr__(self, name):
         return 42

Works just fine...

 >>> from blog.models import Post
 >>> p = Post()
 >>> p.random
 42

Now when I try it with a Django model:

from django.db import models
class Post(models.Model):
     def __getattr__(self, name):
         return 42

And test it on on the interpreter:

 >>> from blog.models import Post
 >>> p = Post()
 ERROR: An unexpected error occurred while tokenizing input The

following traceback may be corrupted
or invalid The error message is: ('EOF
in multi-line statement', (6, 0))

--------------------------------------------------------------------------- TypeError
Traceback (most recent call last)

/Users/josh/project/
in ()

/Users/josh/project/lib/python2.6/site-packages/django/db/models/base.pyc
in init(self, *args, **kwargs)
338 if kwargs:
339 raise TypeError("'%s' is an invalid keyword
argument for this function" %
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
in send(self, sender, **named)
160
161 for receiver in self._live_receivers(_make_id(sender)):
--> 162 response = receiver(signal=self, sender=sender,
**named)
163 responses.append((receiver, response))
164 return responses

/Users/josh/project/python2.6/site-packages/photologue/models.pyc
in add_methods(sender, instance,
signal, *args, **kwargs)
728 """
729 if hasattr(instance, 'add_accessor_methods'):
--> 730 instance.add_accessor_methods()
731
732 # connect the add_accessor_methods function to the
post_init signal

TypeError: 'int' object is not
callable

Can someone explain what is going on?


EDIT: I may have been too abstract in the examples, here is some code that is closer to what I actually would use on the website:

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...

While I could create a property for each attribute of the Post class, that would lead to a lot of code duplication. Further more, that would mean anytime I add or edit a attribute of the Post class I would have to remember to make the same change to the CuratedPost class, which seems like a recipe for code rot.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

聽兲甴掵 2024-10-18 02:54:57

使用 __getattr__ 时必须小心。只拦截你知道的,让基类处理你不知道的。

第一步是,你可以使用属性来代替吗?如果你想要一个返回 42 的“random”属性,那么这会更安全:

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)

One must be careful using __getattr__ . Only intercept what you know, and let the base class handle what you do not.

The first step is, can you use a property instead? If you want a "random" attribute which return 42 then this is much safer:

class Post(...):
  @property
  def random(self):
    return 42

If you want "random_*" (like "random_1", "random_34", etc) to do something then you'll have to use __getattr__ like this:

class Post(...):
  def __getattr__(self, name):
    if name.startswith("random_"):
      return name[7:]
    return super(Post, self).__getattr__(name)
隱形的亼 2024-10-18 02:54:57

当模型首次初始化时(即通过加载 shell),Django 发送某些信号 - 通过使对 __getattr 的调用始终返回一个整数,您已经以 Django 的方式修改了代码信号没有预料到(因此,它们正在中断)。

如果你想这样做,也许可以尝试这样:

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

Django sends certain signals when models are first initialized (ie, by loading up the shell) - by making it so that calls to __getattr always return an integer, you've modified the code in a way that Django signals weren't expecting (and therefore, they're breaking).

If you want to do this, maybe try it this way:

def __getattr__(self, attr):
  if hasattr(self, attr):
    return super(MyModel, self).__getattr__(attr)
  return 42
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文