为什么我不能将 __getattr__ 与 Django 模型一起使用?
我在网上看到过人们将 __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 signalTypeError: '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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
使用 __getattr__ 时必须小心。只拦截你知道的,让基类处理你不知道的。
第一步是,你可以使用属性来代替吗?如果你想要一个返回 42 的“random”属性,那么这会更安全:
如果你想要“random_*”(如“random_1”、“random_34”等)做某事,那么你必须像这样使用 __getattr__ :
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:
If you want "random_*" (like "random_1", "random_34", etc) to do something then you'll have to use __getattr__ like this:
当模型首次初始化时(即通过加载 shell),Django 发送某些信号 - 通过使对 __getattr 的调用始终返回一个整数,您已经以 Django 的方式修改了代码信号没有预料到(因此,它们正在中断)。
如果你想这样做,也许可以尝试这样:
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: