GET Body is not being sent with APIClient Django DRF - django

i want to send body data using get request in the django drf test case APITestCase
for example
data ={'hi':'bye'}
self.client.get('media_list/', {'body': data})
and in the views i can able get the body using below code
request.data.get('hi', None)
but it is not working using {'body': data} my test method but it is working fine in the postman raw type.
what is tried is(not working)
self.client.get('media_list/', data=data)

I faced this issue while writing unit test. You can solve it with this workaround.
Problem: When you request with client.get method, the method put your data to url. You can see link in the below.
https://github.com/encode/django-rest-framework/blob/master/rest_framework/test.py#L194
Solution: You should use client.generic() directly. Don't use client.get() https://github.com/encode/django-rest-framework/blob/master/rest_framework/test.py#L203
Example
import json
from rest_framework.test import APIClient
from rest_framework.test import APITestCase
class TestClass(APITestCase):
def setUp(self):
# Setup run before every test method.
pass
def test_send_json_in_body(self):
data ={'hi' : 'bye'}
resp = self.client.generic(method="GET", path="/return/data/", data=json.dumps(data), content_type='application/json')
request._request
<WSGIRequest: GET '/return/data/'>
request.data
{'hi': 'bye'}
It works :)

data` returns request body and for get api's you need to get query params
Try bellow code:
data = {'hi':'bye'}
self.client.get('media_list/', data)
request.query_params.get('hi', None)

Related

how to make thunk HTTP POST request to Django rest api

I'm pretty new to this, so I will show you the error first and then display my thought process.
I am attempting to use a thunk (Http request in redux) to make a post request to my personal rest api in django. (so it can run cURL commands and use a authorization code to gain an access token and get user data from an API)
when I make this request, the network returns a 201 status to stay it worked, while my console logs the dispatch as rejected, and gives the error, "message: "Unexpected end of JSON input". In this message, payload is undefined, and my payload is living in meta under arg.
After much reserach, it looks like I either need to JSON.strigify something on my front end, OR that django is receiving a null object and not knowing what to do with it. I believe it is the latter, and not sure how to proceed. Here is my code:
FRONTEND:
slice.py
import {createAsyncThunk} from '#reduxjs/toolkit'
export const postRequest= createAsyncThunk('slice/postRequest',
async postData => {
const response=
await client.post('url', {slice:postData})
return response.slice
}
)
component.py
import {useDispatch} from 'react-redux'
import {postRequest} from './slice'
const Component = () => {
const dispatch=useDispatch()
const authCode= 'samplecode'
const data={authCode:`${authCode}`, userData:{}}
dispatch(postRequest(JSON.stringify(data))
return(null)
}
export default Component
BACKEND:
models.py
from django.db import models
class UserData(models.Model):
authCode=models.CharField(max_length=100, default='')
userData= models.JSONField
serializers.py
from rest_framework import serializers
from .models import *
class UserDataSerializer(serializers.ModelSerializer):
class meta:
model=UserData
fields= ('authCode', 'userData')
views.py
from .models import *
from .serializers import *
from django.views.decorators.csrf import csrf_excempt
from rest_framework.decorators import api.view
from rest_framework.resposne import Response
from rest_framework import status
#api_view(['POST'])
#csrf_excempt
define restapi(request)
if request.method == "POST":
serializer = UserDataSerializer(data = request.data
if serializer.is_valid():
serializer.save
return Response(status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_Request
I also tried it where it would return a status of 204, and without JSON.stringify.
I noticed that when I made a successful post request to a json server, it would make the request with the payload in args, and if it succeeded, the payload would get moved to payload, but it is my guess that my current error is happening because the payload is blank at the start of the thunk dispatch. Really I am not sure though
I solved this problem, it was stupid.
I was using a homemade client and I am not experienced enough to do that. I just got axios and used that as the client and it worked fine. I also simplified the request in the component to not have JSON.stringify and not have the backticks.

Django REST Framework - Set request in serializer test for ApiClient

I already read: Django REST Framework - Set request in serializer test?. And it doesn't work for me! Because I'm using APIClient and not RequestFactory like him.
I built a web app where the back-end is implemented using the Django REST Framework. Now I'm writing unit tests and I have come across a problem in testing my serializer methods. Here is one example of a serializer method I'm struggling with:
def get_can_edit(self, obj):
request = self.context['request']
user = User.objects.get(username=request.user)
return user == obj.admin
When trying to call this from the test, first I declare an instance of the serializer:
But now I need self.serializer to have the correct request when get_can_edit does self.context.get('request'). I've created a fake request with the correct information using APIClient:
self.client = APIClient()
self.client.force_authenticate(user)
conference = a_fake_conference
res = self.client.get('conference:conference-detail'. args=[conference.id])
serializer = ConferenceSerializer(conference, context={WHAT_IS_REQUEST?})
# I'm using a dict as context but the request gave me an error: context={'request': { 'user': user }}
sert.assertEqual(res.data, serializer.data)
Now I am stuck because I am unsure how to add request1 to serializer such that request = self.context['request'] will return
Thanks.
Use wsgi_request (source code-Django) of APIClient to get the WSGI request object.
self.client = APIClient()
self.client.force_authenticate(user)
res = self.client.get('conference:conference-detail'. args=[conference.id]
# make sure to call the `get(...)` method before accessing `wsgi_request` attribute
request_object = res.wsgi_request
Disclaimer: Not sure whether this is a DRF way to get the request object.
You're testing two things with each other here, so that's sort of part of the problem. Typically, you'd use self.client.get(...) as an integration test, that is, a test that ensures that everything from request to response is working as expected. For tests like these, you wouldn't use a serialiser, because then you're using your application code (your serialiser) to test itself.
If you're writing an integration test, you should be testing the raw response with something like:
from django.test import TestCase, RequestFactory
class MyUnitTestCase(TestCase):
def test_the_whole_stack(self):
response = self.client.get(
"conference:conference-detail", args=[conference.id]
)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json()["some-key-you-expect"],
"Some value you expect"
)
Note that you also don't have to invoke APIClient() directly. It's there by default.
For a unit test, like the kind of test you'd write to make sure your serialiser is working properly, you don't need or want to be poking around with a WSGIRequest object. Instead, Django supplies a RequestFactory for just this case:
from django.test import TestCase, RequestFactory
class MyUnitTestCase(TestCase):
def test_my_serialiser(self):
serializer = ConferenceSerializer(
conference,
context={"request": RequestFactory().get("/")}
)
self.assertEqual(
serialiser.data["some-key-you-expect"],
"Some value you expect"
)

django test client doesnt call POST, but GET

I try to test view calling by POST. I use follow=True. But test client uses GET method and my POST data are not passed.
here is my view:
def aaa(request):
n = request.method
d = request.POST
template = 'shop/test.html'
return render(request, template, d)
Here is my test:
from django.utils import unittest
from django.test.client import Client
def test_add_to_cart_page(self):
response = self.client.post('/aaa/', {'product': 11}, follow=True)
self.assertEqual(response.status_code, 200)
When the view is called. It is not POST, but GET used and my POST params are empty of course. Can somebody say why its happened?
EDIT:
I made a clean venv with fresh Django. And it works as expected(calls POST) Looks like there is something rotten in the state of Denmark.
follow=True
means that the client follows the redirection.
response = self.client.post('/aaa/', {'product': 11}, follow=True)
means that the response contains the followed response content. There is nothing wrong with your test; it must be doing a POST.
What's weird is that your view doesn't redirect to anything so I don't understand why you use follow=True. Also I don't see why you assume that post isn't working. What's the result of your test?
After investigation I realize that using PREPEND_WWW breaks the test client post requests.

Getting PATCH request parameter in Django

Is it possible to retrieve the request parameters of a HTTP PATCH request in Django? request.method == 'PATCH' is recognised, but I struggle to retrieve the request payload. I've tried request.REQUEST.items(), but this didn't contain any data. I know I could use Django-tastypie, but in this case, I would like to avoid it (and I supposed tastypie is using some Django methods to retrieve this data anyway).
I'm using Django 1.5.1.
You can use a QueryDict class manually. This is class implemented in django and processing all text data received through http request.
Link on the docs:
https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.QueryDict
And here is an example of usage:
from django.http import QueryDict
def home_view(request):
if request.method == 'PATCH':
data = QueryDict(request.body)
print data['your_field']
As #asitm9 mentioned, using request.data works for POST, PUT and PATCH and is recommended.
Just try to use MultiPartParser when the CONTENT_TYPE is "multipart/form-data;",else QueryDict.
if request.META.get('CONTENT_TYPE', '').startswith('multipart'):
from django.http.multipartparser import MultiPartParser
query_dict, multi_value_dict = MultiPartParser(request.META, request,
request.upload_handlers).parse()
else:
from django.http import QueryDict
query_dict = QueryDict(request.body)
raw_post_data did the trick (I used it before, but forgot it existed)

Django how to test if message is sent

I want to test if message is sent to user after submit. I'm using django.contrib.messages. Everything seems to be working during manual testing (runserver), but in unit test I don't get messages.
Code that stores message:
messages.success(request, _('Internationalized evil message.'))
Code that should test message:
from django.contrib.messages.api import get_messages
...
def test_message_should_sent_to_user(self):
"""After successful phone number submit, message should be displayed."""
response = self.client.post(
reverse('evil.views.evil_data_submit'), self.valid_data)
messages = get_messages(response.request)
self.assertNotEqual(len(messages), 0)
It looks like that no middleware is called during test client post method call.
Update after #Tisho answer
Messages should be found in response.context, even my guts say that it should work, but it doesn't. I've placed import pdb; pdb.set_trace() in django/contrib/messages/context_processors.py to see if its called during test client.post, its not.
I've double checked TEMPLATE_CONTEXT_PROCESSORS, MIDDLEWARE_CLASSES and INSTALLED_APPS - probably tomorrow I'll discover that I missed something.
Important detail
Forgot to mention that in case of successful submit view returns HttpResponseRedirect therefore response.context is empty.
Solution
View returns redirect (which has no context data), to solve that we can pass follow=True to client.post method (method suggested by #Tisho).
During unit tests, the message could be found in
response = self.client.post(
reverse('evil.views.evil_data_submit'), self.valid_data)
messages = response.context['messages']
If your view returns a redirect, response.context will be empty unless you pass follow=True, like so:
response = self.client.post(
reverse('evil.views.evil_data_submit'),
self.valid_data,
follow=True)
The best way I found is by using mock
http://www.voidspace.org.uk/python/mock/patch.html#patch-methods-start-and-stop
class SimpleCommentTestDirect(TestCase):
def setUp(self):
self._patcher1 = patch('django.contrib.messages.error')
self.mock_error = self._patcher1.start()
def tearDown(self):
self._patcher1.stop()
def test_error_message(self):
self.client.get('/vote/direct/unknownapp/comment/1/up/')
self.assertEqual(self.mock_error.call_args[0][1], 'Wrong request. Model.')
BTW: There are also should be a way to get request by using mock. And by using request object get message from django.contrib.messages.get_messages