floflo29
floflo29

Reputation: 2321

Replace PrimaryKeyRelatedField with another field

I have models which consist in a User model and a Present one. A User can have multiple Present but a Present has a unique User:

My models.py is:

from django.db import models
from django.contrib.auth.models import User

class Present(models.Model):
    name = models.CharField(max_length=15)
    price = models.FloatField()
    link = models.CharField(max_length=15)
    isAlreadyBought = models.BooleanField()
    user = models.ForeignKey(User, related_name='presents', on_delete=models.CASCADE)

My serializers.py are:

from django.contrib.auth.models import User, Group
from rest_framework import serializers
from blog.models import Present, Location
from django.contrib.auth.models import User


class UserSerializer(serializers.ModelSerializer):

    presents = serializers.PrimaryKeyRelatedField(many=True, queryset=Present.objects.all(), required=False)

    class Meta:
        model = User
        fields = ('username', 'password', 'email', 'presents')

    def create(self, validated_data):
        user = super().create(validated_data)
        if 'password' in validated_data:
                user.set_password(validated_data['password'])
                user.save()
        return user



class PresentSerializer(serializers.ModelSerializer):

    user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), read_only=False, many=False)

    class Meta:
        model = Present
        fields = ('name', 'link', 'price', 'isAlreadyBought', 'user')

    def create(self, validated_data):
        return Present.objects.create(**validated_data)

Currently, if I want to get the all the presents associate with a given User, I use the primary key (in views.py):

class PresentsOfUser(viewsets.ModelViewSet):
    queryset = Present.objects.all().filter(user=33)
    serializer_class = PresentSerializer

However, I would rather use the username field of the User instead of the primary key.

I have tried using a SlugRelatedField but I am not sure this is the right way to achieve my goal:

class PresentSerializer(serializers.ModelSerializer):

    user = serializers.SlugRelatedField(queryset=User.objects.all(), slug_field='username', read_only=False, many=False)

    class Meta:
        model = Present
        fields = ('name', 'link', 'price', 'isAlreadyBought', 'user')

    def create(self, validated_data):
        return Present.objects.create(**validated_data)

And with this modification, I now use the following View to get the user 'Marcel' whose id is 33:

class PresentsOfUser(viewsets.ModelViewSet):
    queryset = Present.objects.all().filter(user='Marcel')
    serializer_class = PresentSerializer

But in this case, I get:

ValueError: invalid literal for int() with base 10: 'Marcel'

But if I replace user='Marcel' by user=33 (as previously), I get:

[{"name":"Nintendo","link":"fake_link","price":50.8,"isAlreadyBought":true,"user":"Marcel"},{"name":"Gamecube","link":"fake_link","price":50.8,"isAlreadyBought":true,"user":"Marcel"}]

where the user field is now the username and not the user's id.

However, I do not understand why filtering with user='Marcel' fails...

Upvotes: 1

Views: 2194

Answers (2)

floflo29
floflo29

Reputation: 2321

I ended up by overriding the get_querysetmethod while keeping the PrimaryKeyRelatedField in my serializer (with user__username='Marcel'as mishbah suggested):

class PresentsOfUser(viewsets.ModelViewSet):

    serializer_class = PresentSerializer

    def get_queryset(self):
        """
        Optionally restricts the returned purchases to a given user,
        by filtering against a `username` query parameter in the URL.
        """
        queryset = Present.objects.all()
        username = self.kwargs['user']
        if username is not None:
            queryset = queryset.filter(user__username=username)

        if len(queryset) == 0:
            raise Http404

        return queryset

It also works when replacing PrimaryKeyRelatedField by SlugRelatedField:

user = serializers.SlugRelatedField(queryset=User.objects.all(), slug_field='username', read_only=False, many=False)

and adding to_field='username'in the ForeignKey of my Present model:

user = models.ForeignKey(User, related_name='presents', to_field='username', on_delete=models.CASCADE)

Upvotes: 2

mishbah
mishbah

Reputation: 5597

I think your issue is w/ this line:

queryset = Present.objects.all().filter(user='Marcel')

With the assumption Marcel is username with pk => 33

You can't filter using a string, instead something like this:

queryset = Present.objects.all().filter(user__username='Marcel')

Hope that helps.

Upvotes: 1

Related Questions