0

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'.
3
  • So... the additional_features is a ForeignKey to your BookDetail. So why do you try to add header and footer? Doesn't seem to exist anything in your BookDetail model that match the thing you're trying to add. Commented May 4, 2017 at 3:50
  • Could you be little more specific? I'm not able to understand your question. I'm not sure whether it should be foriegn key or onetooneRel. I'm a newbie at django Commented May 4, 2017 at 3:54
  • It seems like you should have used OneToOne instead of ForeignKey Commented May 4, 2017 at 6:03

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Well I have just started to convert the whole app into resful. I can't put the fields into parent since , there are multiple other fields(not shown in this example) which needs segregation. What are the problems of using OnetoOne field?
I only meant, in this particular case, using another table with one to one relation is completely unnecessary.. There are many cases where using one to one relation can be very useful.
Converting the whole structure into onetoone did the trick. Thanks Man
Always a pleasure buddy!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.