Unit testing Django JSON View - python

I'm trying to write some unit tests for some Django json_view views and I'm having trouble passing the json_string to the view. I posted a related question yesterday about passing a json string to a Django view from the JS, the issue was that in my JS I was just passing the json string where I needed to be passing the string as the attribute of an object, because I was failing to do this the string was being taken as the key for the resulting query dict. I'm having a similar problem again except that this time it is form a Django unit test to the Django View. Here is a simplified version of my code which produces the same result.
class MyTestCase(TestCase):
def setUp(self):
self.u = User.objects.create_user('test','test','test')
self.u.is_active = True
self.u.save()
self.client.login(username='test',password='test')
def test_create_object_from_form(self):
"""Test the creation of the Instance from the form data."""
import json
json_string json.dumps({'resource':{'type':'book','author':'John Doe'}})
print(json_string)
response = self.client.post(reverse('ajax_view'),
{'form':json_string},'json')
self.assetNotContains(response,'error')
and the view looks like this
#json_view
def ajax_view(request):
"""Process the incoming form data."""
if request.method == 'POST':
print(request.POST)
form_data = json.loads(request.POST['form'])
resource_data = form_data['resource']
form = MyUserForm(resource_data)
if form.is_valid():
...
Here is what the two print statements produce when the test is run. The json_string is
{"resource": {"type": "book", "author": "John Doe"}}
and the query dict looks like
<QueryDict: {u'{\'form\': \'{"resource": {"type": "book", "author": "John Doe"}}\'}': [u'']}>
I'm total newbie with JS and ajax, so don't worry about hurting my pride, the answer is probably so close it could jump up and bite me.

Final edit
I originally stated that header HTTP_X_REQUESTED_WITH='XMLHttpRequest' was necessary in the post call but this is currently false while in tests. This header is necessary for the csrf middleware but csrf is disabled in tests. However, I still believe it is a good practice to put in test even if middleware disables csrf since most javascript library already pass this header by default when doing ajax. Also, if another piece of code that is not disabled ever use the is_ajax method, you won't need to debug your unittest for hours to figure out that the header was missing.
The problem is with the content-type because when django gets a value in there that is different than text/html, it doesn't use the default post data handling which is to format your data like in a query: type=book&author=JohnDoe for example.
Then the fixed code is:
response = self.client.post(reverse('ajax_view'),
{'form':json_string},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
Here's how I'm using it myself:
post_data = {
"jsonrpc" : "2.0", "method": method, "params" : params, "id" : id }
return client.post('/api/json/',
json.dumps(post_data), "text/json",
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
to do some json-rpc. Notice that since I pass a different content-type than the default value, my data is passed as is in the post request.

Thank you to #Eric_Fortin for turning me on to the header, it does not however resolve my issue with the malformed query dictionary using 'client.post'. Once I made the change from POST to GET with the XMLHttpRequest header my query dictionary straitened itself out. Here is the current solution:
response = self.client.get(reverse('ajax_view'),
{'form':json_string},'json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
this is only a partial answer since this request is going to change data on the server and should be POST not a GET.
Edit:
Here is the final code in my test that works for passing a JSON string via POST to my view:
response = self.client.post(reverse('ajax_view'),
{'form':json.dumps(json_dict)})
Now printing from the view shows that the query dictionary is well formed.
<QueryDict: {u'form': [u'{"resource": {"status": "reviewed", "name": "Resource Test", "description": "Unit Test"}}']}>
I found the answer while tinkering with one of my co-workers, removing the content_type 'json' fixed the malformed query dictionary. The view that is being tested does not make use of or call the 'HttpRequest.is_ajax()', sending the header XMLHttpRequest' has no impact on my issue, though including the header would constitute good-form since this post is an ajax request.

Related

How do I POST boolean data to Flask route

I want to pass a variable called manual to Flask a route, which will then do something based on the value in the POST form data. But the forms data is interpreted as string in flask even though I send it in a form as a dictionary.
here's the code
#app.route("/result", methods= [ 'POST', 'GET'])
def result():
manual = request.form.get("manual")
if manual is None:
return "manual is required"
here's how I am sending the data
r = requests.get('http://127.0.0.1:5000/result'
,data={manual':False})
I understand that I can do something like;
if manual == 'True'
but I don't want to be comparing strings, I want to do it in the standard way whichever it is.
Thanks
First of all, do a POST request, not a GET:
r = requests.post('http://127.0.0.1:5000/result', json={'manual': False})
Then (untested):
#app.route("/result", methods=['POST'])
def result():
json_data = flask.request.json
manual = json_data.get("manual")
if manual is None:
return "manual is required"
Have a look at the doc for details: More complicated POST requests.
Note that there are differences between using the data parameter and the json parameter. An important thing to note is the presence of the Content-Type header:
Using the json parameter in the request will change the Content-Type
in the header to application/json.

Need to store values from URL then send this values using post method

I need to store some values from a GET method than send this values using the POST method in a form.
How can i do that? it is possible?
I Already get the values from GET method and works fine, but when i try to use the payload in the POST it goes wrong because the payload dict doesnt exist in the POST
if request.method == 'GET':
payload = {
"sessId": request.GET['sessId'],
"userName": request.GET['userName'],
"sessionLifetime": SESSION_TIMEOUT,
"idleTimeout": IDLE_TIMEOUT,
}
if request.method == 'POST':
logic with the payload
requests.post(url,payload)
Thanks.
when GETting a url the data can be parsed from the URI.
when POSTing a url the data must be parsed from the headers.
usually you will find the POSTed data in the 'x-www-form-urlencoded' header

Django json data in request.body but empty

I'm using an API which requires to send a callback to a url. Thus I configure my url and my view :
def get_callback(request):
...
some treatment with request.body
My view always returns that request.body contains " b'' ". However, it must contain a lot of informations, encoded in JSON.
Indeed I know that theses informations are well sent to the callback url, I tried with requestbin.in (http://requestb.in/1d4dkk01?inspect#10fl7s) and the raw body is full.
What could case the body to be empty ? Could it be the nginx configuration ? or in setting.py ?
Thanks you
I think, you should return response have kind of json data for view. Like it
import json
def get_call_back(request):
# Do something to return dictionary same as {'abc': xyz}
json_data = json.dumps(data)
return HttpResponse(json_data, content_type='application/json')

send a POST request from python that has both JSON and non-JSON data

How would you send a request (from python) that ultimately ends up looking like this in the live header on the web page (http://localhost:8080):
POST /rest/marker/
json=%7B%22label%22%3A%22w%22%2C%22folderId%22%3Anull%2C%22url%22%3A%22%23FF0000%22%2C%22comments%22%3A%22%22%2C%22position%22%3A%7B%22lat%22%3A39.426912683948%2C%22lng%22%3A-120.20892536635%7D%7D&tid=0V7V
In better syntax, the POST request looks like this:
URL=http://localhost:8080/rest/marker/
json: {"label":"my label here","folderId":null, "url":"#FF0000","comments":"","position":{"lat":39.2965796259061,"lng":-120.16708374023438}}
tid: "0V7V"
(please ignore the data values, they are different in each of these tests)
I've tried several variations like the following:
a=requests.post("http://localhost:8080/rest/marker",data="json=%7B%22label%22%3A%22stuff%22%2C%22folderId%22%3Anull%2C%22url%22%3A%22%2300FF00%22%2C%22comments%22%3A%22%22%2C%22position%22%3A%7B%22lat%22%3A39.418%2C%22lng%22%3A-120.2%7D%7D&tid=0V7V")
a=requests.post("http://localhost:8080/rest/marker",json={"label":"stuff","folderId":"null","url":"#FF0000","comments":"","position":{"lat":39.4112,"lng":-120.2},"tid":"0V7V"})
a=requests.post("http://localhost:8080/rest/marker/",json={"label":"stuff","folderId":"null","url":"#FF0000","comments":"","position":{"lat":39.4112,"lng":-120.2}},data={"tid":"0V7V"})
The stacktrace I get in the response text always starts with this,
which probably just indicates I'm doing it wrong:
java.lang.ClassCastException: net.sf.json.JSONNull cannot be cast to
net.sf.json.JSONObject
What's the right way to do this?
Try this:
import json
payload = {
"label": "my label here",
"folderId": None, # converted to null by json serializer
"url": "#FF0000",
"comments":"",
"position": {
"lat":39.2965796259061,
"lng":-120.16708374023438,
}
}
response = requests.post(
"http://localhost:8080/rest/marker",
data={'json': json.dumps(payload), 'tid': '0V7V'}
)
From details in your question it sounds like the server expects you to post a form containing a field called json with a json-serialized string as its value.
When you send form data the values need to be urlencoded ...however requests will automatically do this for you. You just need to pass in a dict of your form data, though you will still have to json serialize the payload as the value of json key.
The problem with your first example is you have already urlencoded your payload, so when you pass it to requests it will end up double-encoded.
In your second example you are telling requests to send a json-serialized payload, just as a raw POST body rather than as form data.

Google App Engine self.redirect() POST method

In GAE (Python), using the webApp Framework, calling self.redirect('some_url') redirects the user to that URL via the GET method. Is it possible to do a (redirect) via the POST method with some parameters as well?
If possible, how?
Thanks!
This is not possible due to how most clients implement redirection [1]:
However, most existing user agent implementations treat 302 as if it
were a 303 response, performing a GET on the Location field-value regardless
of the original request method.
So you must use a workaround (like simply calling the method post() from the RequestHandler) or forget the idea.
[1] http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2
You can pass parameters. Here is an example:
Let's say you have a main page and you want to POST to '/success'. Usually, you may use this way:
self.redirect('/sucess')
But if you want to pass some parameters from the main page to /success page, like username for example, you can modify the code to this:
self.redirect('/sucess?username=' + username)
In this way, you successfully passed the username value into the URL. In /success page, you can read and store the value by using this:
username = self.request.get('username')
At last, you can make you favorite information onto the /success page by using this simple code:
self.response.out.write('You\'ve succeeded, ' + username + '!')
But, it's of course not a safe way to pass password. I wish it helps.
Looks like there's a similar question asked here: Google App Engine self.redirect post
The answer to that one recommends using the urlfetch.fetch() to do the post.
import urllib
form_fields = {
"first_name": "Albert",
"last_name": "Johnson",
"email_address": "Albert.Johnson#example.com"
}
form_data = urllib.urlencode(form_fields)
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
result = urlfetch.fetch(url=url,
payload=form_data,
method=urlfetch.POST,
headers=headers)

Categories