Django Rest Framework not working with simple nested serializer - python

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.

Related

Django nested serializers - Direct assignment to the reverse side of a related set is prohibited

I want to implement my own create function on my serializer but I'm getting the following error:
TypeError: Direct assignment to the reverse side of a related set is prohibited. Use lines.set() instead.
Internal Server Error: /api/cheatsheets/
Traceback (most recent call last):
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/rest_framework/viewsets.py", line 125, in view
return self.dispatch(request, *args, **kwargs)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/rest_framework/mixins.py", line 19, in create
self.perform_create(serializer)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/rest_framework/mixins.py", line 24, in perform_create
serializer.save()
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/rest_framework/serializers.py", line 212, in save
self.instance = self.create(validated_data)
File "/home/christian-sama/Code/cheatsheet-app/api/serializers.py", line 35, in create
Section.objects.create(cheatsheet=cheatsheet, **section)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/django/db/models/query.py", line 669, in create
obj = self.model(**kwargs)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/django/db/models/base.py", line 562, in __init__
_setattr(self, prop, value)
File "/home/christian-sama/Code/cheatsheet-app/venv/lib/python3.10/site-packages/django/db/models/fields/related_descriptors.py", line 595, in __set__
raise TypeError(
TypeError: Direct assignment to the reverse side of a related set is prohibited. Use lines.set() instead.
My models.py
from django.db import models
class Cheatsheet(models.Model):
title = models.CharField(max_length=500)
description = models.CharField(max_length=500)
score = models.IntegerField()
def __str__(self):
return self.title
class Section(models.Model):
title = models.CharField(max_length=500)
description = models.CharField(max_length=500)
cheatsheet = models.ForeignKey(Cheatsheet, on_delete=models.CASCADE, related_name='sections', null=True, blank=True)
def __str__(self):
return self.title
class Line(models.Model):
description = models.CharField(max_length=500)
snippet = models.CharField(max_length=500)
section = models.ForeignKey(Section, on_delete=models.CASCADE, related_name='lines', null=True, blank=True)
def __str__(self):
return self.description
My serializers.py
from rest_framework import serializers
from .models import Cheatsheet, Section, Line
class LineSerializer(serializers.ModelSerializer):
class Meta:
model = Line
fields = "__all__"
class SectionSerializer(serializers.ModelSerializer):
lines = LineSerializer(many=True)
class Meta:
model = Section
fields = "__all__"
def create(self, validated_data):
lines_data = validated_data.pop('lines')
section = Section.objects.create(**validated_data)
for line in lines_data:
Line.objects.create(section=section, **line)
return section
class CheatsheetSerializer(serializers.ModelSerializer):
sections = SectionSerializer(many=True)
class Meta:
model = Cheatsheet
fields = "__all__"
def create(self, validated_data):
sections_data = validated_data.pop('sections')
print(sections_data)
cheatsheet = Cheatsheet.objects.create(**validated_data)
print(cheatsheet)
for section in sections_data:
Section.objects.create(cheatsheet=cheatsheet, **section)
return cheatsheet
My views.py
from api.serializers import CheatsheetSerializer
from .models import Cheatsheet
from rest_framework import viewsets
class CheatsheetViewSet(viewsets.ModelViewSet):
queryset = Cheatsheet.objects.all()
serializer_class = CheatsheetSerializer
And this is the json I'm sending in the request:
{
"title": "Cheatsheet title",
"description": "Cheatsheet description",
"score": 24,
"sections": [
{
"title": "Section title",
"description": "Section description",
"lines": [
{
"description": "Line description",
"snippet": "line snippet"
},
{
"description": "Line description2",
"snippet": "line snippet2"
}
]
}
]
}
I've been following this tutorial but can't figure out why my code is failing. Other answers suggest using set() instead of get(), but I don't think that's my case.
The way you handled line data in SectionSerializer needs to also be done on CheatsheetSerializer, so something like:
class CheatsheetSerializer(serializers.ModelSerializer):
# ...
for section in sections_data:
# pop the lines
lines_data = section.pop('lines')
section = Section.objects.create(cheatsheet=cheatsheet, **section)
# and create them after the section:
for line in lines_data:
Line.objects.create(section=section, **line)

Django Rest - Using a SerializerMethodField and getting an error: 'QuerySet' object has no attribute 'birth_date'

I'm new to Django and APIs in general and I want to create a Django based API using Django Rest Framework.
Here's what I want to do:
Endpoint to age range report:
curl -H 'Content-Type: application/json' localhost:8000/reports/employees/age/
Response:
{
"younger": {
"id": "1",
"name": "Anakin Skywalker",
"email": "skywalker#ssys.com.br",
"department": "Architecture",
"salary": "4000.00",
"birth_date": "01-01-1983"},
"older": {
"id": "2",
"name": "Obi-Wan Kenobi",
"email": "kenobi#ssys.com.br",
"department": "Back-End",
"salary": "3000.00",
"birth_date": "01-01-1977"},
"average": "40.00"
}
Here's my code so far:
employees/model.py:
class Employee(models.Model):
name = models.CharField(max_length=250, default='FirstName LastName')
email = models.EmailField(max_length=250, default='employee#email.com')
departament = models.CharField(max_length=250, default='Full-Stack')
salary = models.DecimalField(max_digits=15, decimal_places=2, default=0)
birth_date = models.DateField()
employees/serializers.py:
class EmployeeSerializer(serializers.ModelSerializer):
age = serializers.SerializerMethodField()
class Meta:
model = Employee
fields = ['id', 'name', 'email', 'departament', 'salary', 'birth_date', 'age']
def get_age(self, obj):
delta = datetime.now().year - obj.birth_date.year
return delta
class EmployeeAgeReportSerializer(serializers.Serializer):
youngest = EmployeeSerializer()
oldest = EmployeeSerializer()
# average = serializers.FloatField()
def create(self, validated_data):
pass
def update(self, instance, validated_data):
pass
employees/views.py:
class EmployeeGenericViewSet(
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet
):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
lookup_field = 'pk'
In employees/services.py I have:
class EmployeeAgeReport:
def __init__(self, youngest, oldest):
self.youngest = youngest
self.oldest = oldest
# self.average = average
def get_employee_age_report():
age_report_dict = Employee.objects.aggregate(
earliest_birth_date=Min("birth_date"),
oldest_birth_date=Max("birth_date")
)
youngest_employee = Employee.objects.filter(
birth_date=age_report_dict.get("earliest_birth_date")
).first()
oldest_employee = Employee.objects.filter(
birth_date=age_report_dict.get("oldest_birth_date")
)
return EmployeeAgeReport(
youngest=youngest_employee,
oldest=oldest_employee
)
But when I try to access http://localhost:8000/reports/employees/age/ I get this error:
Got AttributeError when attempting to get a value for field `birth_date` on serializer `EmployeeSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'birth_date'.
Full Traceback:
Traceback (most recent call last):
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\fields.py", line 457, in get_attribute
return get_attribute(instance, self.source_attrs)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\fields.py", line 97, in get_attribute
instance = getattr(instance, attr)
During handling of the above exception ('QuerySet' object has no attribute 'birth_date'), another exception occurred:
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\django\views\generic\base.py", line 84, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
raise exc
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "D:\Programming\Desafios\Ssys\SSYS_Employee_Manager\applications\reports\views.py", line 21, in get
return Response(serializer.data)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\serializers.py", line 555, in data
ret = super().data
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\serializers.py", line 253, in data
self._data = self.to_representation(self.instance)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\serializers.py", line 522, in to_representation
ret[field.field_name] = field.to_representation(attribute)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\serializers.py", line 509, in to_representation
attribute = field.get_attribute(instance)
File "C:\Users\Laila\.virtualenvs\SSYS_Employee_Manager-Ih3-mAGZ\lib\site-packages\rest_framework\fields.py", line 490, in get_attribute
raise type(exc)(msg)
Exception Type: AttributeError at /reports/employees/age/
Exception Value: Got AttributeError when attempting to get a value for field `birth_date` on serializer `EmployeeSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'birth_date'.
What am I doing wrong and how do I fix it?
Thank you in advance.
Try this,
oldest_employee = Employee.objects.filter( birth_date=age_report_dict.get("oldest_birth_date") ) here add .first() in the end like you did for youngest_employee.
oldest_employee getting a list instead of an object here.

I don't understand that keyerror occurs in my code in django

I am using the django rest framework modelviewset to re-turn the entire post when I request get. But after writing the code, I sent the get request and the following error appears.
Traceback (most recent call last):
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\viewsets.py", line 114, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\views.py", line 505, in dispatch
response = self.handle_exception(exc)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\views.py", line 465, in handle_exception
self.raise_uncaught_exception(exc)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\views.py", line 476, in raise_uncaught_except
ion
raise exc
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\views.py", line 502, in dispatch
response = handler(request, *args, **kwargs)
File "D:\school\대회 및 프로젝트\CoCo\feed\views.py", line 23, in list
'email': serializer.data['author_email'],
KeyError: 'author_email'
I have clearly stated this in the serializer, but I don't understand why a keyerror appears. Can you tell me what the problem is in my code? Here's my code. Thank in advance.
views.py
class CreateReadPostView (ModelViewSet) :
serializer_class = PostSerializer
permission_classes = [IsAuthenticated]
queryset = Post.objects.all()
def perform_create (self, serializer) :
serializer.save(author=self.request.user)
def list (self, request) :
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
data = {
'author': {
'email': serializer.data['author_email'],
'username': serializer.data['author_username'],
'profile': serializer.data['author_profile']
},
'title': serializer.data['title'],
'text': serializer.data['text'],
'images': serializer.data['image'],
'view': serializer.data['view'],
'tag': serializer.data['tag']
}
return Response(data, status=200)
serializers.py
class PostSerializer (serializers.ModelSerializer) :
author_username = serializers.CharField(source='author.username', read_only=True)
author_email = serializers.CharField(source='author.email', read_only=True)
author_profile = serializers.ImageField(source='author.profile', read_only=True, use_url=True)
title = serializers.CharField(allow_null=True)
text = serializers.CharField(allow_null=True)
image = ImageSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = [
'author_username',
'author_email',
'author_profile',
'title',
'text',
'image',
'view'
]
The problem is how you defined your list method. Please use the default list method in the viewset as a reference to define yours.
Specifically, you are initializing the serializer with request.data, when there is no data in the request because it is a GET request. This is only done in the POST/PUT/PATCH requests when you want to serialize the input and create a new object with the data. Instead, you should pass in the queryset that should be serialized for output.There is KeyError because author_email is readonly and you initialized the serializer in write mode so of course, the author-email is not available.
I would advice that you instead keep the inbuilt list method and use a nested representation in the serializers like this:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('username', 'email', 'profile')
class PostSerializer (serializers.ModelSerializer) :
author = AuthorSerializer(read_only=True)
title = serializers.CharField(allow_null=True)
text = serializers.CharField(allow_null=True)
image = ImageSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = [
'author',
'title',
'text',
'image',
'view'
]
Then you can have a simple viewset:
class CreateReadPostView (ModelViewSet) :
serializer_class = PostSerializer
permission_classes = [IsAuthenticated]
queryset = Post.objects.all()
def perform_create (self, serializer) :
serializer.save(author=self.request.user)
To understand more about how the list method is implemented, check the ListMixin. It fetches the queryset, applies filters and then paginates them. These are things you ommitted in your code

'Request' object has no attribute 'pop' in Django Rest Framework?

I have a Post model which has Foreign key with User model. So when I created Post object I need to pass user instance by overriding the create method inside ModelViewSet but it failed.
here is what I have tried:
models.py
class Post(models.Model):
author = models.ForeignKey(User, related_name="posts", on_delete=models.CASCADE)
title = models.CharField(max_length=200)
content = models.TextField()
serializers.py
class PostSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="api:posts-detail")
author = UserSerializer()
class Meta:
model = models.Post
fields = ['url', 'id', 'author', 'title', 'content']
views.py
class PostViewSet(viewsets.ModelViewSet):
serializer_class = serializers.PostSerializer
def get_queryset(self, *args, **kwargs):
user = self.request.user
return models.Post.objects.all().filter(author=user)
permission_classes_by_action = {
'create': [permissions.IsAuthenticated],
'list': [permissions.IsAuthenticated],
# other permissions..
}
def create(self, validated_data):
author = validated_data.pop('author_id')
print(author)
post = models.Post.objects.create(author=author, **validated_data)
return post
def list(self, request, *args, **kwargs):
return super(PostViewSet, self).list(request, *args, **kwargs)
# other methods
def get_permissions(self):
try:
# return permission_classes depending on `action`
return [permission() for permission in self.permission_classes_by_action[self.action]]
except KeyError:
# action is not set return default permission_classes
return [permission() for permission in self.permission_classes]
the whole error I got
Traceback (most recent call last):
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/rest_framework/viewsets.py", line 125, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "/Users/muongkimhong/Developments/django-api/new_api/api/views.py", line 76, in create
author = validated_data.pop('author_id')
File "/Users/muongkimhong/Developments/django-api/env/lib/python3.8/site-packages/rest_framework/request.py", line 418, in __getattr__
return self.__getattribute__(attr)
AttributeError: 'Request' object has no attribute 'pop'
[29/Sep/2020 09:50:36] "POST /api/posts/ HTTP/1.1" 500 103017
I can provide more if you need more information. The error came from the create method inside ModelViewSet.
Thanks
You are mixing up method of ModelViewSet.create with ModelSerializer.create. You need to put it in serializer like this:
class PostSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="api:posts-detail")
author = UserSerializer(read_only=True)
class Meta:
model = models.Post
fields = ['url', 'id', 'author', 'title', 'content']
def create(self, validated_data):
author = self.context['request'].user
post = models.Post.objects.create(author=author, **validated_data)
return post

Django Rest Framework: AttributeError: 'NoneType' object has no attribute '_meta' [for OneToOneField]

I need help with a POST request using Django rest framework.
I have a User model that inherits from AbstractBaseUser which has 2 fields: name and email.
Then I have a DojoMaster model that has a OneToOne relationship with the User model:
class DojoMaster(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
phone = models.BigIntegerField()
country = models.ForeignKey(Country, on_delete=models.CASCADE)
I want to register the dojo master via an API so I created the following serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('name', 'email', 'password')
class DojoMasterCreateSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = DojoMaster
fields = ('user', 'phone', 'country')
def create(self, validated_data):
validated_data['country'] = Country.objects.get(
country=validated_data['country'])
user_data = validated_data.pop('user')
user = UserSerializer.create(UserSerializer(), validated_data=user_data)
subscriber, created = DojoMaster.objects.update_or_create(user=user,
phone = validated_data.pop('phone'),
country = validated_data['country'])
return subscriber
To call on these serializers, I created the following view:
class DojoMasterCreateView(generics.CreateAPIView):
def post(self, request, format='json'):
serializer = DojoMasterCreateSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.create(validated_data=request.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
For the body of the POST request I had the following:
{
"user": {
"name": "XYZ",
"email": "xyz#mail.com",
"password": "8Xa,9Lv&"
},
"phone": 9696510,
"country": "USA"
}
However, when I do that I get the following error:
Traceback (most recent call last):
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
response = get_response(request)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\django\views\decorators\csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\django\views\generic\base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\views.py", line 494, in dispatch
response = self.handle_exception(exc)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\views.py", line 454, in handle_exception
self.raise_uncaught_exception(exc)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\views.py", line 491, in dispatch
response = handler(request, *args, **kwargs)
File "C:\Users\app_dev\development\GOQ\dojomaster_api\views.py", line 29, in post
if serializer.is_valid(raise_exception=ValueError):
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\serializers.py", line 236, in is_valid
self._validated_data = self.run_validation(self.initial_data)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\serializers.py", line 435, in run_validation
value = self.to_internal_value(data)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\serializers.py", line 465, in to_internal_value
validated_value = field.run_validation(primitive_value)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\serializers.py", line 437, in run_validation
self.run_validators(value)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\fields.py", line 533, in run_validators
for validator in self.validators:
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\fields.py", line 392, in validators
self._validators = self.get_validators()
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\serializers.py", line 1460, in get_validators
self.get_unique_for_date_validators()
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\serializers.py", line 1502, in get_unique_for_date_validators
info = model_meta.get_field_info(self.Meta.model)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\utils\model_meta.py", line 39, in get_field_info
forward_relations = _get_forward_relationships(opts)
File "C:\Users\app_dev\Envs\game_of_quarks\lib\site-packages\rest_framework\utils\model_meta.py", line 96, in _get_forward_relationships
not field.remote_field.through._meta.auto_created
AttributeError: 'NoneType' object has no attribute '_meta'
I cannot figure out what the problem is. Help would be much appreciated.
serializer.is_valid method accepts keyword argument raise_exception. And its value should be true or false. By default it is false. If it is true, the exception will be auto triggered.
change your serializer.is_valid(raise_exception=ValueError) to serializer.is_valid().
And also for saving the serializer you should call the save method not the create method
class DojoMasterCreateView(generics.CreateAPIView):
def post(self, request, format='json'):
serializer = DojoMasterCreateSerializer(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)
or in simple form
class DojoMasterCreateView(generics.CreateAPIView):
def post(self, request, format='json'):
serializer = DojoMasterCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
And i have no idea what user = UserSerializer.create(UserSerializer(), validated_data=user_data) means or create method logic is doing. If you want to save the user data, then follow the code given beloe
class DojoMasterCreateSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = DojoMaster
fields = ('user', 'phone', 'country')
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
subscriber, created = DojoMaster.objects.update_or_create(user=user, **validated_data)
return subscriber
And the json should contain the id of the country, like
{
"user": {
"name": "XYZ",
"email": "xyz#mail.com",
"password": "8Xa,9Lv&"
},
"phone": 9696510,
"country": 1
}

Categories

Resources