Why can't I use __getattr__ with Django models?

I have seen examples online using __getattr__ with Django models, but whenever I try, I get errors. (Django 1.2.3)

I have no problem when I use __getattr__ for regular objects. For example:

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

It works well ...

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

Now when I try it using the Django model:

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

And check it on the interpreter:

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

The next trace may be damaged or invalid. Error message: ("EOF in multi-line expression ', (6, 0))

----------------------------------------------- --- ------------------------- TypeError
Traceback (last last call)

/ 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 = itself) 341 342 def repr (self):

/Users/josh/project/lib/python2.6/site-packages/django/dispatch/dispatcher.pyc in the shipment (itself, sender, ** named) 160 161 for the receiver in self._live_receivers (_make_id (sender)): → 162 response = receiver (signal = itself, sender = sender, ** by name) 163 response.append ((receiver, answer)) 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: object 'int' not callable

Can someone explain what is happening?


EDIT: Maybe I was too abstract in the examples, here is some code that is closer to what I actually 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... 

Although I could create a property for each attribute of the Post class, this will lead to a lot of code duplication. Moreover, this would mean that someday I would add or edit an attribute of the Post class that I would have to remember in order to make the same change in the CuratedPost class, which seems to be a recipe for the code gene.

+6
python django django-models getattr
source share
2 answers

You have to be careful using __getattr__. Just intercept what you know and let the base class handle what you are not doing.

In the first step, can you use the property? If you need a "random" attribute that returns 42, this is much safer:

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

If you want "random_ *" (for example, "random_1", ​​"random_34", etc.) to do something, you will have to use __getattr__ as follows:

 class Post(...): def __getattr__(self, name): if name.startswith("random_"): return name[7:] return super(Post, self).__getattr__(name) 
+5
source share

Django sends specific signals when models are initialized first (i.e. loading the shell) - making it so that calls to __getattr always return an integer, you change the code so that the Django signals aren (and therefore break).

If you want to do this, try doing this:

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

All Articles