Django prefetch_related on ForeignKey relation
I have this database setup:
class Profile(AbstractBaseUser, PermissionsMixin):
email = models.CharField(max_length=30, null=True, unique=True)
date_created = models.DateTimeField(auto_now=True) ...
class SubProfile(models.Model):
profile = models.OneToOneField(Profile, on_delete=models.CASCADE)
display_name = models.CharField(max_length=25) ...
class SubProfilePost(models.Model):
profile = models.ForeignKey(Profile, related_name='sub_profile_postings', on_delete=models.CASCADE)
title = models.CharField(max_length=20, null=False)
looking_for_date = models.DateTimeField(null=False)
How do I now fetch the SubProfiles, and prefetch the related SubProfilePosts?
I have tried doing this:
subprofile_queryset = SubProfile.objects \
select_related('profile') \
prefetch_related(
Prefetch('profile__sub_profile_postings', queryset=SubProfilePost.objects.only('id', 'looking_for_date')))
When I run the queryset through my serializers:
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
The data does not contain SubProfilePost(s). If I directly access the obj.profile.sub_profile_postings.all() I can see the data.
I might be misunderstanding how prefetching works, or whether the data is annotated to the queryset when prefetching etc.
Can someone more experienced enlighten me if there is something wrong with my setup, or am I misunderstanding the whole concept.
Here are my serializers:
class ProfileSerializer(ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
extra_kwargs = {
'password': {'write_only': True, 'required': False},
}
class SubProfilePostSerializer(ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = SubProfilePost
fields = '__all__'
class SubProfileSerializer(ModelSerializer):
sub_profile_postings = SubProfilePostSerializer(many=True, read_only=True)
class Meta:
model = SubProfile
fields = [
'id',
'profile',
'display_name',
'sub_profile_postings', # Prefetched field
]
Implemented with SerializerMethodField.
I tried to express it as Queryset as much as possible, but I couldn't resolve it where multiple values could be returned to the subquery.
class SubProfileSerializer(ModelSerializer):
sub_profile_postings = serializers.SerializerMethodField()
def get_sub_profile_postings(self, obj):
posts = SubProfilePost.objects.filter(profile=obj.profile)
serializer = SubProfilePostSerializer(posts, many=True)
return serializer.data
class Meta:
model = SubProfile
fields = [
'id',
'profile',
'display_name',
'sub_profile_postings', # Prefetched field
]
Try this
class ProfileSerializer(ModelSerializer):
sub_profile_postings = SubProfilePostSerializer(many=True, read_only=True)
class Meta:
model = Profile
fields = '__all__'
extra_kwargs = {
'password': {'write_only': True, 'required': False},
}
class SubProfilePostSerializer(ModelSerializer):
class Meta:
model = SubProfilePost
fields = '__all__'
class SubProfileSerializer(ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = SubProfile
fields = [
'id',
'profile',
'display_name',
'sub_profile_postings', # Prefetched field
]
Should return something like this, just a rough sample.
{
"id": 23
"profile": {
"id":23224,
"sub_profile_postings": [{}, {}]
}
}
or add a getter in the SubProfileSerializer()
to filter all the SubProfilePost
based on profile.