non_field_errors "No data provided" error on PUT request - python

I am new in Django and python. Now I am trying to do web API with Django and python. My GET, POST, and DELETE requests are working, but PUT gives me error:
{
"non_field_errors": [
"No data provided"
] }
(i used Postman)
Here's my code:
Serializer:
from rest_framework import serializers
from .models import Topic
class TopicSerializer(serializers.ModelSerializer):
title = serializers.CharField(max_length=50)
text = serializers.CharField(max_length=500)
class Meta:
model = Topic
fields = [
'title', 'text'
]
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.description = validated_data.get('text', instance.description)
instance.save()
return instance
Views:
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import Topic
from .serializers import TopicSerializer
class TopicView(APIView):
def get(self, request):
topics = Topic.objects.all()
serializer = TopicSerializer(topics, many=True)
return Response({'topic': serializer.data})
def post(self, request):
topic = request.data
serializer = TopicSerializer(data=topic)
if serializer.is_valid(raise_exception=True):
topic_saved = serializer.save()
return Response({'success': 'Topic {} created successfully'.format(topic_saved.title)})
def put(self, request, pk):
# saved_topic = get_object_or_404(Topic.objects.all())
saved_topic = get_object_or_404(Topic.objects.filter(id=pk))
data = request.data.get('topic')
serializer = TopicSerializer(instance=saved_topic, data=data, partial=True)
if serializer.is_valid(raise_exception=True):
topic_saved = serializer.save()
return Response({
"success": "Topic '{}' updated successfully".format(topic_saved.title)
})
def delete(self, request, pk):
topic = get_object_or_404(Topic.objects.all(), pk=pk)
topic.delete()
return Response({
"message": "Topic with id `{}` has been deleted.".format(pk)
}, status=204)
App URLs:
from django.urls import path
from .views import TopicView
app_name = "rest_test_app"
# app_name will help us do a reverse look-up latter.
urlpatterns = [
path('topics/', TopicView.as_view()),
path('topics/<int:pk>', TopicView.as_view())
]
request body:
{
"title": "pipiska",
"text": "pipiska111"
}
is this because of using wrong methods?
(sorry for terrible english)

Two changes and PUT request will work fine.
One is in the serializers.py file.
Instead of instance.description = validated_data.get('text', instance.description) change it to instance.text = validated_data.get('text', instance.text)
Another one, as mentioned in the comments, in views.py in put definition use data = request.data instead of data = request.data.get('topic')
Then give PUT request from Postman with the following request body:
{
"title": "pipiska",
"text": "pipiska111"
}
it will work fine.

Related

Django POST method for bulk creation using postman

I have POST method in views.py in django to create an entry in database
I can create a single entry using postman, but can not create bulk entries using postman
can anyone help, please?
models.py file
from django.db import models
class Users(models.Model):
user = models.CharField(max_length=50,default='')
function = models.CharField(max_length=50,default='')
department = models.CharField(max_length=50,default='')
logon = models.CharField(max_length=50,default='')
def __str__(self):
return self.user+" - Last_Logon: "+self.logon
class Meta:
ordering = ('id',)
serializers.py file
from rest_framework import serializers
from activities.models import Users
class UsersSerializer(serializers.ModelSerializer):
class Meta:
model = Users
fields = ('id', 'user', 'function', 'department', 'logon')
views.py file with GET,POST,PUT,DELETE, from this file POST method creates only a single entry
from django.shortcuts import render
from django.http.response import JsonResponse
from rest_framework.parsers import JSONParser
from rest_framework import status
from activities.models import Users
from activities.serializers import UsersSerializer
from rest_framework.decorators import api_view
#api_view(['GET', 'POST'])
def users_list(request):
if request.method == 'GET':
users = Users.objects.all()
user = request.GET.get('user', None)
if user is not None:
users = users.filter(user__icontains=user)
users_serializer = UsersSerializer(users, many=True)
return JsonResponse(users_serializer.data, safe=False)
# 'safe=False' for objects serialization
elif request.method == 'POST':
users_data = JSONParser().parse(request)
users_serializer = UsersSerializer(data=users_data)
if users_serializer.is_valid():
users_serializer.save()
return JsonResponse(users_serializer.data, status=status.HTTP_201_CREATED)
return JsonResponse(users_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#api_view(['GET', 'PUT', 'DELETE'])
def users_detail(request, pk):
try:
users = Users.objects.get(pk=pk)
except Users.DoesNotExist:
return JsonResponse({'message': 'The user does not exist'}, status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
users_serializer = UsersSerializer(users)
return JsonResponse(users_serializer.data)
elif request.method == 'PUT':
users_data = JSONParser().parse(request)
users_serializer = UsersSerializer(users, data=users_data)
if users_serializer.is_valid():
users_serializer.save()
return JsonResponse(users_serializer.data)
return JsonResponse(users_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
users.delete()
return JsonResponse({'message': 'User was deleted successfully!'}, status=status.HTTP_204_NO_CONTENT)
urls.py file
from django.urls import re_path
from activities import views
urlpatterns = [
re_path(r'^api/activities$', views.users_list),
re_path(r'^api/activities/(?P<pk>[0-9]+)$', views.users_detail),
]
if I set many=True in user serializer, UsersSerializer(data=users_data, many=True) for POST metho
In postman, for sending a single entry, got this error
for sending multiple entries, got this error
for UsersSerializer(data=users_data, many=True) you must send list of json objects
[
{
"User": "user1",
"function": "t",
"deportment": "t",
"logon": "23...."
},
{
"User": "user1",
"function": "t",
"deportment": "t",
"logon": "23...."
}
]
and for a user UsersSerializer(data=users_data)
you can split serializers with a send a query param into your url and in view function
if request.GET.get('bulk'):
serializer = UsersSerializer(data=users_data, many=True)
else:
serializer = UsersSerializer(data=users_data)

Django Rest Framework Error: JSON parse error - Expecting ':' delimiter: line 4 column 21 (char 103)

I want to save a new user via POST request using the #api_view decorator in DRF but got the following error :
{
"detail": "JSON parse error - Expecting ':' delimiter: line 4 column 21 (char 103)"
}
models.py code:
from django.db import models
from django.contrib.auth.models import User
from PIL import Image
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
image = models.ImageField(default = 'users/default.jpg', upload_to = 'users/profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self, **kwargs):
super().save()
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
views.py code:
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.http import HttpResponse, JsonResponse
from rest_framework.parsers import JSONParser
from .serializers import UserSerializer
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
#api_view(['GET', 'POST'])
def user_list(request):
if request.method == 'GET':
users = User.objects.all().order_by('id')
serializer = UserSerializer(users, many = True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = UserSerializer(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)
serializers.py code:
from rest_framework import serializer
from django.contrib.auth.models import User
from .models import Profile
from django.contrib.auth.hashers import make_password
class UserSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only = True)
password = serializers.CharField(
required = False,
write_only = True,
style = {"input_type": "password", "placeholder": "Password"}
)
class Meta:
model = User
fields = ['id', 'username', 'email', 'password']
def create(self, validated_data):
validated_data['password'] = make_password(validated_data.get('password'))
return super(UserSerializer, self).create(validated_data)
urls.py code:
from django.urls import path, include
from .views import user_list, user_detail
from rest_framework import routers
urlpatterns = [
path('users/', user_list),
path('users/<int:pk>/', user_detail),
]
I am learning DRF from scratch and am following the documentation accordingly. I am not sure why I am getting the HTTP 400 Bad Request error here. It would be great if anyone can help me out.
This is an example of my POST request:
{
"username": "user_1",
"email": "user1m#gmail.com",
"password" : "testing1234"
}
ID is auto-incremented and a password is required (as mentioned in the serializer.py code)
you should post like this:
{
"username": "user_1",
"email": "user1m#gmail.com",
"password" : "testing1234"
}

Add url to API response on serializers.Serializer

I have a django-application with the followin files
models.py
from datetime import datetime
class Comment(object):
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
serializers.py
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
url = serializers.CharField(source='get_absolute_url', read_only=True)
in views.py I now define a a ViewSet to return the serialized results. In this class I define a list of comments
views.py
from rest_framework import viewsets
from .serializers import *
from .models import Comment
from rest_framework.response import Response
class CommentViewSet(viewsets.ViewSet):
lc = [Comment(email='jan#auto.com', content='hallo mike'),
Comment(email='jan#auto.com', content='hallo jan'),
Comment(email='jan#auto.com', content='hallo niklas')]
def list(self, request):
serializer = CommentSerializer(self.lc, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
user = self.lc[int(pk)]
serializer = CommentSerializer(user)
return Response(serializer.data)
When I now call the api (http://127.0.0.1:8000/comments/?format=json) I get the following result
[
{
"email": "jan#auto.com",
"content": "hallo mike",
"created": "2019-08-16T16:53:56.371890Z"
},
{
"email": "jan#auto.com",
"content": "hallo jan",
"created": "2019-08-16T16:53:56.371890Z"
},
{
"email": "jan#auto.com",
"content": "hallo niklas",
"created": "2019-08-16T16:53:56.371890Z"
}
]
In this response I would have hoped to see a url for each dataset. The error ist probably thats for url = serializers.CharField(source='get_absolute_url', read_only=True) the source is undefined in the Comment class. However I have no clue how to achieve this. Any help is appreciated.
You need to define a get_absolute_url method [Django-doc] on your model, like:
# app/models.py
from django.db import models
from django.urls import reverse
class Comment(models.Model):
email = models.EmailField()
content = models.CharField(max_length=128)
created = models.DateTimeField(auto_now_add=True)
def get_absolute_url(self):
return reverse('name-of-view', kwargs={'pk': self.pk})
Here 'name-of-view' is the name of the view you defined, for example in your urls.py, and the kwargs=... is given a dictionary that maps the values for the corresponding URL path parameters.

Django rest framework - hyperlinking non-model serializer fields

I'm struggling to figure out how to implement the Hyperlinked relationships for non-model querysets. I have a viewset:
class GGGViewSet(viewsets.ViewSet):
def list(self, request):
serializer_class = manufacture_serializer(ar)
serializer = serializer_class(
instance = sample.values(), many=True
)
return Response(serializer.data)
def retrieve(self, request, pk=None):
try:
anobject = sample[pk]
except KeyError:
return Response(status=status.HTTP_404_NOT_FOUND)
except ValueError:
return Response(status=status.HTTP_400_BAD_REQUEST)
serializer_class = manufacture_serializer(ar)
serializer = serializer_class(instance=anobject)
return Response(serializer.data)
I am trying to get the value resource at /data/trait/ to be rendered as a link, where:
trait-list
data/trait/
{
"value": 12334,
"another_value": 45672,
}
trait-detail
data/trait/value/
{
"value":12334
}
Attempted:
url = serializers.HyperlinkedIdentityField(view_name='trait-list')
Error: AttributeError at /asvo/data/trait/ 'AObj' object has no attribute 'pk'.
Any ideas on the best way to approach this would be appreciated. :)
You were probably quite close. Based on the information provided, here's something that demonstrates HyperlinkedIdentityField usage without relying on an actual Django model. I had to use my imagination when filling in the details of your architecture.
from rest_framework import routers
from rest_framework import serializers
from rest_framework import status
from rest_framework import viewsets
from rest_framework.response import Response
# This goes in the URL routing file
router = routers.DefaultRouter()
router.register(r'trait', GGGViewSet, base_name='trait')
urlpatterns = router.urls
# The "model"
class Thing(object):
def __init__(self, pk, value, another_value):
self.pk = pk
self.value = value
self.another_value = another_value
# The "queryset"
sample = {
'1': Thing(1, 12334, 45672),
'2': Thing(2, 12335, 45673),
'3': Thing(3, 12336, 45674)
}
# The serializer
class manufacture_serializer(serializers.Serializer):
pk = serializers.HyperlinkedIdentityField(
view_name='trait-detail', read_only=True)
value = serializers.IntegerField()
another_value = serializers.IntegerField()
class Meta:
fields = ['pk', 'value', 'another_value']
# The view
class GGGViewSet(viewsets.ViewSet):
def list(self, request):
serializer = manufacture_serializer(
instance=sample.values(), many=True, context={'request': request})
return Response(serializer.data)
def retrieve(self, request, pk=None):
try:
anobject = sample[pk]
except KeyError:
return Response(status=status.HTTP_404_NOT_FOUND)
except ValueError:
return Response(status=status.HTTP_400_BAD_REQUEST)
serializer = manufacture_serializer(
instance=anobject, context={'request': request})
return Response(serializer.data)
I did not fully understand the second half of the question regarding the data/trait/ and data/trait/value/, but hopefully this is enough to further you along.
Cheers!

name 'snippets' is not defined [Django Rest]

I am following the tutorial of Django REST Framework and when I try to curl http://127.0.0.1:8000/snippets, I get that error. I am new to Python/the Framework and Django, so I don't have a clue where to look at.
The code seems to be pretty fine, since I have double checked on github. Where do you think the error should be?
Snippet/serializers.py
from rest_framework import serializers
from snippets.models import Snippet
class SnippetSerializer(serializers.Serializer):
class Meta:
model = Snippet
fields = ('id', 'title','code','linenos','language','style')
class SnippetSerializer(serializers.Serializer):
pk = serializers.Field() # Note: `Field` is an untyped read-only field.
title = serializers.CharField(required=False,
max_length=100)
code = serializers.CharField(widget=widgets.Textarea,
max_length=100000)
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES,
default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES,
default='friendly')
def restore_object(self, attrs, instance=None):
"""
Create or update a new snippet instance, given a dictionary
of deserialized field values.
Note that if we don't define this method, then deserializing
data will simply return a dictionary of items.
"""
if instance:
# Update existing instance
instance.title = attrs.get('title', instance.title)
instance.code = attrs.get('code', instance.code)
instance.linenos = attrs.get('linenos', instance.linenos)
instance.language = attrs.get('language', instance.language)
instance.style = attrs.get('style', instance.style)
return instance
# Create new instance
return Snippet(**attrs)
snippet/urls.py
from django.conf.urls import patterns, url
urlpatterns = patterns('snippets.views',
url(r'^snippets/$', 'snippet_list'),
url(r'^snippets/(?P<pk>[0-9]+)/$', 'snippet_detail'),
)
snippet/views.py
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
class JSONResponse(HttpResponse):
"""
An HttpResponse that renders its content into JSON.
"""
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
#csrf_exempt
def snippet_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return JSONResponse(serializer.data)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = SnippetSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
return JSONResponse(serializer.errors, status=400)
#csrf_exempt
def snippet_detail(request, pk):
"""
Retrieve, update or delete a code snippet.
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return HttpResponse(status=404)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return JSONResponse(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data)
return JSONResponse(serializer.errors, status=400)
elif request.method == 'DELETE':
snippet.delete()
return HttpResponse(status=204)
In your file urls.py, put quotes around snippets.urls.
Like this:
url(r'^',include('snippets.urls'))
Use serializers.ModelSerializer:
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title','code','linenos','language','style')
This lets DRF know to extract the fields and their types from the specified model class Snippet as parameters for the serializer. Your current version is using only serializers.Serializer. With the base Serializer class you need to define field variables for it to work. like this example:
class SnippetSerializer(serializers.Serializer):
pk = serializers.Field() # Note: `Field` is an untyped read-only field.
title = serializers.CharField(required=False,
max_length=100)
code = serializers.CharField(widget=widgets.Textarea,
max_length=100000)
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES,
default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES,
default='friendly')
Additionally, its probably a better practice for you to import your views from views.py like so:
from django.conf.urls import patterns, url
import views
urlpatterns = patterns('',
url(r'^snippets/$', views.snippet_list, name='snippet-list'),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail, name='snippet-detail'),
)
for me, what solved the problem is the file name.
make sure that the file serializers itself have no spelling issues and his name is the same as in the code.

Categories