clusterBuddy
clusterBuddy

Reputation: 1554

Python Django REST call returning object number instead of object name

I'm new to Python, and I guess I'm serializing it incorrectly.

This is the REST call result: enter image description here

authors/models.py:

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


# Create your models here.

class Author(models.Model):
    name = models.CharField(max_length=100)
    def __str__(self):
        return self.name


class Book(models.Model):
    auto_increment_id = models.AutoField(primary_key=True)
    name = models.CharField('Book name', max_length=100)
    author = models.ForeignKey(Author, blank=False, null=False, related_name='book_author')
    contents = models.TextField('Contents', blank=False, null=False)
    def __str__(self):
        return self.name

authors/views.py

from authors.models import Book, Author
from authors.serializers import BookSerializer, AuthorSerializer
from django.shortcuts import render
from rest_framework import generics


class ListCreateBooks(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

class ListCreateAuthor(generics.ListCreateAPIView):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

authors/serializers.py

from authors.models import Book, Author
from rest_framework import serializers

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ('name', 'author')

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        book_author = 'name'

I'm new to Django, but I tried many things, in my views.py I've added another class called AuthorSerializer importing from a corresponding class I created in serializers, but then I realized that I have no clue how to add my ListCreateAuthor to:

 url(r'^api-auth/', ListCreateBooks.as_view(), name='list_books')

I've added another parameter with ListCreateAuthor.as_view() that gave me an immediate error (and which also didn't make much sense) Am I going the wrong way here and how can I solve this?

EDIT: @Abdulafaja gave a partial answer, which did solve the read, but now after checking a POST insert - it gives an error for create or update.

in Django rest_framework's documentation (link provided by @Abdulafaja) it give only one example for a nested serializer, and it's a one to many relationship (Album->tracks) but mine is one to one (book->author) so I have no idea how to serilize the nested feature. This API needs to give the frontend all CRUD features.

Thanks.

EDITED serializers.py by Abdullah:

from authors.models import Book, Author
from rest_framework import serializers


class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        book_author = 'name'


class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(many=False)

    class Meta:
        model = Book
        fields = ('name', 'author')

        def create(self, validated_data):
            author, _ = Author.objects.get_or_create(name=validated_data.get('author').get('name'))
            return Book.objects.create(name=validated_data.get('name'), author=author)

        def update(self, instance, validated_data):
            author = validated_data.get('author')
            if author:
                instance.author = Author.objects.get_or_create(name=author.get('name'))
            instance.name = validated_data.get('name', instance.name)
            instance.save()
            return instance

Upvotes: 1

Views: 1289

Answers (2)

Sarosh Khatana
Sarosh Khatana

Reputation: 561

From my understanding you wish to get details of Authors in the api request not just their number??

The way to do that is to set the depth in your model serializer.


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ('name', 'author')
        depth=1

The depth option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation. i.e. 1, 2, 3...

source: http://www.django-rest-framework.org/api-guide/serializers/

Upvotes: 3

Abdulafaja
Abdulafaja

Reputation: 261

For Book model Author is just an id of that field in database, so that's why it's return an integer field. Try to add an Author field in Book serializer

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(read_only=True)

    class Meta:
        model = Book
        fields = ('name', 'author')

This is a NestedSerializer, which is read-only by default. On it's DRF doc site link is mentioned that you

need to create create() and/or update() methods in order to explicitly specify how the child relationships should be saved.

So your BookSerializer needs to look like this

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(read_only=True)

    class Meta:
        model = Book
        fields = ('name', 'author')

    def create(self, validated_data):
        # Create new object

    def update(self, instance, validated_data):
        # Update existing instance

Both create and update methods need to save the object to the database and return it. There are called when you save your BookSerializer class instance. The difference between them is that create is called when you create new instance of Book object

serializer = BookSerializer(data=data)

and update is called if you passed an existing instance of Book object when instantiating the serializer class

serializer = BookSerializer(book, data=data)

More information you can find here

EDIT: If you want to create an instance with NestedSerializer it should not be read only field.

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(many=False)

    class Meta:
        model = Book
        fields = ('name', 'author')

    def create(self, validated_data):
        author, _ = Author.objects.get_or_create(name=validated_data.get('author').get('name'))
        return Book.objects.create(name=validated_data.get('name'), author=author)

    def update(self, instance, validated_data):
        author = validated_data.get('author')
        if author:
            instance.author = Author.objects.get_or_create(name=author.get('name'))
        instance.name = validated_data.get('name', instance.name)
        instance.save()
        return instance

Upvotes: 1

Related Questions