Django get child object through parent

Question:

I was trying to access the child object, through the parent object, as I want to perform different operations depending on the type of the object. What I have is:

#models.py
class A(models.Model):
    ...

class B(A):
    field1 = models.CharField(...)
    ...

class C(A):
    field2 = models.IntegerField(...)

I could perform 2 for loops and accomplish what I want:

for obj in B.objects.all():
    if field1 == 'something':
        do some operation

for obj in C.objects.all():
    if field2 == 5:
        do some other operation

But I was thinking, is it not possible to do this with 1 for loop and access the child through the parent?

for obj in A.objects.all():
    if obj.b and obj.b.field1 == 'something':
        do some operation
    elif obj.c and obj.c.field2 == 5:
        do some other operation

I also thought that select_related may do the trick, but it says it works only for ForeignKey.

Moreover, I was hoping to get this done, without using additional apps such as django-model-utils or django-polymorphic, because I feel like there should be a simple query operation to do this.

Asked By: dsax7

||

Answers:

You can get to a child object from parent like you did in the final for loop, but you’d also need to check if the child exists for parent like so:

for obj in A.objects.all():
    try:
        if obj.b.field1 == 'something':
            do some operation
    except ObjectDoesNotExist:
        # obj does not have a child of class B

    try:
        if obj.c.field2 == 5:
            do some other operation
    except ObjectDoesNotExist:
        # obj does not have a child of class C

You can also combine this into a single query using Q objects:

A.objects.filter(Q(b__isnull=False, b__field1='something') | Q(c__isnull=False, c__field2=5))

This would return objects of class A

Answered By: Ozgur Akcali

Perhaps this may be useful for someone. hasattr will return True for the correct child instance, False otherwise. Please note class name is in smallcase for hasattr parameter.

for obj in A.objects.all():
    if obj.hasattr('b'):
       # do something specific to b
    if obj.hasattr('c'):
       # do something specific to c
Answered By: bir singh
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.