Django testing post request with multipart/form data and a dictionary - python

My client uses the requests library to make this call to my Django server
import requests
response = requests.post(url, files=dict({
'value': 'key',
}))
This will create a requests that inserts the dictionary into the field request.FILES as a <MultiValueDict: {}>
I am trying to recreate this with django.test.
I keep seeing to try something like
from django.test import TestCase, Client
client = Client()
response = client.post('/sign', dict(request_data))
but the request.FILES object is empty
edit ----
I have also tried with the same result ( request.FILES -> <MultiValueDict: {}>)
client.post('/sign', {'file': dict({
'key' : 'value'
})})
Edit 2---
A look at the midldleware where I am checking the value
class ApiAuthenticationMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request: HttpRequest):
print(request.FILES)

Solution
fake_file.name = 'data.json'
post_data = {
'data.json': fake_file,
}
return self.client.post('/sign', post_data, content_type=MULTIPART_CONTENT)

Related

Django Views not getting POST Data from api call

I am not using django rest frame work
but in normal views.py I have a simple views
#views.py
def api_post_operations(request):
pdb.set_trace()
if request.POST:
print(request.POST["name"])
print(request.POST["address"])
now I call it
import requests
url = "http://localhost:8000/api_post_operations"
payload = {"name":"raj", "address": "asasass" }
rees = requests.post(url, data=payload, headers={})
it is comming
(Pdb) request.POST
<QueryDict: {}>
Any reason why it is comming {} balnk
request.body also comming blank

POST document with Django RequestFactory instead of form data

I'd like to build a request for testing middleware, but I don't want POST requests to always assume I'm sending form data. Is there a way to set request.body on a request generated from django.test.RequestFactory?
I.e., I'd like to do something like:
from django.test import RequestFactory
import json
factory = RequestFactory(content_type='application/json')
data = {'message':'A test message'}
body = json.dumps(data)
request = factory.post('/a/test/path/', body)
# And have request.body be the encoded version of `body`
The code above will fail the test because my middleware needs the data to be passed as the document in request.body not as form data in request.POST. However, RequestFactory always sends the data as form data.
I can do this with django.test.Client:
from django.test import Client
import json
client = Client()
data = {'message':'A test message'}
body = json.dumps(data)
response = client.post('/a/test/path/', body, content_type='application/json')
I'd like to do the same thing with django.test.RequestFactory.
RequestFactory has built-in support for JSON payloads. You don't need to dump your data first. But you should be passing the content-type to post, not to the instantiation.
factory = RequestFactory()
data = {'message':'A test message'}
request = factory.post('/a/test/path/', data, content_type='application/json')
I've tried Jay's solution and didn't work, but after some reseach, this did (Django 2.1.2)
factory = RequestFactory()
request = factory.post('/post/url/')
request.data = {'id': 1}
Here's what worked for me in Django 4.1:
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase, RequestFactory
from customauth import views
class RegistrationViewTest(TestCase):
def setUp(self):
self.factory = RequestFactory()
def test_post_request_creates_new_user(self):
data = {
'email': 'new_user#email.com',
'screen_name': 'new_user',
'password1': 'new_user_password',
'password2': 'new_user_password',
}
request = self.factory.post('/any/path/will/do/', data )
middleware = SessionMiddleware(request)
middleware.process_request(request)
request.session.save()
response = views.registration_view(request)
self.assertEqual(response.status_code, 302)
# ok
This test passes. The form was successfully processed in views.registration_view.
Note:
When I included content_type='application/json' in the call to self.factory.post (as the accepted answer suggests), request.POST had no content in the view. Without that, it worked. I don't know why but would be happy to learn.
I needed to manually added SessionMiddleware to request.
In later version of Django (tested on 4.0) this is no longer an issue. On the other hand, to pass data to request.POST might be.
In default, when passing content-type to a RequestFactory, data goes into request.body and when you don't, data goes into request.POST.
request_factory = RequestFactory()
# provide content-type
request = request_factory.post(f'url', data={'foo': 'bar'}, content_type="application/json")
print(request.body) # b'{"foo": "bar"}'
# don't provide content type
request = request_factory.post(f'url', data={'foo': 'bar'})
print(request.POST) # <QueryDict: {'foo': ['bar']}>

Serializer.is_valid() is always False

I have the following Serializer to handle a user:
class FriendSerializer(serializers.Serializer):
login = serializers.CharField(max_length=15, required=True)
password = serializers.CharField(max_length=15, required=True)
mail = serializers.CharField(max_length=50, required=True)
Currently, my view which processes the POST request to register a new user is, based on the Django REST tutorial:
#api_view(['POST'])
def register_new_user(request):
if request.method == 'POST':
print('POST request !')
stream = BytesIO(request.body)
data = JSONParser().parse(stream)
print(data)
serializer = FriendSerializer(data=data)
print(serializer.is_valid())
else:
print('Not a POST request!')
return HttpResponse('Nothing')
Thus, to simulate a client with a POST request, I use the following lines:
import requests
import json
json_data = json.dumps({'login': 'mylogin', 'password': 'mypassword', 'mail': 'mymail'})
r = requests.post('http://127.0.0.1:8000/register_new_user', json=json_data)
However, although the print(data) retrieves, as expected,
{"login": "mylogin", "mail": "mymail", "password": "mypassword"}
The serializer.is_valid() always returns False.
Am I missing any processing of my request?
EDIT:
I got the following info with serializer.errors:
{'non_field_errors': ['Invalid data. Expected a dictionary, but got str.']}
You dont have to convert a dictionary to string when using requests library's json option. This should work:
import requests
data = {'login': 'mylogin', 'password': 'mypassword', 'mail': 'mymail'}
r = requests.post('http://127.0.0.1:8000/register_new_user', json=data)
The solution is to use ast.literal_eval(request.data) to convert the string to a dictionary. I use request.data instead of manually parsing request.body.
However, v1k45's answer is the best solution, as I do not need to convert my dict to a string before sending my request.

how can I provide csrf protection in case of using requests module to post data to a django view

I have a modelForm as follows:
class UserProfileForm(forms.ModelForm):
class Meta:
model = FileUploads
fields = ['uploads']
and a view like so:
#csrf_exempt
def upper(request):
form = UserProfileForm(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
return HttpResponse(status=200)
return HttpResponse(status = 403)
And I have a simple script to send a multipart/encoded file to the view as such:
import requests
f = open('C:\\Users\\myname\\Desktop\\image.jpg', 'rb')
urls='http://localhost:8000/upper'
r=requests.post(urls, files= {'uploads':f})
print(r.status_code)
My question being: everything works fine as long as I have the csrrf_exempt decorator above the receiving view, that's fine for test environment. But what if I wanted the csrf protection in place? Considering the fact that I'm using requests module, how can I provide the csrf token?
You need to pass a cookie and a header with the same value:
import requests
f = open('C:\\Users\\myname\\Desktop\\image.jpg', 'rb')
urls='http://localhost:8000/upper'
cookies = {'csrftoken': 'token'}
headers = {'X-CSRF-TOKEN': 'token'}
r=requests.post(urls, files={'uploads':f}, cookies=cookies, headers=headers)
print(r.status_code)
The value of the token does not matter, you can take any literal, as long as they are the same.

Angular resource posts data but not receiving by django view

I have created an angular resource as
var services = angular.module('Services', ['ngResource']).
// SEND_REPLY_SMS
factory('SendSMS', ['$resource', function($resource){
return $resource('/bulk-sms/reply/', null,
{
send: {method: 'POST'},
}
);
}]);
I used it as
var data = $scope.data;
SendSMS.send({},data,
function(data){
console.log(data);
},function(error){
console.log(error);
}
);
I have checked with console.log(data), data contains the data and the browser shows that the post request has submitted the data.
But When I receive it in django view, I can not get the data in django view and my django view is
class ReplySMSView(View):
def post(self, request):
data = request.POST.copy()
print 'post data', request.POST # here data is not printed
data = dict(data.items())
return self.process(request, data)
def get(self, request):
data = request.GET.copy()
print 'get data', request.GET # here data is not printed
data = dict(data.items())
return self.process(request, data)
def process(self, request, data):
dct = {}
print data
model = IncomingMessage
account = request.user.account
contacts = data.get('contacts', '')
contacts = contacts if contacts else get_contacts_by_filter(model, data)
# TODO: get_contacts_by_filter is not working here for IncomingMessage
message = data.get('message', '')
identity = data.get('identity', '')
if not contacts:
dct['contacts'] = 'No contacts found.'
if not message:
dct['message'] = 'Message is required.'
if not identity:
dct['identity'] = 'Identity is required.'
if dct:
return HttpResponse(json.dumps(dct), content_type='application/json')
response = send_bulk_sms(contacts, message, identity, account, module='bulk')
return HttpResponse(response)
I am not getting where is problem in this code ?
AngularJS will post that data serialized into JSON, but django is expecting to receive form data. If you want to receive that data, you can change default behavior of AngularJS, fetch data not using POST, but rather request.body or you can use some third-party package, like Django REST framework to do job for you.
When calling ajax, you recieve encoded json string in request body, so you need to decode it using python's json module to get python dict.
As django is a web framweork, it expect data from a form.
I really reccomend using this framework http://www.django-rest-framework.org/
Anyway, to get post data will be like this in your view:
(Pdb) request.POST
<QueryDict: {}>
(Pdb) import json
(Pdb) json.loads(request.body)
{u'operator': u'pepe', u'password': u'1234', u'transport': u'LUUAAA'}
import json
class ReplySMSView(View):
def post(self, request):
data = json.loads(request.body)
print 'post data', request.POST # here data is not printed
data = dict(data.items())
return self.process(request, data)

Categories