Arghya Saha
Arghya Saha

Reputation: 5723

DRF: ModelSerializer for nested JSON data

I'm trying to convert my existing DJANGO app to API based. I have done the front-end in Angular and working on the backend now. The data from client is going to be in json format as below

  {
    "title": "wue",
    "author": "asd",
    "addons": {
                "header":"head",
                "footer":"foot"
              }
  }

I have written the model.py

class BookDetails(models.Model):
    title= models.CharField(max_length=10)
    author= models.CharField(max_length=10)
    addons= models.ForeignKey(Addons, null=True)
class Addons(models.Model):
    header= models.CharField(max_length=10)
    footer= models.CharField(max_length=10)

serializer.py

class AddonsSerializer(serializers.ModelSerializer):

    class Meta:
        model = Addons
        fields = ('header', 'footer')
        depth = 2


class BookDetailsSerializer(serializers.ModelSerializer):
    addons = AddonsSerializer(many=False, read_only=True)
    class Meta:
        model = BookDetails
        depth = 2
        fields = ('title', 'author','addons')

views.py

class BookDetailsList(APIView):

    def get(self, request):
        stocks = BookDetails.objects.all()
        serializers = BookDetailsSerializer(stocks, many=True)
        return Response(serializers.data)

    def post(self, request):
        serializer = BookDetailsSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

my urls.py is

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^bookdetails/', views.BookDetailsList.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

Now when I'm using postman to send the data then the addons field is not getting populated, it gets null value.

I have tried to write a custom create but I think somewhere I'm messing it up, but not able to figure out where.

def create(self, validated_data):
    tracks_data = validated_data.pop('addons')
    addons = BookDetails.objects.create(**validated_data)
    Addons.objects.create(addons=addons, **tracks_data)
    return addons

it gives me an error that

KeyError at /bookdetails/ 'addons'

I have tried assigning depth=2 but it is not working. what is that I'm doing wrong?

Edit 1 (removed read_only=True) still no luck:

Traceback (most recent call last):
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/views.py", line 483, in dispatch
    response = self.handle_exception(exc)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/views.py", line 443, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/views.py", line 480, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/argo/Django/pagination-backend/backend/publishbook/views.py", line 19, in post
    serializer.save()
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/serializers.py", line 214, in save
    self.instance = self.create(validated_data)
  File "/Users/argo/Django/pagination-backend/backend/publishbook/serializers.py", line 26, in create
    Addons.objects.create(addons=addons, **tracks_data)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/django/db/models/manager.py", line 122, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/django/db/models/query.py", line 399, in create
    obj = self.model(**kwargs)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/django/db/models/base.py", line 443, in __init__
    raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
TypeError: 'addons' is an invalid keyword argument for this function

Upvotes: 1

Views: 2421

Answers (3)

zaidfazil
zaidfazil

Reputation: 9235

I think its your create method you need to rectify. You've got a Bookdetails model with a ForeignKey to Addon model. From my point of view, the code should be something like this, (You should remove read_only=True, Obviously)

def create(self, validated_data):
    tracks_data = validated_data.pop('addons')
    book_details = BookDetails.objects.create(**validated_data)
    addons = Addons.objects.create(**tracks_data)
    book_details.addons = addons
    book_details.save()
    return book_details

Hope you got what were you looking for.

Seriously, you need to re-consider about your naming conventions.

Upvotes: 0

Sathish Kumar VG
Sathish Kumar VG

Reputation: 2172

You are adding book details as addon to addon model itself, try updationg your code like this,

    def create(self, validated_data):
        tracks_data = validated_data.pop('addons')
        addons = Addons.objects.create(**tracks_data)
        book = BookDetails.objects.create(addons=addons, **validated_data)
        return book 

Upvotes: 1

ChidG
ChidG

Reputation: 3223

You have read_only=True specified in your addons field in your BookDetailsSerializer. This prevents the addons part of the json dictionary being passed to your create method. That's why there's a KeyError on your attempt to pop (I guess, although if you pasted your stacktrace rather than just the error it would help).

Other than that, I'm not sure about your create method - perhaps you could reformat it so the variable names are more meaningful. You still have the tracks_data from the DRF example, and you've named the product of your BookDetails create 'addons' - which doesn't really make sense and makes it hard to read. Also, could you specify which serializer the create method is part of, as it's hard to tell just by looking at it.

Note: In general it's conventional to give Django model names singular form, so yours would be BookDetail and Addon.


Update after question edit:

Now you have an error on this line: Addons.objects.create(addons=addons, **tracks_data). The error is telling you that addons is not a valid field on the Addons model. I think it would work just to do Addons.objects.create(**tracks_data).

Upvotes: 0

Related Questions