简体   繁体   中英

Django Rest Framework not working with simple nested serializer

I was converting my existing app to api based. The structure of my file is as follow:

models.py

class BookDetail(models.Model):
    title= models.CharField(max_length=10, default='title')
    author= models.CharField(max_length=10)
    series= models.CharField(max_length=10)
    edition= models.CharField(max_length=10)
    description= models.CharField(max_length=10)
    keywords= models.CharField(max_length=10)
    reading_age= models.CharField(max_length=10)
    genre= models.CharField(max_length=10)
    publishing_rights= models.CharField(max_length=10)

    def __str__(self):
        return self.title


class Addon(models.Model):
    header= models.CharField(max_length=10)
    footer= models.CharField(max_length=10, default='1')
    additional_features = models.ForeignKey(BookDetail, related_name='additional_features',
                                            on_delete=models.CASCADE, null=True)

    def __str__(self):
        return self.header

views.py

class BookDetailsList(APIView):

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

    def post(self, request):
        serializer = BookDetailsSerializer(data=request.data)
        print(serializer.is_valid())
        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)

serializer.py

class AddonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Addon
        fields = ('header', 'footer')


class BookDetailsSerializer(serializers.ModelSerializer):
    additional_features = AddonSerializer(many=True)
    class Meta:
        model = BookDetail
        fields = ('title', 'author','series', 'edition',
                  'description', 'keywords', 'reading_age',
                  'genre', 'publishing_rights','additional_features')
        # fields = '__all__'

    def create(self, validated_data):
        additional_feature = validated_data.pop('additional_features')
        book_detail = BookDetail.objects.create(**validated_data)
        for a in additional_feature:
            Addon.objects.create(additional_features=book_detail, **a)
        # Addon.objects.create(additional_features=book_detail, **additional_feature)
        return book_detail

My input data is in JSON format

{
    "title": "lolwa",
    "author": "asd",
    "series": "string",
    "edition": "a",
    "description": "as",
    "keywords": "sd",
    "reading_age": "aasd",
    "genre": "adasda",
    "publishing_rights": "aadasd",
    "additional_features": [{"header":"head",
                             "footer":"foot"}]
  }

This is working fine. But what I really want is not passing my additional_features as a list which I'm currently doing, I want to pass it like this.

    "additional_features": {"header":"head",
                             "footer":"foot"}

But my code throws error when I'm trying to pass it like this. I made the following changes in my serializer.py

class BookDetailsSerializer(serializers.ModelSerializer):
    additional_features = AddonSerializer(many=False)
    # additional_features = AddonSerializer(many=True)
    class Meta:
        model = BookDetail
        fields = ('title', 'author','series', 'edition',
                  'description', 'keywords', 'reading_age',
                  'genre', 'publishing_rights','additional_features')
        # fields = '__all__'

    def create(self, validated_data):
        additional_feature = validated_data.pop('additional_features')
        book_detail = BookDetail.objects.create(**validated_data)
        # for a in additional_feature:
        #     Addon.objects.create(additional_features=book_detail, **a)
        Addon.objects.create(additional_features=book_detail, **additional_feature)
        return book_detail

Made many=False and removed the for loop . Since both of them, **a and **additional_feature are

OrderedDict([('header', 'head'), ('footer', 'foot')])

I don't see a reason why it is failing.

This is the stack trace of the error

Internal Server Error: /bookdetails/
Traceback (most recent call last):
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/fields.py", line 444, in get_attribute
    return get_attribute(instance, self.source_attrs)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/fields.py", line 103, in get_attribute
    instance = getattr(instance, attr)
AttributeError: 'RelatedManager' object has no attribute 'header'

During handling of the above exception, another exception occurred:

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 21, in post
    return Response(serializer.data, status=status.HTTP_201_CREATED)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/serializers.py", line 531, in data
    ret = super(Serializer, self).data
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/serializers.py", line 262, in data
    self._data = self.to_representation(self.instance)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/serializers.py", line 500, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/serializers.py", line 487, in to_representation
    attribute = field.get_attribute(instance)
  File "/Users/argo/Django/pagination-backend/env/lib/python3.5/site-packages/rest_framework/fields.py", line 463, in get_attribute
    raise type(exc)(msg)
AttributeError: Got AttributeError when attempting to get a value for field `header` on serializer `AddonSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `RelatedManager` instance.
Original exception text was: 'RelatedManager' object has no attribute 'header'.

From the structure of your models, a BookDetail object can have more than one Addon instances, since, Addon model have a foreign key to BookDetail.

If you want to have more than one Addons for a particular BookDetail, you cant update the Addon instance without using a list.

But, if its not the case, then I would recommend using a OneToOne relation instead of Foreign Key. Actually, its pretty ugly using a OneToOne field, rather you can just add the fields into your parent model(BookDetail), which can be defaulted to null, if there is None.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM