Test Django Forms submitted through Ajax view - python

I I'm trying to test a view that is used by Ajax. While with the server (manual testing) everything works, when I ran my unit test an error I can't explain appears.
If you take a look at the following lines, where I present my code the most simplified as possible, you will see that although I add a variable to the json dict named something (just as I do in ajax) the field appears as missing in the form.
My Test (tests.py):
def test_edit_profile_user(self):
self.user = User.objects.create_user(
username='user', email='test#awesome.org', password='top_secret')
self.client.login(username=self.user.username, password='top_secret')
data = {'something': 'data'}
response = self.client.post(reverse('edit_something'),
json.dumps(data),
content_type='application/json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
print(response.content)
self.assertEqual(response.status_code, 200)
While my Ajax view works, my test gives the following traceback:
b'{"something": ["This field is mandatory."]}'
Failure
Traceback (most recent call last):
File "***\tests.py", line 81, in ***
self.assertEqual(response.status_code, 200)
AssertionError: 400 != 200
What am I doing wrong in the test?
The rest of the code:
My view (views.py):
def edit_something(request):
if request.method == 'POST':
form = CustomForm(request.POST)
if form.is_valid():
form.save()
response_code = 200
response_data = {'something':'updated')
else:
response_code = 400
response_data = form.errors
return JsonResponse(
response_data,
status=response_code,
)
My JS (something.js):
$.ajax({
url : form_endpoint, // the endpoint
type : "POST", // http method
data : {
something: 'data',
},// data sent with the post request
And code to handle the response

I hope I get a better solution, but for now, my solution was to remove content_type and post a dictionary.
In tests.py the code for the var response is:
response = self.client.post(reverse('edit_something'), data)
This makes the view receive the appropriate values, while when I add content_type='application/json' the view request.POST is empty.

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

Why POST API request not working from browser but working fine on Postman? Throws AssertionError in browser to call is_valid() before accessing .data

Ok, Here's the problem: I have created a POST API in Django REST framework & I want to add logged-in user to request.data when making POST request & It works perfectly fine when I create post calling this API from Postman, but when I visit the post create API endpoint from browser it throws AssertionError: '''When a serializer is passed a data keyword argument you must call .is_valid() before attempting to access the serialized .data representation.
You should either call .is_valid() first, or access .initial_data'''.
Here's the snippet of my get_serializer method which i use to override & add user to request.data:
class PostCreateView(CreateAPIView):
serializer_class = PostSerializer
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs["context"] = self.get_serializer_context()
request_data = self.request.data.copy()
request_data["user"] = self.request.user.id
kwargs["data"] = request_data
return serializer_class(*args, **kwargs)
def post(self,request):
serializer = self.get_serializer()
serializer.is_valid(raise_exception=True)
serializer.save()
status_code = status.HTTP_201_CREATED
response = {
'success' : 'True',
'status code' : status_code,
'message': 'Post created successfully',
'post_detail': serializer.data,
}
return Response(response, status=status_code)
Update: I have changed my code in post method to pass user initially in a Post instance & then pass it in PostSerializer as: serializer = self.serializer_class(post, data=request.data) & it works on both Postman as well as browser's DRF request. But I'm curious to know why my above code with get_serializer method gave me AssertionError from browser's DRF request? but worked fine when making a POST request from Postman.

Wrong Behaviour doing tests on Django

I'm having problems to do test on Django. I've been reading the documentation of the responses and I can't do the same as they explain on the documentation.
When I get the response, I only have access to response.status_code and can't access to context or redirect_chain when I write response.(and now PyCharm shows all available options).
I've checked on settings.py and I've 'BACKEND': 'django.template.backends.django.DjangoTemplates' to be sure that I'm using Django templates so I don't know why don't work the test. I need configure something?
The code of the test I'm trying to do it's:
from django.test import TestCase
from django.test.client import Client
class Test(TestCase):
def testLogin(self):
client = Client()
headers = {'X-OpenAM-Username': 'user', 'X-OpenAM-Password': 'password', 'Content-Type': 'application/json'}
data = {}
response = self.client.post('/login/', headers=headers, data=data, secure=True, follow=True)
assert (response.status_code == 200)
# self.assertRedirects(response, '/menu/', status_code=301, target_status_code=200)
I'm not using Django authentication, the login form sends the data to an IDP and if the IDP sends with a correct answer, the "login" it's successful:
def login(request):
logout(request)
message = None
if request.method == "POST":
form = LoginForm(request.POST)
if form.is_valid():
username = request.POST['username']
password = request.POST['password']
headers = {'X-OpenAM-Username': username, 'X-OpenAM-Password': password, 'Content-Type': 'application/json'}
data = {}
req = requests.post('http://openam.idp.com:8090/openamIDP/json/authenticate', headers=headers, params=data)
if req.status_code == 200:
respJson = json.loads(req.content)
tokenIdJson = respJson['tokenId']
request.session['tokenId'] = tokenIdJson
return render_to_response('menu/menu.html', request)
elif req.status_code == 401:
message = "Invalid username and/or password. Please, try again"
else:
form = LoginForm()
return render_to_response('registration/login.html', {'message': message, 'form': form},
context_instance=RequestContext(request))
The redirect assert it's commented because now it fails, when I do the debug I see an empty redirect_chain. I don't understand why happens this because running the web everything works, all views redirect as expected.
Why I only can check status_code? I'm doing something wrong when I redirect after a successful login that on a normal use it works but on the test not?
Thanks.
The remote authentication url expects the credentials as headers, but your local login view expects them as POST data. Your test passes the credentials as headers to your local view.
As a result, the form is passed an empty dictionary (request.POST contains no actual data), and the form is invalid. You get an empty form as a response, without any redirects.
You have to simply pass the credentials as post data to your local view:
def testLogin(self):
client = Client()
data = {'username': 'user', 'password': 'password'}
response = self.client.post('/login/', data=data, secure=True, follow=True)
assert (response.status_code == 200)
self.assertRedirects(response, '/menu/', status_code=301, target_status_code=200)

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)

Django views testing , RequestFactory with data

I have this view:
def send_results(request):
print request
if request.is_ajax():
address = request.POST.get('url')
process_data(address)
context = get_all_from_database()
return HttpResponse(json.dumps(context), content_type='application/json')
and I need to test it:
def test_send_results(self):
factory = RequestFactory()
request = factory.get('/views/send_results')
response = send_results(request)
self.assertEqual(response.status_code, 200)
But it always stop with error that in my view 'address' value is referensed before asignment. How to pass it some value ?
If the request.is_ajax() is False then address will not be assigned before process_data(address) is called. If you want to test an AJAX request then you should pass the HTTP_X_REQUESTED_WITH header:
request = factory.get('/views/send_results', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
However you'll still need to fix your view to handle the case when the request is not an AJAX request.

Categories