I can't get a valid response, always get this error
{"required": "This field is required.", "null": "This field may not be null.", "not_a_list": "Expected a list of items but got type \"{input_type}\".", "empty": "This list may not be empty."}
Here is some code (models, serializers, views)
models
class Task(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=500, blank=True, default='')
pub_datetime = models.DateTimeField()
priority = models.CharField(choices=PRIORITY_CHOICES, max_length=1)
status = models.BooleanField(default=False)
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE, blank=True, default=None, null=True)
def __str__(self):
return self.name
serializers
class TaskSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
agenda = serializers.ReadOnlyField(source='agenda.name')
class Meta:
model = Task
fields = ('id', 'name', 'description', 'pub_datetime', 'priority', 'status', 'owner', 'agenda')
views
class TaskListView(generics.ListCreateAPIView):
serializer_class = TaskSerializer
permission_classes = (permissions.IsAuthenticated,)
renderer_classes = [JSONRenderer, TemplateHTMLRenderer]
template_name = 'task_list.html'
def get(self, request, *args, **kwargs):
queryset = Task.objects.filter(owner=self.request.user)
if self.request.accepted_renderer.format == 'html':
return Response({'tasks': queryset})
else:
serializer = TaskSerializer(data=queryset, many=True)
if serializer.is_valid():
return JsonResponse(serializer.data, safe=False)
else:
return JsonResponse(serializer.error_messages)
def post(self, request, *args, **kwargs):
name = request.data['name']
description = request.data['description']
priority = request.data['priority']
new_task = Task.objects.create(name=name, description=description, pub_datetime=datetime.datetime.now(),
priority=priority, status=False, owner=self.request.user)
new_task.save()
return redirect('task-list')
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
I'm not sure, at what place I'm wrong. Can someone help?
just text to avoid errors
The queryset or object instance should be passed to serializer as instance, not data, so:
queryset = Task.objects.filter(owner=self.request.user)
serializer = TaskSerializer(instance=queryset, many=True)
Also, I believe that serializer can't validate instance, so in this case you should be able to just return return JsonResponse(serializer.data)
Related
I am trying to make a POST request with an object for example this is how I send my request :
{
"title": "Haloween",
"body": " This is one of the greatest ones",
"grade_level": {
"id": 2,
"country": "UG"
},
"tags": [{"name": "Jamming"}]
}
So I wanted to post an object :
"grade_level": {
"id": 2,
"country": "UG"
}
and below is my Serializer I use :
class GradeClassSerializer(CountryFieldMixin, serializers.ModelSerializer):
"""GradeClass Serializer."""
class Meta:
model = ClassGrade
fields = ('id', 'grade', 'country', 'color_code', )
class PostSerializer(serializers.ModelSerializer):
"""Post Serializer"""
owner = UserProfile(read_only=True)
tags = TagSerializer(many=True)
comments = CommentSerializer(many=True, read_only=True)
slug = serializers.SlugField(read_only=True)
grade_level = GradeClassSerializer(many=False)
When I send the object grade_level , I cant seem to receive it it only receives the the id :
def create(self, validated_data):
"""Create a blog post in a customized way."""
grade_level = validated_data.pop('grade_level', {})
status = validated_data.pop('status', '')
post = Post.objects.create(**validated_data,
owner=self.context['request'].user)
if grade_level:
grade = ClassGrade.objects.get(id=grade_level['id'])
post.grade_level = grade
post.save()
return post
When I make a request, this is what happens :
KeyError: 'id'
The object comes with only an country without an id.
This is what grade_level = validated_data.pop('grade_level', {}) prints :
OrderedDict([('country', 'UG')])
How can get the id from the object.
NOTE:
id is not flagged as read_only
EDIT :
In the views.py below is the view :
class PostList(generics.ListCreateAPIView):
"""Blog post lists"""
queryset = Post.objects.filter(status=APPROVED)
serializer_class = serializers.PostSerializer
authentication_classes = (JWTAuthentication,)
permission_classes = (PostsProtectOrReadOnly, IsMentorOnly)
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data, context={
'request': request})
if serializer.is_valid():
serializer.save()
return response.Response(serializer.data,
status=status.HTTP_201_CREATED, )
return response.Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
Then models :
class ClassGrade(TimeStampedModel, models.Model):
"""ClassGrade is the class which Identifies the class or grade."""
grade = models.CharField(
_('Name'), max_length=150, null=True, blank=True)
country = CountryField()
color_code = ColorField(format='hexa', default='#33AFFF', null=True)
def __str__(self):
return self.grade
class Post(MainProcess, TimeStampedModel, models.Model):
"""Post model."""
title = models.CharField(_('Title'), max_length=100, blank=False,
null=False)
image = models.ImageField(_('Image'), upload_to='blog_images', null=True,
max_length=900)
body = models.TextField(_('Body'), blank=False)
description = models.CharField(_('Description'), max_length=400,
blank=True, null=True)
By default, DRF treats the id(PrimaryKey) inside ModelSerializer as read-only. So to override this behavior u can try PrimaryKeyRelatedField
class GradeClassSerializer(CountryFieldMixin, serializers.ModelSerializer):
"""GradeClass Serializer."""
id = serializers.PrimaryKeyRelatedField(queryset=ClassGrade.objects.all(),
required=True)
class Meta:
model = ClassGrade
fields = ('id', 'grade', 'country', 'color_code', )
So, by default, DRF will use the model fields in a ModelSerializer if you don’t define a field. Because the Id is an auto-created primary key (Django does this if you don’t explicitly override it) and Django assumes a primary key is read only, the id is omitted from the deserialized request
I created two models that are interlinked with each other.
class Company(models.Model):
company_name = models.CharField(max_length=50, unique=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.company_name
class Order_Placement(models.Model):
company = models.ForeignKey(Company, on_delete=models.SET_NULL, null=True)
product_quantity = models.IntegerField()
product_price = models.FloatField()
product_total_price = models.FloatField(blank=True, null=True)
other_cost = models.FloatField()
cost_of_sale = models.FloatField(blank=True, null=True)
advance_payment = models.FloatField()
remaining_payment = models.FloatField(default=0.0, blank=True)
date = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
self.product_total_price = self.product_price * self.product_quantity
self.cost_of_sale = self.product_total_price + self.other_cost
self.remaining_payment = self.cost_of_sale - self.advance_payment
super(Order_Placement, self).save(*args, *kwargs)
def __str__(self):
return str(self.company) + ' : ' + str(self.date.date())
I am trying to create a view that will help me to create an object or instant of the Order_placemen model
But when I try to post Data it rises an error while saving with post request:
Here I created a serializer for both company and Order_placement
class CompanySerializer(s.ModelSerializer):
class Meta:
model = m.Company
fields = '__all__'
class Order_PlacementSerializer(s.ModelSerializer):
class Meta:
model = m.Order_Placement
fields = '__all__'
def to_representation(self, instance):
response = super().to_representation(instance)
response['company'] = CompanySerializer(instance.company).data
return response
and I created a view for OrderPlacement using Django rest_framework.viewsets.ViewSet
class OrderProccessViewset(viewsets.ViewSet):
def create(self, request):
try:
serializer = s.Order_PlacementSerializer(data=request.data, context={"request": request})
serializer.is_valid(raise_exception=True)
serializer.save()
dict_response = {"error": False, "message": "Order Save Successfully"}
except:
dict_response = {"error": True, "message": "Error During Saving Data"}
return Response(dict_response)
def list(self, request):
orderProccess = m.Order_Placement.objects.all()
serializer = s.Order_PlacementSerializer(orderProccess, many=True, context={"request": request})
response_dict = {"error": False, "message": "All List Data", "data": serializer.data}
return Response(response_dict)
When i print the prin(request.data) in create method its shown like this:
<QueryDict: {'company': ['1'], 'product_quantity': ['500'],
'product_price': ['530'], 'other_cost': ['1000'], 'advance_payment':
['0']}>
You need to fix your save method, because you misspeled kwargs's argument.
You wrote:
super(Order_Placement, self).save(*args, *kwargs)
and it should be:
super(Order_Placement, self).save(*args, **kwargs)
Before asking this question, I have seen the following links but they don't help me at all:
pass extra arguments to serializer
pass request context to serializer from viewset
pass context from one serializer to another
I have an author model that has foreign key to default django user model:
apps/author/models.py
class Author(models.Model):
user = models.OneToOneField(
User,
related_name='author',
on_delete=models.CASCADE,
default="",
)
is_author = models.BooleanField(
default=True,
)
full_name = models.CharField(
max_length=100,
default="",
)
def __str__(self):
return self.user.username
Post model has a foreign key to Author.
apps/posts/models.py
class Post(models.Model):
author = models.ForeignKey(
Author,
related_name="posts",
on_delete=models.CASCADE,
)
title = models.TextField(
null=True,
blank=True,
)
content = models.TextField(
null=True,
blank=True,
)
is_draft = models.BooleanField(
default=True
)
created_at = models.DateTimeField(
auto_now_add=True,
null=True,
)
published_at = models.DateField(
null=True,
blank=True,
default=None,
)
def __str__(self):
return str(self.id) + ", " + self.title
Problem Definition: In order to create a new post, I am getting the current user from self.request.user in views, and pass it to the PostSerializer. But whenever I want to create a new post using the following request to localhost:8000/posts/ I have got an error:
# I also added JWT authorization header to the postman! and it doesn't have any problem at this level
{
"title": "",
"content": ""
}
error
This is what I have done in apps/posts/views.py:
def get_serializer_context(self):
context = super().get_serializer_context()
context["user"] = self.request.user
context["author"] = Author.objects.get(user=context["user"])
print(context["author"])
return context
print(context["author"]) works well and prints out the current author. The problem is that I can't get it in serializers.
class PostSerializer(serializers.ModelSerializer):
# author = serializers.SerializerMethodField('get_author')
#
# def get_author(self, obj):
# print('current author', self.context["author"])
# return self.context["author"]
class Meta:
model = Post
fields = '__all__'
extra_fields = ['author']
#def create(self, validated_data):
#print(self.context["author"])
#print(self.context)
PS: The comments are the ways I have tried but the error is still occurred. How can I fix the problem?
Maybe you should add "required=False" in author field of PostSerializer, this will avoid "this field is required" error.
class PostSerializer(serializers.ModelSerializer):
author = serializers.PrimaryKeyRelatedField(required=False)
And what view do you use? If you use GenericAPIView and its subclasses, context will pass to serializer, the default get_serializer_context will pass request by default. if not you should pass context manually.
class PostSerializer(serializers.ModelSerializer):
author = serializers.PrimaryKeyRelatedField(required=False)
def create(self, validated_data):
request = self.context.get('request')
author = Author.objects.get(user=request.user)
# ....
class PostView(mixins.CreateModelMixin, viewsets.GenericViewSet):
serializer_class = PostSerializer
def create(self, request, *args, **kwargs):
super().create(request, *args, **kwargs)
UPDATE:
I was wrong, there is a simpler solution: https://stackoverflow.com/a/38167148/7285863
That should be simplest way.
class PostViewSet(viewsets.ModelViewSet):
# ... other implementations
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data={"author": request.user.author.id, **request.data})
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def update(self, request, pk, *args, **kwargs):
try:
post = Post.objects.get(pk=pk)
except Post.DoesNotExist:
raise NotFound("Post doesn't exist.")
if post.author != request.user.author:
raise PermissionDenied('Permission Denied!')
serializer = PostSerializer(post, data={"author": request.user.author.id, **request.data})
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
It's default method except passing author data.
You may need to check user has author relation.
I am adding a following/follower system and I made it a foreign field in my profile model. The thing is, I want to be able to save multiple information to it like the time when a particular user started following and other future info, so I made the many to many field pass through a model, as shown here: https://docs.djangoproject.com/en/3.0/topics/db/models/#extra-fields-on-many-to-many-relationships
Please, I will like to know what I am missing as I am getting
AttributeError at /api/opeyemi-odedeyi-ikx5yh/follow/
'User' object has no attribute 'following'
I have tried this:
models.py
class Profile(models.Model):
SEX= (
('M', 'Male'),
('F', 'Female'),
)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profiles')
date_of_birth = models.DateField(blank=True, verbose_name="DOB", null=True)
bio = models.TextField(max_length=500, blank=True, null=True)
sex = models.CharField(max_length=1, choices=SEX, blank=True, null=True)
following = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, through='Follow', related_name='followed_by')
updated_on = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.fullname
class Follow(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user_following')
profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='profile')
date_followed = models.DateField(auto_now_add=True)
serializers.py
class ProfileDetailedSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(read_only=True, slug_field='slug')
age = serializers.SerializerMethodField(read_only=True)
following = serializers.SlugRelatedField(read_only=True, slug_field='slug', many=True)
following_count = serializers.SerializerMethodField(read_only=True)
user_has_followed = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Profile
fields = "__all__"
read_only_fields = ('pk', 'user')
def get_age(self, instance):
today = date.today()
dob = instance.date_of_birth
if dob==None:
return None
return today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))
def get_following_count(self, instance):
return instance.following.count()
def get_user_has_followed(self, instance):
request = self.context.get("request")
return instance.following.filter(pk=request.user.pk).exists()
views.py
class UserFollowAPIView(APIView):
'''
Can follow(post) and unfollow(delete) the user
'''
serializer_class = ProfileDetailedSerializer
permission_classes = [IsAuthenticated]
def delete(self, request, slug):
followUser = get_object_or_404(User, slug=slug)
user = self.request.user
followUser.following.remove(user)
followUser.save()
serializer_context = {"request": request}
serializer = self.serializer_class(followUser, context=serializer_context)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, slug):
followUser = get_object_or_404(User, slug=slug)
user = self.request.user
followUser.following.add(user)
followUser.save()
serializer_context = {"request": request}
serializer = self.serializer_class(followUser, context=serializer_context)
return Response(serializer.data, status=status.HTTP_200_OK)
urls.py
path("<slug:slug>/follow/", UserFollowAPIView.as_view(), name="user-follow"),
Note:
I used slug because I am not using username in my app, so I had a slug field and made it the lookup field instead of the normal pk
The serializer_context is so the user cannot follow when it returns true (i.e they cannot follow twice)
first, in models.py
class UserComment(models.Model):
user = models.ForeignKey(User)
rate = models.IntegerField()
description = models.CharField(max_length=512)
createTime = models.DateTimeField(auto_now=True)
def __unicode__(self):
return '<UserComment {%s %d}>' % (self.user.username, self.rate)
then, serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', )
class UserCommentSerializer(serializers.ModelSerializer):
user = UserSerializer(required=False)
class Meta:
model = UserComment
views.py
class UserCommentViewSet(viewsets.ModelViewSet):
queryset = UserComment.objects.all()
serializer_class = UserCommentSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
serializer.is_valid()
print serializer.errors
print serializer.data
return super(UserCommentViewSet, self).create(request, *args, **kwargs)
then i post json data
{"user":{"id":"1","username":"watsy"},"rate":"5","description":"hello"}
i think,it will work. and insert it to db, but i get errors.
{"user": [{"username": ["User with this Username already exists."]}]}
>_<, I have no idea.
You need to make few changes to your serializer:
class UserCommentSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = UserComment
depth = 1
Now pass this JSON dict in your request:
{"user":"1", "rate":"5", "description":"hello"}