I have following code
class MyClass(restful.Resource):
def get(self):
headers = {'Content-Type': 'text/html'}
return make_response(render_template('myfile.html'),200,headers)
def post(self):
session['CONSUMER_KEY']=request.form.get('consumer_key')
session['CONSUMER_SECRET']=request.form.get('consumer_secret')
render_template('myfile.html')
api.add_resource(MyClass,"/mag/",endpoint="mag")
I have written following test:
def mytest(self):
content_type={"Content-Type": "application / x - www - form - urlencoded","Content-Disposition": "form-data"}
response = self.client.post(
api.url_for(MyClass), data = json.dumps({'consumer_key':'testconsumerkey',
'consumer_secret':'testconsumersecret'}),
headers=content_type
)
The issue is form data is blank and thats the values are not getting set in session. When i debug i see that request.data is populated but request.form is an empty dictionary. Can someone suggest how I can send form data in a post request from a test
EDIT: Environment details
Python 2.7, Flask web framework, self.client is . I am using flask.ext.testing
You seem to be confused as to what the expected format for the post body should be. Should it be JSON data (which is what you send in the test case), or should it be in the application/x-www-form-urlencoded format (which is what you claim to send in the test case, and what the endpoint will read)?
If you wish to receive JSON data, you'll need to change the endpoint to read the data from request.get_json(). You'll also need to use application/json as the Content-Type header in the test case.
If you wish to receive urlencoded post data, then just simplify the test case by removing the Content-Type header and the json.dumps. Just pass the data dict to the data argument.
Related
I have a django method and I'm just trying to pull out a POST variable:
#csrf_exempt
def update_backer(request):
for k, v in request.POST.items():
print(k, v)
email = request.POST.get("email", "none")
return JsonResponse({"data":{
"email":email
}})
When I try to do a POST via javascript XMLHttpRequest, I wasn't getting the data through. So I fell back to Postman to try to confirm things are working on the django end. And I am posting the data, but django isn't seeing it. Am I doing something obviously wrong?
Edit: Interestingly enough, if I change it to GET instead of POST, it works as I would expect.
For POST request values are sent in request body. You can use application/x-www-form-urlencoded content type so that request body has same format of query params.
I tried to POST a query in json in postman's raw body like below:
and I got 500 internal error server and HTML results like below
Otherwise, I'm able to POST using form-data and from a script ran from the terminal using the same json format.
I use Django 1.5.12 to serve the API and python 2.7.
Can anyone help?
When you submit form data the content-type header will be
Content-Type: multipart/form-data
and in your Django view, you will do something like below to read the posted data:
name = request.POST.get('name')
However, when you post data using the way you did now with the postman, the header will be:
Content-Type: application/json
and to grab this json data, you can do something like:
import json
mydata = json.loads(request.body)
name = mydata.get('name')
Status 500, must be because you have not handled the case for json request in your view.
If you are using request.POST in your code to access the HTTP POST body values, then take note that as stated in https://www.django-rest-framework.org/tutorial/2-requests-and-responses/
request.POST # Only handles form data. Only works for 'POST' method.
If you would like your view to be able to read HTTP POST body values both from form-data (or x-www-form-urlencoded) and raw (JSON format) at the same time, then you cannot use request.POST alone. You could either:
Read from request.body
Read from request.data (only available if the logic is under rest_framework)
Example
urls.py
path('printpostedvalues/', printpostedvalues_view),
views.py
#csrf_exempt
def printpostedvalues_view(request):
print("request.body", request.body)
try:
print("json.loads(request.body)", json.loads(request.body))
for k, v in json.loads(request.body).items():
print("json.loads(request.body)", k, "-->", v)
except Exception:
print("request.body's string value is not in the format of JSON key:value pairs")
print("request.POST", request.POST)
for k, v in request.POST.items():
print("request.POST", k, "-->", v)
return HttpResponse("Printed values in console...")
Perform an HTTP POST http://127.0.0.1:8000/printpostedvalues/ using Postman.
Case 1: form-data
HTTP Body
KEY - VALUE
athlete - LeBron James
status - GOAT!
quote - Strive for greatness
Console Output
request.GET <QueryDict: {}>
request.body b'----------------------------269460357877673035654590\r\nContent-Disposition: form-data; name="athlete"\r\n\r\nLeBron James\r\n----------------------------269460357877673035654590\r\nContent-Disposition: form-data; name="status"\r\n\r\nGOAT!\r\n----------------------------269460357877673035654590\r\nContent-Disposition: form-data; name="quote"\r\n\r\nStrive for greatness\r\n----------------------------269460357877673035654590--\r\n'
request.body's string value is not in the format of JSON key:value pairs
request.POST <QueryDict: {'athlete': ['LeBron James'], 'status': ['GOAT!'], 'quote': ['Strive for greatness']}>
request.POST athlete --> LeBron James
request.POST status --> GOAT!
request.POST quote --> Strive for greatness
[11/Mar/2020 04:57:43] "POST /printpostedvalues/ HTTP/1.1" 200 28
Conclusion:
The request.body contains the HTTP POST information from the form-data but is clearly not in the JSON format and thus cannot be parsed through json.loads(). The request.POST also contains the values.
Case 2: x-www-form-urlencoded
HTTP Body
same as Case 1
Console Output
same as Case 1 except for line 2 - request.body b'athlete=LeBron%20James&status=GOAT%21"e=Strive%20for%20greatness'
Conclusion:
same as Case 1
Case 3: raw (JSON)
HTTP Body
{
"athlete" : "LeBron James",
"status" : "GOAT!",
"quote" : "Strive for greatness"
}
Console Output
request.GET <QueryDict: {}>
request.body b'{\n "athlete" : "LeBron James",\n "status" : "GOAT!",\n "quote" : "Strive for greatness"\n}'
json.loads(request.body) {'athlete': 'LeBron James', 'status': 'GOAT!', 'quote': 'Strive for greatness'}
json.loads(request.body) athlete --> LeBron James
json.loads(request.body) status --> GOAT!
json.loads(request.body) quote --> Strive for greatness
request.POST <QueryDict: {}>
Conclusion:
The request.body contains the HTTP POST information from the raw JSON data and is in the JSON format and thus possible to be parsed through json.loads(). The request.POST does not contain any values.
SUMMARY
Long story short, do not rely on request.POST if you expect HTTP POST body values to come not just as form-data but also as raw JSON format. If you are using rest_framework for this, your best option is to use request.data because it will be able to catch all information coming from an HTTP POST regardless of format.
I'm sending a post request to my API made using django rest framework:
curl --header "X-MyHeader: 123" --data "test=test" http://127.0.0.1:8000/api/update_log/
In my rest framework view, I want to get my costum header, and if the custom header satisfies a condition, I will proceed to analyze my post data.
Ok, my view looks like:
class PostUpdateLogView(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (
parsers.FormParser,
parsers.MultiPartParser,
parsers.JSONParser,
)
renderer_classes = (renderers.JSONRenderer,)
def post(self, request):
print request.Meta
# Get custom header
# Validate custom header
# Proceed to analize post data
# Make response
content = {
'response': 'response',
}
return Response(content)
I'm trying to find my custom header on request.Meta element, but when I print request.Meta, I get a 500 error. If I print request.data, I get the expected response.
¿What is the way to get a custom header on my post request using django rest framework?
The name of the meta data attribute of request is in upper case:
print request.META
If your header is called "My-Header", your header will be available as:
request.META['HTTP_MY_HEADER']
Or:
request.META.get('HTTP_MY_HEADER') # return `None` if no such header
Quote from the documentation:
HTTP headers in the request are converted to META keys by converting all characters to uppercase, replacing any hyphens with underscores and adding an HTTP_ prefix to the name. So, for example, a header called X-Bender would be mapped to the META key HTTP_X_BENDER.
If you provide a valid header information and get that information from backend then follow those
client-name='ABCKD'
then you have get that client information in post or get function following this-
request.META['HTTP_CLIENT_NAME']
it will give you output 'ABCKD'.
remember that, whatever the valid variable name you provide in your header information in request, django convert it uppercase and prefix with 'HTTP_'
in here it will client-name converted to CLIENT_NAME and prefix with HTTP_.
so final output is HTTP_CLIENT_NAME
Seeing this is an old post, I thought I would share the newer and more flexible approach that is available since Django 2.2. You can use the request's headers object:
# curl --header "X-MyHeader: 123" --data "test=test" http://127.0.0.1:8000/api/update_log/
request.headers['X-MYHEADER'] # returns "123"
request.headers['x-myheader'] # case-insensitive, returns the same
request.headers.get('x-myheader') # returns None if header doesn't exist
# standard headers are also available here
request.headers.get('Content-Type') # returns "application/x-www-form-urlencoded"
The biggest differences with request.META are that request.headers doesn't prepend headers with HTTP_, it doesn't transform the header names to UPPER_SNAKE_CASE and that the headers can be accessed case-insensitively. It will only transform the header to Title-Casing (e.g. X-Myheader) when displayed.
I'm creating two POST calls. One using a django form and one using angular js via a resource xhr.
The angular setup looks like this:
myModule.factory('gridData', function($resource) {
//define resource class
var root = {{ root.pk }};
var csrf = '{{ csrf_token }}';
return $resource('{% url getJSON4SlickGrid root.pk %}:wpID/', {wpID:'#id'},{
get: {method:'GET', params:{}, isArray:true},
update:{method:'POST', headers: {'X-CSRFToken' : csrf }}
});
});
With creating an xhr post request as such:
item.$update();
This post request is send to the server as expected, but when I want to access the QueryDict I cannot access the data passed using:
name = request.POST.get('name', None)
name is always None like this.
The issue behind this is that the QueryDict object is getting parsed quite strange.
print request.POST
<QueryDict: {u'{"name":"name update","schedule":0"}':[u'']}>
Whereas I would have expected this result, which I got when I send the data via a "normal" Post request:
<QueryDict: {u'name': [u'name update'], u'schedule': [u'0']}>
So it seems to be that Django receives something in the POST request which instructs Django to parse the parameters into one string. Any idea how to circumvent this?
Update:
I found this discussion where they say that the issue is if you provide any content type other than MULTIPART_CONTENT the parameters will be parsed into one string. I checked the content-type send with the POST request and it is really set to 'CONTENT_TYPE': 'application/json;charset=UTF-8'. Thus this is likely the issue. Therefore my question is: How can I set the CONTENT_TYPE for a xhr post request created using angular.js resources to MULTIPART_CONTENT?
you could either:
fiddle with the client to send data instead of json
use json.loads(request.raw_post_data).get('name', None) (django < 1.4)
use json.loads(request.body).get('name', None) (django >= 1.4)
The Angular documentation talks about transforming requests and responses
To override these transformation locally, specify transform functions as transformRequest and/or transformResponse properties of the config object. To globally override the default transforms, override the $httpProvider.defaults.transformRequest and $httpProvider.defaults.transformResponse properties of the $httpProvider.
you can find an example here as was previously pointed at.
The title pretty much says it all: I use raw_post_data in a couple of views, and thus I need the test client to properly grant access to it.
I have copied the raw_post_data string, from a mock request, passed it to json.loads(), and then used the resulting dict as the POST data for the test client. Then, I set the content type to "application/json" - this causes raw_post_data to appear, but it is not the same raw_post_data as the mock request.
When you change the content type in the test client, the data parameter is not parsed as a dictionary anymore but sent directly. Try copyin your JSON string directly as the data parameter to your post request, you should receive it in raw_post_data in your application.
Just need to follow the steps as below:
1. set the data attribute to your string.
2. then set the content_type attribute to application/octet-stream.
payload = {'k1':'v1'}
data = json.dumps(payload)
response = self.client.post(url, data=data, content_type='application/octet-stream', **self.auth_headers)