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
      ] 

enter image description here

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.

Back to Top