Jeet Patel
Jeet Patel

Reputation: 1241

How to get foreign key value using Serializer() class DRF

I have models User and Role. User model has foreign key role from model Role as show below.

User Model

class User(model.Model):

    first_name = models.CharField(max_length=255, blank=False)
    role = models.ForeignKey(Role, on_delete=models.CASCADE, blank=False)

Role Model

class Role(models.Model):
    
    role_name = models.CharField(max_length=255, blank=False, unique=True)

I wrote a serializer for model User.

class User(serializers.Serializer):

    id = serializers.IntegerField(read_only=True)
    role = serializers.IntegerField(required=True)

I am using APIView to get the list of all the users. I want get() method of APIVIew to a return list of all the user with role_name instead of role_id like this -

[
    {
        "first_name":"Name",
        "role": "Admin"
    },
    {
        "first_name":"Name",
        "role": "Merchant"
    }
]

views.py

class ListUsers(APIView):

    def get(self, request, formate=None):

        admin_users = User.objects.all().values('first_name', 'role')


        return Response(admin_users)

My ListUsers(APIView) class returns the list like this -

[
    {
        "first_name":"Name",
        "role":1
    },
    {
        "first_name":"Name",
        "role":2
    }
]

I want my APIView to return list of users and each object should have role_name instead of role_id. I don't want to use ModelSerializer. Also, I have an APIView() class which create an object for model User and it uses the same serializer. I want a solution which would not affect create user functionality.

Upvotes: 2

Views: 2612

Answers (3)

theguru42
theguru42

Reputation: 3378

Use the below serializer for getting the output you need.

This serializer can be used for creation too but instead of sending the role_id you have to send the role_name in the role field.

You did not specify what is the structure of your POST data so I am assuming it to be the same as the output.

class User(serializers.Serializer):

    first_name = serializers.CharField(max_length=255, allow_blank=False)
    role = serializers.CharField(max_length=255, allow_blank=False)

    def create(self, validated_data):
        role = get_object_or_404(Role.objects.all(), role_name=validated_data['role'])
        user = User.objects.create(first_name=validated_data['first_name'], role=role)
        return user  

    def update(self, instance, validated_data):
        role = get_object_or_404(Role.objects.all(), role_name=validated_data['role'])
        instance.first_name = validated_data['first_name']
        instance.role = role
        instance.save()
        return instance

    def to_representation(self, instance):
        ret = {
            "first_name": instance.first_name,
            "role": instance.role.role_name,
        } 
        return ret       

Upvotes: 0

Ohad the Lad
Ohad the Lad

Reputation: 1929

First, Please do not use "id" as a field name, it is so wrong!

Answer 1: easy way to do it:

class ListUsers(APIView):

    def get(self, request, formate=None):
        users = Userdrf.objects.all().values('first_name', 'role__role_name')
        return Response(users)

The output

[
{'first_name': 'asd', 'role__role_name': 'admin'},
 {'first_name': 'asd', 'role__role_name': 'not_admin'}
]

Answer 2: Another way is to connect the serializer to the view and modify it a bit with somthing like :

class User(serializers.Serializer):

    id = serializers.IntegerField(read_only=True)
    my_role = serializers.SerializerMethodField()
    class Meta:
        model = User
    fields = (
        'id',
        'my_role'
        )

def get_my_role(self, obj):
    
    return {
        'role': obj.user.role.role_name,
        }

BTW-

Upvotes: 0

Ali Asgari
Ali Asgari

Reputation: 850

First of all you are not using your serializer. In order to use your serializer you have to use ListAPIView instead of APIView and set its serailizer_class to the serializer you have implemented and set its queryset or implement its get_queryset. The list of lists is due to your code nesting the objects within a set:

    return Response({admin_users})

But as for returning role name instead of role id take look at SlugRelatedField which is there specifically for your use case.

Upvotes: 2

Related Questions