Django REST - Incorrect type. Expected pk value, received str (pk is a CharField)

Viewed 2124

I have a ModelSerializer which I'm using to create new posts. It has a field book, of type PrimaryKeyRelatedField(queryset=Book.objects.all(), pk_field=serializers.CharField(max_length=255)).

When I post to this endpoint I get the error:

{
    "book": ["Incorrect type. Expected pk value, received str."] 
}

How can this be, as my Primary Key is a CharField.

The thing that really throws me off is, that I tried circumventing this with a SlugRelatedField, but when I do this, I get a really long and weird error: DataError at /api/content/posts/ value "9780241470466" is out of range for type integer LINE 1: ...020-05-22T20:14:17.615205+00:00'::timestamptz, 1, '978024147..., I don't understand it at all, as I am not setting an integer.

Serializer Code:

class PostCreationSerializer(serializers.ModelSerializer):
    book = serializers.PrimaryKeyRelatedField(queryset=Book.objects.all(), pk_field=serializers.CharField(max_length=255))

    class Meta:
        model = Post
        fields = ['content', 'book', 'page', 'date_posted', 'user', 'id']
        read_only_fields = ['date_posted', 'user', 'id']

Model Code:

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=100)
    pages = models.PositiveSmallIntegerField(null=True, blank=True)

    image = models.URLField(null=True, blank=True)
    date_published = models.CharField(max_length=4, null=True, blank=True)
    publisher = models.CharField(max_length=140, null=True, blank=True)
    isbn13 = models.CharField(max_length=13, primary_key=True)

    objects = AutomaticISBNDBManager

    def __str__(self):
        return self.title

View Code:

class PostListCreate(UseAuthenticatedUserMixin, generics.ListCreateAPIView):
    serializer_class = PostSerializer
    queryset = Post.objects.order_by('-date_posted')

    def get_serializer_class(self):
        if self.request.method == 'POST':
            return PostCreationSerializer
        else:
            return PostSerializer

Edit: The POST I'm sending:

{
  "book": "9780241470466",
  "content": "test",
  "page": "10"
}

Note: the user and date_posted are set automatically.

2 Answers

You can use SlugRelatedField instead of PrimaryKeyRelatedField like that:

book = serializers.SlugRelatedField(
slug_field='isbn13',
queryset=Book.objects.all()
)

From the docs:

SlugRelatedField may be used to represent the target of the relationship using a field on the target.

  • For an alternative method, you can set book reference to your post model in serializer validate method:

1-) Replace PrimaryKeyRelatedField with CharField

2-) Find book object in your validate method and assign it to validated data.

class PostCreationSerializer(serializers.ModelSerializer):
    book = serializers.CharField()

    class Meta:
        model = Post
        fields = ['content', 'book', 'page', 'date_posted', 'user', 'id']
        read_only_fields = ['date_posted', 'user', 'id']

    def validate(self, attrs):
        try:
            attrs['book'] = Book.objects.get(isbn13=attrs['book'])
            return attrs
        except Book.DoesNotExist:
            raise serializers.ValidationError("Book not found")

So, I don't really solved it, but I found out how to revert the problem:

I'm using PostgreSQL as a database backend. These problems seem to be coming from me choosing to use a custom primary key, or because my custom primary key, is a CharField. Luckily, I had made a DB Backup before making these changes, as I wasn't sure if everything would go smoothly and I reverted the code to using id as the primary key and used the SlugRelatedField to get the book.

So, the solution would be: Postgres doesn't like CharField as primary keys?

Related