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