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']