MultiValueDictKeyError in Django REST APIView POST request - django

I am trying to write a POST request for a game API and am passing some data from the GET request and some through this POST request. However, I keep getting the following error:
MultiValueDictKeyError 'gameround'
What am I doing wrong here?
def post(self, request, *args, **kwargs):
if not isinstance(request.user, CustomUser):
current_user_id = 1
else:
current_user_id = request.user.pk
gameround = request.GET['gameround']
random_resource = request.GET['resource']
created = datetime.now()
score = 0
origin = ''
name = request.POST['name']
language = request.POST['language']
user_input_tag = Tag.objects.create(name=name, language=language)
tag_serializer = TagSerializer(user_input_tag)
if Tagging.objects.all().filter(tag=user_input_tag).exists():
# if tagging like this exists, save tagging anyway and leave tag unchanged
score += 5
user_input_tagging = Tagging.objects.create(user_id=current_user_id,
gameround=gameround,
resource=random_resource,
tag=user_input_tag,
created=created,
score=score,
origin=origin)
tagging_serializer = TaggingSerializer(user_input_tagging)
return Response({'tag and ': tag_serializer.data}, {'tagging': tagging_serializer.data})
elif not Tagging.objects.all().filter(tag=user_input_tag).exists():
# save tagging otherwise and del tag?
user_input_tagging = Tagging.objects.create(user_id=current_user_id,
gameround=gameround,
resource=random_resource,
tag=user_input_tag,
created=created,
score=score,
origin=origin)
user_input_tagging.save()
tagging_serializer = TaggingSerializer(user_input_tagging)
return Response({'tagging only': tagging_serializer.data})

You don't have key gameround in your GET. You can get gameround data as
gameround = request.GET.get('gameround')
If default value is not given then it defaults to None.
Or you can set default value as
gameround = request.GET.get('gameround', '')

Related

why do i keep getting this error( Field 'id' expected a number but got ''. ) each time users like a post from their own timeline?

Here is the users account view that i want users to be able to like their post from, it has really been a challenge to get my head around this. would appreciate some help.
def account_view(request, *args, **kwargs):
"""
- Logic here is kind of tricky
is_self
is_friend
-1: NO_REQUEST_SENT
0: THEM_SENT_TO_YOU
1: YOU_SENT_TO_THEM
"""
context = {}
user_id = kwargs.get("user_id")
try:
account = User.objects.get(pk=user_id)
except:
return HttpResponse("Something went wrong.")
if account:
context['id'] = account.id
context['username'] = account.username
context['bio'] = account.bio
context['get_full_name'] = account.get_full_name
context['email'] = account.email
context['profile_pic'] = account.profile_pic.url
context['cover_image'] = account.cover_image.url
context['city'] = account.city
context['country'] = account.country
context['gender'] = account.gender
context['hide_email'] = account.hide_email
post_id = request.GET.get("likeId", "")
try:
post_id = Post.objects.get(pk=post_id)
except Post.DoesNotExist:
liked= False
like = Like.objects.filter(user=user, post=post)
try:
post_list = Post.objects.filter(user_name=account)
except Post.DoesNotExist:
post_list = Post(user_name=account)
save.post_list()
posts = post_list.all()
context['posts'] = posts
Here is my like view, how best do can i accomplish users and friends being able to like their post from their own profile or timeline ?
def like(request):
post_id = request.GET.get("likeId", "")
user = request.user
post = Post.objects.get(pk=post_id)
liked= False
like = Like.objects.filter(user=user, post=post)
if like:
like.delete()
else:
liked = True
Like.objects.create(user=user, post=post)
resp = {
'liked':liked
}
response = json.dumps(resp)
return HttpResponse(response, content_type = "application/json")
The error occurs either because likeId is sent as an empty string http://url_path?likeId=, or (most probably) because it is not present in the request.GET at all.
You've used request.GET.get("likeId", "") so the default value is "".
Next, the code runs post_id = Post.objects.get(pk=post_id). It's forbidden to pass a string to a pk filter, hence the error. So, verify the likeId is indeed send in the request and it's an actual Post.id (check for possible typos). Maybe you've meant to use request.POST instead?
Generally, it's a bad idea to use dict.get() where you clearly DO need an value. Also, there should be some validation in place to ensure the likeId is a valid integer. I would recommend validating input parameters with a form, but a manual approach is also possible.
try:
post_id = int(request.GET["likeId")
except (KeyError, ValueError):
return HttpResponse('Provide a valid likeId integer', status_code=404)

REQUEST certain Elements in Django POST API from User

I want to Request a dictionary like
{
"username": "a",
"password": "b",
"year": "2019-20",
"start": 1,
"end": 2,
"reference_id": "two"
}
from a user so that a user can hit the API and get the desired result.
My view looks like
def post(self,request, *args, **kwargs):
# self.http_method_names.append("post")
user = request.POST.get("user")
pasd = request.POST.get("pasd")
year = request.POST.get("year")
start = request.POST.get("start")
end = request.POST.get("end")
reference_id = request.POST.get("reference_id")
#Passing the parameters to another function to get the result.
status = main_function(user=user,pasd=pasd,year=year,start=start,end=end,reference_id=reference_id)
return Response(status)
Now the problem when I'm posting something in Django like
I'm getting None in every field. Ideally, I should get the values passed in the Dictionary.
Can someone please help me here.
In django rest framework, you should use request.data instead of request.POST, as it requires parser to receive JSON data. More info can be found in drf docs.
Change your code to:
def post(self,request, *args, **kwargs):
# self.http_method_names.append("post")
user = request.data.get("username") # <-- this used incorrect key
pasd = request.data.get("password") # <-- this used incorrect key
year = request.data.get("year")
start = request.data.get("start")
end = request.data.get("end")
reference_id = request.data.get("reference_id")
#Passing the parameters to another function to get the result.
status = main_function(user=user,pasd=pasd,year=year,start=start,end=end,reference_id=reference_id)
return Response(status)
I Got The Answer. SO if You Want to take input from a user then You should write this in your views.py file.
But First you have to tell Django Which Fields input you want. So you have to create a serializer.py file in the Django APP.
from rest_framework import serializers
class userdetailSerializer(serializers.Serializer):
username = serializers.CharField(max_length=20)
password = serializers.CharField(max_length=20)
year = serializers.CharField(max_length=8)
start = serializers.IntegerField()
end = serializers.IntegerField()
reference_id = serializers.CharField(max_length=50)
I need all the Above values from the User that's why i have added all. If you want Only certain values you can delete it from your serializer file.
After this goto your Views.py file and get the response entered by user using below code.
Serializer will autovalidate the input and if the input is not correct it will give a dictionary of error(In else Statement)
def post(self,request, *args, **kwargs):
# self.http_method_names.append("post")
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
username = serializer.validated_data.get('username')
password = serializer.validated_data.get('password')
year = serializer.validated_data.get('year')
start = serializer.validated_data.get('start')
end = serializer.validated_data.get('end')
reference_id = serializer.validated_data.get('reference_id')
response_status = main_function(user=username, pasd=password,year=year,start=start,end=end,reference_id=reference_id)
return Response("The File Upload Response Will Come HEre",response_status)
# return Response({'name':username, 'pass':password,'year':year,'start':start,'end':end,'reference_id':reference_id})
else:
return Response(serializer.errors)
The POST API will LOOk Like this.
If you guys are facing an error here then Plese comment.
Upvote if it helps.

How to pass data saved from a POST method to the GET method using REST API Django (without a model)?

I have created an API that allows me to upload an image using the POST method in POSTMAN. After submission, I want to display that image name after making a GET request. I am not using any model and I don't intend to grab the image from the directory it is stored in; since I will be uploading images in a server later.
I have looked at multiple sources. A few examples are this, and this.
This is my current code so far but not successful:
views.py:
class API(APIView):
parser_classes = (MultiPartParser,)
def get(self, request, *args, **kwargs):
name = self.request.GET.get('image')
if name:
return Response({"img_name": name}, status=200)
return Response({"img_name" : None}, status = 400)
def post(self, request):
file = self.request.data
img_file = file['image'] #store the image data in this variable
if img_file:
uploaded_file = img_file
img = [{"image_name": uploaded_file}]
serializer = ImgSerializer(img, many = True).data
return Response(serializer, status = 200)
else:
return Response("Please upload", status = 400)
serializers.py:
from rest_framework import serializers
class ImgSerializer(serializers.Serializer):
image_name = serializers.CharField()
My expected result within GET request should be like this:
{'image_name' : 'image_name_from_POST_Request'}
But I am getting this result instead:
None
How can I pass data from the POST request to the GET request using Django's rest framework? Is there an efficient way to deploy this requirement without using a model?
I figured it out. I just created a JSON file in the POST method and stored the necessary data in it. Finally, in order to view the data within the GET method, I opened the file and returned it as a Response.
views.py:
class API(APIView):
parser_classes = (MultiPartParser,)
def get(self, request):
with open('data.txt') as json_file:
data = json.load(json_file)
if data:
return Response(data, status=200)
return Response({"name" : None}, status = 400)
def post(self, request):
posted_file = self.request.data
img_file = posted_file['image']
if img_file:
uploaded_file = img_file
data = [{"image_name": uploaded_file}]
json_data = {"image_name": uploaded_file}
data = {}
data['key'] = []
data['key'].append(json_data)
with open('data.txt', 'w') as outfile:
json.dump(image, outfile)
serializer = ImgSerializer(image, many = True).data
return Response(serializer, status = 200)
else:
return Response(serializer.errors, status = 400)

setUpClass() is being called multiple times in DRF

Perhaps I am missing the logic but I want to add two unique records to a model then run my tests on an API. But my API complains .get() has returned 2 records, raising the error. I notice the error happens on my .post() check, while the get methods work well. Is it a bug?
only enough if I change self.client.post with .get(), it doesn't complain.
I have tried setUpTestData() since my database supports Transactions to no avail. Essentially, I want to try the get method and the post methods. All the get operations pass but on the post test, it fails.
class ResetPwdTest(APITestCase):
""" Test module for resetting forgotten password """
#classmethod
def setUpClass(cls):
super().setUpClass()
cls.valid_payload = '1234567890ABCDEF'
cls.expired_payload = '1234567ACDES'
cls.invalid_payload = 'invalid0000'
user_info = {'first_name': 'John', 'email': 'kmsium#gmail.com','password' : 'kigali11', 'status': 1}
user = User.objects.create_user(**user_info)
PasswordReset.objects.all().delete() # delete any possible we have but having no effect so far
# create valid password token
password2 = PasswordReset(profile_id=user.id, unique_code=cls.valid_payload)
password2.save()
# create an expired passwod token
password = PasswordReset(profile_id=user.id, unique_code=cls.expired_payload)
password.save()
password.createdon = '2018-01-12 21:11:38.997207'
password.save()
def test_valid_token(self):
"""
GET Ensure a valid forgotten password reset token returns 200
WORKING
"""
response = self.client.get(reverse('new-pwd', kwargs= {'token' : self.valid_payload}))
self.assertEqual(response.status_code, 200)
def test_expired_token(self):
"""
GET Ensure an expired token to reset a forgotten password returns 400
WORKING
"""
response = self.client.get(reverse('new-pwd', kwargs= {'token' : self.expired_payload}))
self.assertEqual(response.status_code, 400)
def test_invalid_token(self):
"""
GET Ensure an invali token to reset a forgotten password returns 400
WORKING
"""
response = self.client.get(reverse('new-pwd', kwargs= {'token' : self.invalid_payload}))
self.assertEqual(response.status_code, 400)
def test_valid_token_change_pass(self):
"""
POST Ensure a valid forgotten password with accepted passwords pass reset token returns 200
FAILING BECAUSE TOKEN is not unique
"""
passwords = {'pwd': 'letmein11', 'pwd_confirm': 'letmein11'}
response = self.client.post(reverse('new-pwd', kwargs= {'token' : self.valid_payload}), passwords, format='json')
self.assertEqual(response.status_code, 200)
the View:
class ResetPwd(APIView):
permission_classes = []
def get(self,request,token,format=None):
status = 400
reply = {}
check_token = PasswordReset.objects.values('createdon','profile_id',).get(unique_code=token)
# exists but is it valid or not? It cant be
createdon = check_token["createdon"]
if createdon > timezone.now() - timedelta(hours=USER_VALUES['PWD_RESET_LIFESPAN']):
status = 200
reply['detail'] = True
else:
reply['detail'] = _('errorPwdResetLinkExpired')
return JsonResponse(reply,status=status)
def post(self,request,token,format=None):
'''
#input pwd: password
#input pwd_confirm : confirmation password
'''
status = 400
reply = {}
k = PasswordReset.objects.filter(unique_code= token).count()
print('total in db ', k) # shows 1
check_token = PasswordReset.objects.values('createdon','profile_id',).get(unique_code=token)
# error: returning 2!
#exists but is it valid or not? It cant be
createdon = check_token['createdon']
if createdon > timezone.now() - timedelta(hours=USER_VALUES['PWD_RESET_LIFESPAN']):
status = 200
else:
reply['detail'] = _('errorPwdResetLinkExpired')
'''
except:
reply['detail'] = _('errorBadPwdResetLink')
'''
return JsonResponse(reply,status=status)
I expect all the tests to pass.

Defaulting an updateview action in django form under a def post

Working with another coder on a project. His change stopped the UpdateView on some forms from saving edits. I realized why.... he defined a
def post
which works for the case he was working on, but needs an else action that just does a default update. I am not sure how to do this when he UpdateView isn't doing it all automagically.
The code to the UpdateView:
class ProviderUpdateView(UpdateView):
model = Provider
form_class = ProviderForm
provider_form_class = ProviderForm
provider_term_form_class = ProviderTermForm
template_name = 'ipaswdb/provider/provider_form.html'
success_url = '/ipaswdb/provider/'
def get_context_data(self, **kwargs):
context = super(ProviderUpdateView, self).get_context_data(**kwargs)
provider = context['object']
context['provider_id'] = provider.id
prov = Provider.objects.get(pk=provider.id)
#print "provider: ",
#print prov
#print "provider terminated: ",
#print prov.is_terminated
if prov.is_terminated:
provider_form = ProviderFormView(instance=prov)
context['readonly'] = True
else:
print("NOT TERMINATED SO LETS DO THIS")
provider_form = ProviderForm(instance=prov)
context['readonly'] = False
context['provider_form'] = provider_form
provider_term_form = ProviderTermForm()
context['provider_term_form'] = provider_term_form
### Get the list of GroupLocations, but remove the current one this provider is associated with
### I just need the grouplocation id and the name
#grplocs = GroupLocationProvider.objects.filter(
return context
def post(self, request, *args, **kwargs):
#print self.request.POST.keys()
#print self.request.POST.values()
print("Posting...")
if self.request.POST.has_key('terminate'):
provider = Provider.objects.get(pk=kwargs['pk'])
form = ProviderTermForm(request.POST)
if form.is_valid():
print "Terminating Provider: ",
print provider
provider_term = ProviderTerm()
provider.is_terminated = True
provider.save()
### Update the term fields
provider_term.provider = provider
provider_term.old_id = provider.id
provider_term.term_date = form.cleaned_data['term_date']
provider_term.term_comment = form.cleaned_data['term_comment']
provider_term.save()
return HttpResponseRedirect(self.success_url)
I know I need an else to this statement in the post:
if self.request.POST.has_key('terminate'):
I am just not sure what the just do your regular thing' is in the UpdateView. I tested my hypothesis that his code broke the ability to edit and save a provider cause I removed the def post completely, and all worked well with the UpdateView automagic. Since we are overriding? the def post it seems to me we have to handle the regular update ourselves, just not sure how that looks.