Почему я не могу использовать __getattr__ с моделями Django? - PullRequest
9 голосов
/ 08 января 2011

Я видел в Интернете примеры людей, использующих __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))

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

/ Users/ josh / project / in ()

/ Пользователи / 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_получатели (_make_id (отправитель)): -> 162 ответ = получатель (сигнал = я, отправитель = отправитель, ** по имени) 163 response.append ((получатель, ответ)) 164 ответных ответов

/ Пользователи/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 ]

6 голосов
/ 08 января 2011

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

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

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

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