Chuck
Chuck

Reputation: 391

Retrieving a foreign key value in Django Rest Framework

I am using Django Rest Framework to create a REST Api. My serializers:

class ItemSerializer(serializers.ModelSerializer): #inherit from this class
    owner = serializers.ReadOnlyField(source='user.id')

    class Meta:
        model = Item
        fields = ('id','name', 'owner')


class UserSerializer(serializers.ModelSerializer):
    items = serializers.PrimaryKeyRelatedField(many=True, queryset=Item.objects.all())

    class Meta:
        model = User
        fields = ('id', 'username', 'items')

The only model:

class Item(models.Model):
    name = models.CharField(max_length=10)
    id = models.AutoField(primary_key=True)
    owner = models.ForeignKey('auth.User', related_name='items', on_delete=models.CASCADE)

    def __str__(self):
        return self.name

Now if I GET the users:

HTTP 200 OK Allow: GET, HEAD, OPTIONS Content-Type: application/json Vary: Accept [ { "id": 1, "username": "chuck", "items": [ 1, 2 ] } ]

I receive all the items that this user has (by the way I could I get the name instead of the primary keys of the items ?).

How would I go if I wanted to reverse that process and get all the owners of the objects ? Here's the response I have so far (the owners are missing) :

HTTP 200 OK Allow: GET, POST, HEAD, OPTIONS Content-Type: application/json Vary: Accept [ { "id": 1, "name": "Vest" }, { "id": 2, "name": "Pants" } ]`

I have tried to add owners = serializers.ReadOnlyField(source='owner') as you can see in the code (commented parts), but I have the following error :

Object of type 'User' is not JSON serializable

Thank you for your help.

Upvotes: 1

Views: 1774

Answers (2)

Borut
Borut

Reputation: 3364

If you want to receive name of the item instead of primary key, you could just change items to StringRelatedField and then __str__ from your Item will be used.

items = serializers.StringRelatedField(many=True)

But even better solution would be Nested relationship. You simply change items to serializer defining items, eg:

items = ItemSerializer(many=True, read_only=True)

Another solution would be that you remove owner and define depth. This way all information about owners (users) will be serialized:

class ItemSerializer(serializers.ModelSerializer):

    class Meta:
        model = Item
        depth = 1
        fields = ('id', 'name', 'owner')

and for items

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        depth = 1
        fields = ('id', 'username', 'items')

Now for the reverse, list of items with owners, it's not possible with ModelSerializer. You probably have to manually do that. Here is an example, where you don't need a serializer.

class ItemOwnerViewSet(viewsets.ViewSet):

    def list(self, request):
        data = []
        queryset = Item.objects.values('name', 'owner__username')
        for item in queryset:
            temp = dict()
            t = next((i for i in data if i['name'] == item['name']), None)
            if t:
                t['owners'].append(item['owner__username'])
            else:
                temp['name'] = item['name']
                temp['owners'] = [item['owner__username']]
                data.append(temp)

        return Response(data)

Serializer for this viewset would probably be something along these lines:

class ItemOwnerSerializer(serializers.Serializer):

    name = serializers.CharField()
    owners = serializers.ListField(child=serializers.CharField())

I haven't tested all this, but I guess it should work. I hope it helps.

Upvotes: 3

Jose Cherian
Jose Cherian

Reputation: 7757

You are almost there. In your ItemSerializer, Try

serializers.ReadOnlyField(source='owner.id')

or

owner = UserSerializer()

If you want to see all user information

Upvotes: 4

Related Questions