2012-06-05 2 views
1

Каков подходящий способ переопределения класса модели getattr в Django 1.4?Соответствующий способ переопределения модели __getattr__ в 1.4

У меня есть модель структуры, как:

class Main(models.Model): 
    [blah] 

class Detail(models.Model): 
    main = models.ForeignKey(Main) 
    name = models.CharField(max_length=255) 
    value= models.CharField(max_length=255) 

Я переопределены мой Main. getattr _ так что я мог бы ссылаться на детали записей, как если бы они были нормальными Основные атрибуты. например простой мета- модель модель как

>>> m = Main.objects.create() 
>>> Detail.objects.create(main=m, name='name', value='value') 
>>> print m.name 
'value' 

Чтобы сделать это, мой до 1,4 GetAttr выглядел как:

def __getattr__(self, attrname): 
    qs = self.details.filter(name=attrname) 
    c = len(qs) 
    if c == 0: 
     raise AttributeError 
    elif c == 1: 
     return qs[0].value 
    else: 
     return [d.value for d in qs] 

Это работало отлично, пока я повышен до 1,4. Теперь я получаю все типы ошибки «атрибут X не существует». Я пробовал что-то вроде , но не повезло. Кажется, что в особенности конфликтуют с атрибутами «_ * _ _ cache» , которые Django генерирует для ссылок ForeignKey.

def __getattr__(self, attrname): 
    try: 
     return super(Main, self).__getattr__(attrname) 
    except AttributeError: 
     pass 
    qs = self.details.filter(name=attrname) 
    c = len(qs) 
    if c == 0: 
     raise AttributeError 
    elif c == 1: 
     return qs[0].value 
    else: 
     return [d.value for d in qs] 

Как это разрешить?

+3

Это подходящее время, чтобы остановиться и спросить: почему H-E-хоккейные клюшки вы делаете *, что *? Мало того, что он похож на грязный хак, он также * чрезвычайно неэффективен, генерируя запрос базы данных каждый раз, когда вы получаете доступ к атрибуту экземпляра. Вы даже не кешируете ничего. –

+1

По общему признанию, мой случай необычен. Чтобы кратко описать это, моя модель позволяет пользователям определять свои собственные модели. Кэширование является тривиальным для добавления, которое я забыл для простоты. – Cerin

ответ

1

Копаясь в новом коде модели, кажется, что база данных была существенно изменена, так что класс Model больше не имеет __getattr__ для переопределения. Вместо этого мне нужно позвонить object.__getattribute__, от которого наследуется базовая модель. Тем не менее, Django хранит кешированные данные в специальных атрибутах, которые необходимо надлежащим образом обрабатывать.

Мой новый __getattr__ теперь выглядит следующим образом:

def __getattr__(self, attrname): 
    try: 
     return super(Main, self).__getattribute__(attrname) 
    except AttributeError: 
     if attrname.startswith('_prefetched'): 
      raise 
    qs = self.details.filter(name=attrname) 
    c = len(qs) 
    if c == 0: 
     raise AttributeError 
    elif c == 1: 
     return qs[0].value 
    else: 
     return [d.value for d in qs] 
1

Я не пробовал, но __getattribute__() может работать:

class Main(models.Model): 
    def __getattribute__(self, attrname): 
     try: 
      return super(Main, self).__getattribute__(attrname) 
     except AttributeError: 
      try: 
       return self.__getattr__(attrname) 
      except AttributeError: 
       # do your database accessing 

Но, как сказал Крис Пратт, это немного не эффективным. Возможно, вы захотите рассмотреть кеширование своих атрибутов.

+1

'getattr (self, name)' будет вызывать 'self .__ getattribute __ (name)', что приводит к бесконечной рекурсии. –

+0

О, ты прав! Я отредактировал свой ответ соответственно. – Maccesch

+0

Это вызывает 'RuntimeError: превышена максимальная глубина рекурсии' – perrohunter

Смежные вопросы