How to access child classes of an object in django without knowing the name of the child class?

In Django, when you have a parent class and several child classes that inherit it, you usually access the child through parentclass.childclass1_set or parentclass.childclass2_set, but what if I don't know the name of a particular child class I want?

Is there a way to get related objects in the direction of parent-> child without knowing the name of the child class?

+74
python django many-to-many
May 30 '09 at 4:52 a.m.
source share
8 answers

( Update : for Django 1.2 and later, which can follow the select_related query through the OneToOneField backward relationship (and therefore down the inheritance hierarchy), there is a more advanced technical feature that does not require the added real_type field in the parent model. It is available as InheritanceManager into the django-model-utils project.)

The usual way to do this is to add a ForeignKey in the ContentType to the parent model, which stores the content type of the corresponding sheet class. Without this, you may need to perform quite a few queries on child tables to find an instance, depending on how large your inheritance tree is. Here's how I did it in one project:

 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 

It is implemented as an abstract base class to make it reusable; you can also put these methods and FK directly into the parent class in your specific inheritance hierarchy.

This solution will not work if you cannot change the parent model. In this case, you almost completely check all subclasses manually.

+78
May 30 '09 at 15:47
source share

In Python, given a class ("new style"), you can get its (direct) subclasses using X.__subclasses__() , which returns a list of class objects. (If you need “subsequent descendants”, you will also have to call __subclasses__ for each of the direct subclasses, etc. Etc. - if you need help on how to do this in Python, just ask!).

Once you have somehow determined the class of interest (possibly all of them, if you want instances of all the child subclasses, etc.), getattr(parentclass,'%s_set' % childclass.__name__) should help (if the child name class 'foo' , it's just for example access to parentclass.foo_set - no more, no less). Again, if you need clarification or examples, please ask!

+21
May 30 '09 at 6:02 a.m.
source share

Carl's solution is a good one, here is one way to do it manually if there are several related child classes:

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

It uses a function from _meta, which is not guaranteed to be stable as django develops, but it does the trick and can be used on the fly if necessary.

+5
May 30, '09 at 22:01
source share

It turns out that I really needed this:

Inheriting a model with a content type and manager administering inheritance

This worked great for me. Thanks to everyone else. I learned a lot just by reading your answers!

+5
Jun 09 '09 at 22:20
source share

Here is my solution, again it uses _meta , so stability is not guaranteed.

 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 
+2
Sep 26 '12 at 18:32
source share

You can use django-polymorphic for this.

Allows you to automatically translate derived classes back to their actual type. It also provides Django admin support, more efficient SQL query processing, and proxy support, inline and supported forms.

The basic principle seems to be invented many times (including Wagtail .specific or the examples described in this post). However, more effort is required to ensure that this does not lead to an N-query problem or works well with the administrator, form / embedded applications, or third-party applications.

+2
May 20 '13 at 21:43
source share

You can achieve this by looking for all the parent fields that are an instance of django.db.models.fields.related.RelatedManager. From your example, it seems that the child classes you are talking about are not subclasses. Right?

0
May 30 '09 at 5:23
source share

An alternative approach using proxies can be found in this blog post . Like other solutions, it has its own advantages and obligations, which are very well placed at the end of the message.

0
Sep 14
source share



All Articles