Почему я не могу использовать __getattr__ с моделями 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))

-------------------------------------------------- ------------------------- TypeError
Traceback (последний звонок последний)

/ Пользователи / josh / project / in ()

/Users/josh/project/lib/python2.6/site-packages/django/db/models/base.pyc в init(self, * args, ** kwargs) 338, если kwargs: 339 поднять TypeError("'%s'является недопустимым аргументом ключевого слова для этой функции " % kwargs.keys()[0]) -> 340 сигналов.post_init.send(отправитель = 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 ответа = получатель (сигнал = я, отправитель = отправитель, ** имя) 163 response.append ((получатель, ответ)) 164 ответных ответа

/Users/josh/project/python2.6/site-packages/photologue/models.pyc в add_methods(отправитель, экземпляр, сигнал, *args, **kwargs) 728 """ 729, если hasattr (экземпляр, 'add_accessor_methods'): -> 730 instance.add_accessor_methods() 731 732 # подключить функцию add_accessor_methods к сигналу 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, что похоже на рецепт гниения кода.

2 ответа

Нужно быть осторожным, используя __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)

Django посылает определенные сигналы при первой инициализации моделей (т. Е. При загрузке оболочки) - делая так, чтобы __getattr всегда возвращайте целое число, вы изменили код так, что сигналы Django не ожидались (и, следовательно, они ломаются).

Если вы хотите сделать это, возможно, попробуйте это так:

def __getattr__(self, attr):
  if hasattr(self, attr):
    return super(MyModel, self).__getattr__(attr)
  return 42
Другие вопросы по тегам