1

I am trying to get serialized data for the below model with foreign keys but in the output i am only getting the ids instead of the fields of the model associated with hyperlink.

I have tried getting all the fields of the Skills model using the commented lines in the profile serializer but no luck

models.py

class Profile(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=20, null=True)
    last_name = models.CharField(max_length=20, null=True)

    def __str__(self):
        return 'Profile for user {}'.format(self.user.username)


class Skill(models.Model):
    skill = models.CharField(max_length=20, null=True)
    level = models.CharField(max_length=20, null=True)
    user = models.ForeignKey(Profile, null=True,
                             on_delete=models.PROTECT, related_name='skills')

    def __str__(self):
        return '%s: %s: %s' % (self.user, self.skill, self.level)

serializer.py

class SkillSerializer(serializers.ModelSerializer):

    class Meta:
        model = Skill
        fields = ('user', 'skill', 'level')

class ProfileSerializer(serializers.ModelSerializer):

    class Meta:
        # skills = SkillSerializer(many=True, read_only=True)  # serializes child model's data inside this model's data
        # skills = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='skill-detail')
        # skills = serializers.StringRelatedField(many=True)
        # skills = serializers.HyperlinkedIdentityField( view_name = "skill-list", lookup_field = "user")
        url = HyperlinkedIdentityField( view_name="profile-detail", lookup_field = "id")
        # user = serializers.ReadOnlyField(source='user.username')
        # pdb.set_trace()

        model = Profile
        fields = ['id', 'user', 'url', 'skills']

views.py

class ProfileList(generics.ListCreateAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer

class ProfileDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer

class SkillList(generics.ListCreateAPIView):
    queryset = Skill.objects.all()
    serializer_class = SkillSerializer

class SkillDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Skill.objects.all()
    serializer_class = SkillSerializer

urls.py

path('profiles/', views.ProfileList.as_view(), name='profile-list'),
path('profiles/<int:pk>/', views.ProfileDetail.as_view(), name='profile-detail'),   
path('skills/', views.SkillList.as_view(), name='profile-list'),
path('skills/<int:pk>/', views.SkillDetail.as_view(), name='skill-list'),

Output: http://127.0.0.1:8000/authenticator/profiles/

   [  "id": 6,
        "user": 4,
        "url": "http://127.0.0.1:8000/authenticator/profiles/6/",
        "skills": [
            57,
            55
        ],
   ]

Expected output:

   [  "id": 6,
        "user": 4,
        "url": "http://127.0.0.1:8000/authenticator/profiles/6/",
        "skills": [
              {
                "user": 6,
                "skill": "ABC",
                "level": "Beginner"
              },
              {
                "user": 6,
                "skill": "DEF",
                "level": "Intermediate"
              },
         ]
    ]
user11879807
  • 251
  • 1
  • 3
  • 11

2 Answers2

1

This should normally work with the following in serializer.py:

class ProfileSerializer(serializers.ModelSerializer):
    skills = SkillSerializer(many=True, read_only=True)  # serializes child model's data inside this model's data
    url = HyperlinkedIdentityField( view_name="profile-detail", lookup_field = "id")

    class Meta:
        model = Profile
        fields = ['id', 'user', 'url', 'skills']

So basically uncomment the line in which the ProfileSerializer is told to serialize the skills with your SkillsSerializers, such that the entire skill objects are included in the result instead of its IDs.

Sander Vanden Hautte
  • 2,138
  • 3
  • 22
  • 36
  • I tried, the same, infact all of the commmented lines individually, one by one, none worked... I am guessing i am missing something fundamental point here.. – user11879807 Aug 04 '19 at 08:49
  • Hmm. After having looked again at your code: isn't it because your fields to serialize should be declared on the serializer class and not on the inner Meta class in the serializer? Updated my answer. – Sander Vanden Hautte Aug 04 '19 at 11:44
0

You can use the concept of nested serializer for the solution.
Create two serializer ProfileReadSerializer and ProfileWriteSerializer
serializers.py

class SkillSerializer(serializers.ModelSerializer):
    class Meta:
        model = Skill
        fields = ('user', 'skill', 'level')


class ProfileReadSerializer(serializers.ModelSerializer):
    skills = SkillSerializer(many=True, read_only=True)
    url = HyperlinkedIdentityField( view_name="profile-detail", lookup_field = "id")

    class Meta:
        model = Profile
        fields = ('id', 'user', 'url', 'skills')


class ProfileWriteSerializer(serializers.ModelSerializer):
    skills = SkillSerializer(many=True)

    class Meta:
        model = Profile
        fields = ('id', 'user', 'url', 'skills')

In views you simply can use ModelViewSet to make things easy and use get_serializer_class
views.py

from rest_framework.viewsets import ModelViewSet

class ProfileVewSet(ModelViewSet):
    queryset = Profile.objects.all()

    def get_serializer_class(self):
        if self.request.method == 'POST' or self.request.method == 'PUT' or self.request.method == 'PATCH':
            return ProfileWriteSerializer
        else:
            return ProfileReadSerializer

And at last if you are using ModelViewSet you need to change the urls.py
urls.py

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('profile', views.ProfileViewSet, base_name='profile')

urlpatterns = [
    path('', include(router.urls))
]

I think this will solve your problem. Please have a look.

dipesh
  • 763
  • 2
  • 9
  • 27
  • Thank you with some spell corrections in the above code i was able to get the expected output. – user11879807 Aug 04 '19 at 13:47
  • However in the api page for profile under the skills section I am not able to add any as in place of input box, it says "Lists are not currently supported in HTML", could you help me with this... so I can post as well using the same url in the browser... – user11879807 Aug 04 '19 at 13:49
  • Also could you suggest some documentation where I can get a clear understanding of Relations serialization in Django Rest framework..... I tried to go through https://www.django-rest-framework.org but the example code is a lil confusing – user11879807 Aug 04 '19 at 13:51
  • I have edited the solution above and you can check here for Lists are not currently supported in HTML https://stackoverflow.com/questions/42564732/lists-are-not-currently-supported-in-html-input – dipesh Aug 04 '19 at 13:57
  • I do follow the articles on https://medium.com and another helpful link can be http://www.cdrf.co/ If my answer was correct then please do check. – dipesh Aug 04 '19 at 14:01