Django: получить все реализации абстрактного базового класса

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

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=None', я хочу отобразить все потомки ABClass (в этом случае ConcreteClass{1,2}, ModifiedConcreteClass1). Я изучил документацию и, похоже, не могу найти способ сделать это, не требующий сохранения списка ConcreteClasses. ABClass.__subclasses__() казалось многообещающим, но он возвращает пустой список, когда я звоню.

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

Спасибо

5 ответов

Решение

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

Как получить доступ к дочерним классам объекта в 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)]

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

Список 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() могу найти их.

Я не смотрел на то, что делает 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, он не может найти подклассы. Возможно, если немного поиграть и почитать документ, это может вас куда-нибудь привести.

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

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

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

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']
Другие вопросы по тегам