Как получить доступ к дочерним классам объекта в django, не зная имени дочернего класса?
в Django, когда у вас есть родительский класс и несколько дочерних классов, которые наследуют от него, вы обычно получаете доступ к ребенку через parentclass.childclass1_set или parentclass.childclass2_set, но что делать, если я не знаю имя конкретного дочернего класса, который я хочу?
есть ли способ получить связанные объекты в направлении parent - >child, не зная имени дочернего класса?
8 ответов:
(обновление: для Django 1.2 и более новых, которые могут следовать запросам select_related через обратные отношения OneToOneField (и, следовательно, вниз по иерархиям наследования), есть лучший метод, который не требует добавления
real_type
поле на родительской модели. Он доступен как InheritanceManager на django-model-utils.)обычный способ сделать это-добавить ForeignKey в ContentType на родительской модели, которая сохраняет тип содержимого соответствующего класса "leaf". Без этого Вам может потребоваться выполнить довольно много запросов к дочерним таблицам, чтобы найти экземпляр, в зависимости от того, насколько велико ваше дерево наследования. Вот как я это сделал в одном проекте:
from django.contrib.contenttypes.models import ContentType from django.db import models class InheritanceCastModel(models.Model): """ An abstract base class that provides a ``real_type`` FK to ContentType. For use in trees of inherited models, to be able to downcast parent instances to their child types. """ real_type = models.ForeignKey(ContentType, editable=False) def save(self, *args, **kwargs): if not self._state.adding: self.real_type = self._get_real_type() super(InheritanceCastModel, self).save(*args, **kwargs) def _get_real_type(self): return ContentType.objects.get_for_model(type(self)) def cast(self): return self.real_type.get_object_for_this_type(pk=self.pk) class Meta: abstract = True
это реализовано как абстрактный базовый класс, чтобы сделать его многоразовым; вы также можете поместить эти методы и FK непосредственно на родительский класс В вашей конкретной иерархии наследования.
это решение не будет работать, если вы не удается изменить родительскую модель. В этом случае вы в значительной степени застряли, проверяя все подклассы вручную.
в Python, учитывая ("новый стиль") класс X, вы можете получить его (прямые) подклассы с
X.__subclasses__()
, который возвращает список объектов класса. (Если вы хотите "дальнейшие потомки", вам также придется позвонить__subclasses__
на каждом из прямых подклассов и т. д. и т. д. - Если вам нужна помощь в том, как это эффективно сделать в Python, просто спросите!).как только вы каким-то образом определили дочерний класс интереса (возможно, все они, если вы хотите экземпляры всех дочерних подклассов и т. д.),
getattr(parentclass,'%s_set' % childclass.__name__)
должно помочь (если имя дочернего класса'foo'
, это так же, как доступparentclass.foo_set
-- не больше, не меньше). Опять же, если вам нужны разъяснения или примеры, пожалуйста, спросите!
решение Карла является хорошим, вот один из способов сделать это вручную, если есть несколько связанных дочерних классов:
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)]
Он использует функцию из _meta, которая не гарантируется стабильной по мере развития django, но она делает трюк и может быть использована на лету, если это необходимо.
оказывается, что мне действительно нужно было это:
наследование модели с типом контента и менеджером с поддержкой наследования
Это отлично сработало для меня. Но спасибо всем остальным. Я многому научился, просто читая ваши ответы!
вот мое решение, снова он использует
_meta
поэтому не гарантируется стабильность.class Animal(models.model): name = models.CharField() number_legs = models.IntegerField() ... def get_child_animal(self): child_animal = None for r in self._meta.get_all_related_objects(): if r.field.name == 'animal_ptr': child_animal = getattr(self, r.get_accessor_name()) if not child_animal: raise Exception("No subclass, you shouldn't create Animals directly") return child_animal class Dog(Animal): ... for a in Animal.objects.all(): a.get_child_animal() # returns the dog (or whatever) instance
можно использовать Джанго-полиморфные для этого.
это позволяет автоматически возвращать производные классы к их фактическому типу. Он также обеспечивает поддержку администратора Django, более эффективную обработку запросов SQL и прокси-модель, поддержку строк и наборов форм.
основной принцип, кажется, был изобретен много раз (в том числе трясогузки
.specific
, или примеры, изложенные в этом посте). Однако требуется больше усилий, чтобы убедиться, что это не приведет к N-запросу выпуск или хорошая интеграция с администратором, наборами форм/линиями или сторонними приложениями.
вы можете добиться этого, глядя на все поля в Родительском, которые являются экземпляром django.децибел.модели.поля.связанный.RelatedManager. Из вашего примера кажется, что дочерние классы, о которых вы говорите, не являются подклассами. Верно?
альтернативный подход с использованием прокси можно найти в этот блог. Как и другие решения, он имеет свои преимущества и обязательства, которые очень хорошо ставить в конце поста.