Django Combine Many Unrelated Models on Related Foreign Key

Viewed 132

I have 3 models T, E, Q such that:

class E(Model):
    name_e = CharField()

class T(Model):
    name_t = CharField()
    e = ForeignKey(E)

class Q(Model):
    name_q = CharField()
    e = ForeignKey(E)

I also have Serializers for each:

class ESerializer(ModelSerializer):
    class Meta:
        model = E
        fields = '__all__'

class TSerializer(ModelSerializer):
    e = ESerializer()
    class Meta:
        model = T
        fields = '__all__'

class QSerializer(ModelSerializer):
    e = ESerializer()
    class Meta:
        model = Q
        fields = '__all__'

For one E row there can be many T and Q rows. I can make a query such that

t = T.objects.filter(e__id=1).all()
serialized_t = TSerializer(t, many=True).data

And the output of serialized_t will look something like:

[
 {id:7, name_t:'test_1', e:{id:1, name_e:'ename_1'}},
 {id:9, name_t:'test_2', e:{id:1, name_e:'ename_1'}}
]

My question is how could I combine it so that I can include Q in the query above to create an output for the T query which would also include all Q's where e_id=1 and hence it would look something like this:

[
 {id:7, name_t:'test_1', e:{id:1, name_e:'ename_1'}, q:[{id:4, name_q:'qname_1'}, {id:5, name_q:'qname_2'}]},
 {id:9, name_t:'test_2', e:{id:1, name_e:'ename_1'}, q:[{id:4, name_q:'qname_1'}, {id:5, name_q:'qname_2'}]}
]

I don't mind if this can be done in-query itself, or if it involves some appending to the SerializedT object, as long as I have a serialized result at the end of it which matches my needs that I can send as a JSON object as an API response.

1 Answers

This can be done by writing T serializer like this:

class TSerializer(ModelSerializer):
    e = ESerializer()
    q = QInTSerializer(source='e.q_set', many=True)

    class Meta:
        model = T
        fields = '__all__'

This will build a q field that represents a list of Q objects. Specifying source='e.q_set' is for telling the field where to look for those list of Qs from the T instance being serialized (in this case, all Q's related to T's E).

But you also have to write another Q serializer to not display the E within Q (only id and name_q as required):

class QInTSerializer(ModelSerializer):
    class Meta:
        model = Q
        fields = ('id', 'name_q')

You can also save on DB hits if you plan to serialize multiple T objects by selecting the related E object, and prefetching the related Q objects from E:

t = T.objects.select_related('e').prefetch_related('e__q_set')
Related