Django: получить все реализации абстрактного базового класса - PullRequest
12 голосов
/ 13 ноября 2009

Я определил некоторые модели, которые выглядят так:

class ABClass(models.Model):
   #common attributes
   class Meta:
     abstract = True

class ConcreteClass1(ABClass):
   #implementation specific attributes

class ConcreteClass2(ABClass):
   #implementation specific attributes

class ModifiedConcreteClass1(ConcreteClass1):
   #implementation specific attributes

У меня есть представление, которое принимает параметр 'type', который указывает тип класса для отображения. Если 'type = None', я хочу отобразить все потомки ABClass (в этом случае ConcreteClass {1,2}, ModifiedConcreteClass1). Я изучил документацию и не могу найти способ сделать это, не требующий ведения списка ConcreteClasses. ABClass.__subclasses__() показалось многообещающим, но он возвращает пустой список, когда я его называю.

В настоящее время лучшая стратегия, которую я имею, состоит в том, чтобы создать переменную CLASS_LIST в models.py, которая заполняется всеми потомками ABClass во время выполнения с использованием методов inspect. Есть ли способ сделать это с помощью API django?

Спасибо

Ответы [ 5 ]

6 голосов
/ 09 мая 2014

Список ConcreteClasses уже поддерживается, если у вас есть 'django.contrib.contenttypes' в INSTALLED_APPS.

Вот как я это делаю:

class GetSubclassesMixin(object):
    @classmethod
    def get_subclasses(cls):
        content_types = ContentType.objects.filter(app_label=cls._meta.app_label)
        models = [ct.model_class() for ct in content_types]
        return [model for model in models
                if (model is not None and
                    issubclass(model, cls) and
                    model is not cls)]

class ABClass(GetSubclassesMixin, models.Model):
    pass

Когда вам нужен список подклассов, просто позвоните ABClass.get_subclasses().

Я использую это с конкретными базовыми классами, но не вижу причин для этого не работать с абстрактными.

Этот подход работает, даже если ваши конкретные подклассы находятся в разных приложениях Django. Однако обратите внимание, что они должны иметь тот же app_label, что и базовый класс, поэтому код в get_subclasses() может их найти.

5 голосов
/ 13 ноября 2009

Посмотрите на этот похожий вопрос:

Как получить доступ к дочерним классам объекта в django, не зная имени дочернего класса?

Мое решение этого было, по сути, таким:

def get_children(self):
    rel_objs = self._meta.get_all_related_objects()
    return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]

Хотя есть и ряд других хороших решений. Вполне вероятно, что вы действительно хотите детей базового класса, а не только дочерних классов. Если нет, то ответы могут послужить мотивом для вашего решения.

5 голосов
/ 13 ноября 2009

Я не смотрел на то, что делает Django, когда вы устанавливаете абстрактное True в бэкэнде, но я поиграл с этим и обнаружил, что это работает. Обратите внимание, это работает только в Python 2.6

from abc import ABCMeta

class ABClass():
    __metaclass__ = ABCMeta

class ConcreteClass1(ABClass):
     pass

class ConcreteClass2(ABClass):
     pass

print ABClass.__subclasses__()

Результаты в

[<class '__main__.ConcreteClass1'>, <class '__main__.ConcreteClass2'>]

Без использования ABCMeta и __metaclass__ вы получите пустой список.

Вы можете прочитать хорошее описание того, что происходит здесь . Единственная проблема с этим, и я не уверен, повлияет ли это на вас, я не могу понять, почему, когда я создаю экземпляр ABClass, он не может найти подклассы. Возможно, если немного поиграть и почитать документ, это может вас куда-нибудь привести.

Дайте мне знать, как это работает для вас, так как мне искренне любопытен правильный ответ.

1 голос
/ 13 ноября 2009

Я понимаю, что это некрасивое решение, но я делал это раньше:

class ABClass(models.Model):
   #common attributes
   is_ABClass = True
   class Meta:
     abstract = True

class ConcreteClass1(ABClass):
   #implementation specific attributes

class ConcreteClass2(ABClass):
   #implementation specific attributes

class ModifiedConcreteClass1(ConcreteClass1):
   #implementation specific attributes

def get_ABClasses():
   this = modules[__name__]
   return [getattr(this, attr) for attr in dir(this)
           if hasattr(getattr(this, attr), 'is_ABClass') and attr != 'ABClass']
1 голос
/ 13 ноября 2009

Я смог использовать метод __subclass__(), но не в классе abtract=True. Удаление abstract=True добавит еще одну таблицу в вашу базу данных (и, как я думаю, и дополнительные издержки), но тогда вы сможете получить все ее подклассы, как вы описали.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...