Can't send a post request using Postman with Django - python

I am using Postman + Django rest framework to create a Post request locally but I keep getting a ParseError. My Get requests are working fine but the post requests are not working as expected.
JSON parse error - Expecting ',' delimiter: line 3 column 2 (char 37)
I am not even getting the 400 error defined in the code and Postman returns a 500 internal server error message.
Here is my photo_list views.py:
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from .models import Photo
from .serializers import PhotoSerializer
#csrf_exempt
def photo_list(request, pk=0):
"""
List all photos, or create a new one.
"""
if request.method == 'GET':
if int(pk) > 0: # convert pk to an int then check if it is greater than zero
photo = Photo.objects.get(pk=pk)
serializer = PhotoSerializer(photo, many=False)
return JsonResponse(serializer.data, safe=False)
photos = Photo.objects.all()
serializer = PhotoSerializer(photos, many=True)
return JsonResponse(serializer.data, safe=False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = PhotoSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)

Since you're using a function based view, you might want to try importing the api_view decorator from DRF and use it to modify photo_list. This is to ensure the view receives an instance of Request and allow it to return a Response. See DRF function based views section here.
from rest_framework.decorators import api_view
...
...
#api_view(['GET', 'POST'])
def photo_list(request)
...
elif request.method == 'POST':
d = request.data
serializer = PhotoSerializer(data=d)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)

your request is being parsed as soon as you call that, and request.DATA is actually returning the dictionary that you were expecting to parse.
json = request.DATA
Please update your code like below
from rest_framework.decorators import api_view
#csrf_exempt
#api_view(['GET', 'POST'])
def photo_list(request, pk=0):
"""
List all photos, or create a new one.
"""
if request.method == 'GET':
# your existing code
elif request.method == 'POST':
serializer = PhotoSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)

The error is raising because you are not accepting the data from the request.POST to serializer.
Edit your view like this,
elif request.method == 'POST':
serializer = PhotoSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)

Edit your view like this,
elif request.method == 'POST':

Related

Getting error in .is_valid() method in DRF

I am tyring to create a User Registration API, but I am getting an error when using a POST request in the following view:
#action(methods=['GET', 'POST'], detail=False, permission_classes=[AllowAny])
def users(self, request):
if request.method == 'GET':
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_201_CREATED)
elif request.method == 'POST':
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
if serializer.available():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The exception returned is the following:
Cannot call `.is_valid()` as no `data=` keyword argument was passed when
instantiating the serializer instance.
You pass an object or a queryset to a serializer when you want to serialize it, so it's fine in your GET method.
However, when you want to create an object using a serializer, you have to pass the data parameter before calling .is_valid(), and only then you can create your new objects withsave(). This is because the serializer needs to validate the data given to him before he does any save.
What you should do is the following:
[...]
elif request.method == 'POST':
serializer = UserSerializer(data=request.data, many=True)
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)

Django web application handling post requests

I am new to Django and am confused on handling post requests to my server. I want to accept post requests in this json format: {“username”:”john#doe.com”,”password”:”pass#word1”} and to reply with these json formated responses:
{“status”:”success”,”message”:”user_authed”} if the user and password combination is found
{“status”:”failure”,”message”:”user_not_found”} if the user and password combination is not found
This is my views.py code
from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from . models import users
from . serializers import userSerializer
class userList(APIView):
def get(self, request):
user = users.objects.all()
serializer = userSerializer(user, many=True)
return Response(serializer.data)
def post(self, request):
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)
I want to know how I can format the post function to accept requests in the format above and how to customize the reply as authenticated or not. Any help would be appreciated with this
What you are trying to accomplish is called authentication.
Please read https://www.django-rest-framework.org/api-guide/authentication/ in advance.
Then edit your question according to what you learn.
Hope to hear from you soon!
#serializers.py
from rest_framework import serializers
UserRequestSerializer(serializers.Serializer)
username = CharField(required=True, max_length=128)
password = CharField(required=True, max_length=128)
#views.py
from .serializers import *
UserCheckView(APIView):
def post(request, *args, **kwargs):
serializer = UserRequestSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
try:
user = User.objects.get(username=serializer.validated_data['username'])
if user.check_password(serializer.validated_data['password']:
return Response(data={“status”:”success”,”message”:”user_authed”},status=status.HTTP_200_OK)
return Response(data={“status”:”failure”,”message”:”password_did_not_match”}, status=status.HTTP_400_BAD_REQUEST)
except User.DoesNotExist:
return Response(data={“status”:”failure”,”message”:”user_not_found”}, status=status.HTTP_404_NOT_FOUND)

"detail": "Method \"GET\" not allowed." Django Rest Framework

I know this question maybe a duplicate, but I have tried many solutions and could not understand any. I have followed this tutorial exactly and yet I get this error on the 'userlist' page. Everything else works just fine. Can somebody point out what the error is ?
class UserList(APIView):
"""
Create a new user. It's called 'UserList' because normally we'd have a get
method here too, for retrieving a list of all User objects.
"""
permission_classes = (permissions.AllowAny,)
http_method_names = ['get', 'head']
def post (self, request, format=None):
self.http_method_names.append("GET")
serializer = UserSerializerWithToken(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)
EDIT:
urls.py
from django.urls import include, path
from classroom.views.classroom import current_user, UserList
from .views import classroom, suppliers, teachers
urlpatterns = [path('', classroom.home, name='home'),
path('current_user/', current_user),
path('users/', UserList.as_view()),
Edit:
Still getting this error,
You need to add GET endpoint url to your urls.py in order to use GET requests. GET url is missing in your urls.py, simply edit your urls.py like:
# urls.py
from django.urls import include, path
from classroom.views.classroom import current_user, UserList
from .views import classroom, suppliers, teachers
urlpatterns = [
path('', classroom.home, name='home'),
path('current_user/', current_user),
path('users/', UserList.as_view()),
path('users/<int:pk>/', UserList.as_view()),
]
And you need to implement get method in yourUserList view such as:
# views.py
class UserList(APIView):
"""
Create a new user. It's called 'UserList' because normally we'd have a get
method here too, for retrieving a list of all User objects.
"""
permission_classes = (permissions.AllowAny,)
http_method_names = ['get', 'head']
def get(self, request, format=None):
users = User.objects.all()
serializer = UserSerializerWithToken(users, many=True)
return Response(serializer.data)
def post(self, request, format=None):
self.http_method_names.append("GET")
serializer = UserSerializerWithToken(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)
Basically the problem is that, there is not functionality defined for GET requests in your view. So either you can add it, like this:
class UserList(APIView):
permission_classes = (permissions.AllowAny,)
http_method_names = ['get', 'head', 'post']
def get(self, request, *args, **kwargs):
serializer = UserSerializerWithToken(User.objects.all(), many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post (self, request, format=None):
self.http_method_names.append("GET")
serializer = UserSerializerWithToken(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 you can subclass UserList View from ListAPIView.
FYI, permission_classes won't work with APIView. You need to use GenericAPIView or any other generic views to have those functionalities.
I had the same issue but I noticed that in my views.py, I had
renderer_class = api_settings.DEFAULT_RENDERER_CLASSES
I corrected it to :
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
and it worked for me.
maybe you are calling 'post' from where ever you are calling, example as 'postman'
but your function is
def get:
so call as 'get' or change your function to
def post:

Multiple nested resources with multiple methods are not displayed in Django Rest Framework Documentation

I wonder why Django REST Framework build-in documentation doesn't display methods for a User. I have only available list, create, read, update for these URLs:
url(r'^users$', views.UserList.as_view()),
url(r'^users/(?P<user_id>\w+)$', views.UserDetail.as_view()),
views.py:
#permission_classes([CustomPermission])
class UserList(GenericAPIView):
"""
get: Return all users.
post: Create a user.
"""
serializer_class = UserSerializer
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request):
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)
#permission_classes([UserPermission])
class UserDetail(GenericAPIView):
"""
get: Return user by ID.
put: Update user by ID.
delete: Delete user by ID.
"""
serializer_class = UserSerializer
def get(self, request, user_id):
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = UserSerializer(user)
return Response(serializer.data)
def put(self, request, user_id):
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = UserSerializer(user, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, user_id):
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
However example shown below is not visible in build-in documentation. I have also Swagger documentation in the project and everything is displayed properly.
urls.py:
url(r'^users/(?P<user_id>[0-9]+)/object$', views.UserObject.as_view()),
views.py:
#permission_classes([UserPermission])
class UserObject(GenericAPIView):
"""
post: Create a user object by his ID.
get: Return a user object by his ID.
put: Update a user object by his ID.
delete: Delete a user object by his ID.
"""
serializer_class = ObjectSerializer
def post(self, request, user_id):
try:
Object.objects.get(user=user_id)
return Response(status=status.HTTP_403_FORBIDDEN)
except Object.DoesNotExist:
serializer = ObjectSerializer(data=request.data)
serializer.fields['user'].required = False
if serializer.is_valid():
serializer.save(user_id=user_id)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def get(self, request, user_id):
try:
object = Object.objects.get(user=user_id)
except Object.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = ObjectSerializer(object)
return Response(serializer.data)
def put(self, request, user_id):
try:
object = Object.objects.get(user=user_id)
except Object.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = ObjectSerializer(object, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, user_id):
try:
object = Object.objects.get(user=user_id)
except Object.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
object.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
There should be visible path users/{user_id}/object Any idea why is not?
Edit 2017-08-19
I've made a PR with the fix which has already been merged. So may be try to get latest version.
Edit 2017-08-13
This is a bug with DRF default documentations, where extra actions with more than one method are not displayed in the docs.
Solution: use swagger
Original
I tried reproducing it, looks like there is a bug in the coreapi from django-rest-framework.
I've tried with the doc generator swagger for rest-framework and it looks fine.
There is a bug in the url if you remove object from users/{user_id}/object it works. If you try for example xsers/{user_id}/ it'll work.
You could change the design approach using a ViewSet.
ViewSet provides actions instead of mapping directly to the method. It's another level of abstraction, usually clearer.
class UserViewSet(viewsets.ViewSet):
"""
retrieve:
Return the given user.
list:
Return a list of all the existing users.
create:
Create a new user instance.
update:
Update a user.
"""
serializer_class = UserSerializer
def list(self, request):
# Here you should put the code for GET user/
pass
def create(self, request):
# Here you should put the code for POST user/
pass
def retrieve(self, request, pk=None):
# Here you should put the code for RETRIEVE user/{pk}
pass
def update(self, request, pk=None):
# Here you should put the code for UPDATE user/{pk}
pass
#detail_route(methods=['get'])
def objects(self, request, pk=None):
if request.method == 'GET'
....
And in your urls
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = router.urls
More info http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing

HttpResponse won't display `post` information when submitting a form

I'm working my way through Django and I'm creating an app that will allow users to use an ID number to sign-in to a system. So I have two views, one for users to log-in, and the other to sign-up. The former view works just fine, I can get it to display the information the user has submitted. However I can't get the second view to display the POST data to the user:
from .forms import NameForm, IdForm
from django.shortcuts import render
from django.http import HttpResponse
def sign_in(request):
if request.method == "POST":
#here will construct the form with the POST data
form = NameForm(request.POST)
#the next part is to check that the information submitted is valid
if form.is_valid():
post = form.save()
post.save()
return HttpResponse(post)
else:
return HttpResponse("Form is invalid")
else:
form = NameForm()
return render(request, 'checkin/base.html', {'form': form})
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
return HttpResponse(post)
else:
return HttpResponse('Form is invalid')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})
Basically I want to make the response to be "thank you, your ID number is: post".
Here is the class for my model:
from __future__ import unicode_literals
from django.db import models
from django import forms
from django.forms import ModelForm
# Create your models here.
class Question(models.Model):
question_text = models.CharField("What is your ID?", max_length=200)
id_text = models.CharField("Enter a new identification
number",max_length=200, null=True)
def __str__(self):
return self.question_text
And here are the form classes for both views:
from django.forms import ModelForm
from .models import Question
#put the form here
class NameForm(ModelForm):
class Meta:
model = Question
fields = ['question_text']
class IdForm(ModelForm):
class Meta:
model = Question
fields = ['id_text']
It's not generally acceptable to display the POST data as the respnose to the user. That's not HTML, merely a dictionary which the average user will not understand. The standard way of using forms in Django (and indeed almost any web framework) is to display the form validation errors to the user so that he may rectify it.
The right way
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
return HttpResponseRedirect('/succes_url')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})
The problem is in line return HttpResponse(post),You are passing a whole form into HttpResponse,but as you mentioned,you just need id_text field of the IdForm.
So the updated code should be :
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
id = post.id_text
return HttpResponse('thank you, your ID number is: '+id)
else:
return HttpResponse('Form is invalid')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})

Categories

Resources