Sending POST data between Django apps - python

I need to send POST request from one Django app to another under the same project. The request arrives, but without POST data.
Why is that? And how to send POST data properly?
Sender part, app 1 view:
def get_project_data(request):
if request.method == "POST":
if request.is_ajax():
response = etl_post('get_data', [request.user.project.id], request.POST)
def etl_post(path, identifiers=None, post_data=None):
def date_handler(obj):
if hasattr(obj, 'isoformat'):
return obj.isoformat()
else:
raise TypeError
json_data = json.dumps(post_data, default=date_handler) if post_data else None
return _request(path, identifiers, json_data)
def _request(path, identifiers=None, post_data=None, method=None):
data = None
try:
url = urlparse.urljoin(settings.ETL_WEB_API_URL, path)
if identifiers is not None:
for o in identifiers:
url += "/" + str(o)
if post_data:
url += "/"
request = urllib2.Request(url, data=post_data)
request.add_header("Content-Type", "application/json")
request.add_header("X-ETL-Authorization", settings.ETL_WEB_API_KEY)
if method:
request.get_method = lambda: method
result = urllib2.urlopen(request)
data_str = result.read()
if result.getcode() == 200:
data = json.loads(data_str)
else:
logger.error("Unexpected response %s" % result)
except Exception as e:
logger.exception(e.message)
return data
Also, I tried result = urllib2.urlopen(request, data=post_data), no success.
post_data example:
{"project_id": "nYhRTAmGkkHSlLr8BfPR", "project_name": "rocket-launch", "salt": "805b2892c16369275eerec4dd401f5f", ...}
(Pdb) type(post_data)
<type 'str'>
Receiver part, app 2 view:
#csrf_exempt
def get_project_data(request, trust_id):
if request.method == 'POST':
pdb.set_trace()
The arrived message:
(Pdb) request
<WSGIRequest: POST '/pipeline/get_project_data/2/'>
(Pdb) request.POST
<QueryDict: {}>

You're sending JSON, not form-encoded data. That is found in request.body, not request.POST.
I must say though, if these two apps are in the same project there are much easier ways of sending data between them than by making HTTP requests.

Related

invalid(empty) response with Django

AM building a USSD application, in Django with this API https://documenter.getpostman.com/view/7705958/UyrEhaLQ#intro. I get the responses from the API and initialize the data to be processed. But I don't get the menu (MSG) to display on the user phone successfully. The error I get is invalid(empty) response. This is the response to the user’s request. The content provider should provide a response to the request in the same format.
USERID = This is the ID provided by NALO to the client
MSISDN = The mobile number of the user
MSG =This is a mandatory parameter that holds the message to be displayed on the user’s phone
MSGTYPE = This indicates whether the session should continue or be terminated (True/false)
#csrf_exempt
def ussd(request):
if request.method == 'GET':
html = "<html><body>Nothing here baby!</body></html>"
return HttpResponse(html)
elif request.method == 'POST':
url = "https://99c9-102-176-94-213.ngrok.io/ussd"
response_data = json.loads(request.body)
code_id = response_data["USERID"]
serviceCode = response_data["MSISDN"]
type = response_data["MSGTYPE"]
session_id = response_data["SESSIONID"]
text = response_data["USERDATA"]
msg = ""
if text == "":
msg = "Welcome To TEST Dev"
elif text == "1":
msg = "This is Test Two"
payload ={
"USERID": code_id,
"MSISDN": serviceCode,
"MSGTYPE": type,
"USERDATA": text,
"SESSIONID": session_id,
"MSG": msg,
}
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=json.dumps(payload))
return HttpResponse(response, status=200)

Response is not valid 'application/json'

I have an issue with my post request. This is my code:
def add_review(request, dealer_id):
if request.method == "GET":
context = {
"cars": CarModel.objects.all().filter(dealerId = dealer_id),
"dealerId": dealer_id
}
return render(request, 'djangoapp/add_review.html', context)
if request.method == "POST":
if request.user.is_authenticated:
form = request.POST
review = {
"dealership": int(dealer_id),
"name": request.user.username,
"review": form["review"],
"purchase": form.get("purchasecheck") == 'on',
}
if form.get("purchasecheck"):
review["purchase_date"] = datetime.strptime(form.get("purchasedate"), "%m/%d/%Y").isoformat()
car = CarModel.objects.get(pk=form["car"])
review["car_make"] = car.make.name
review["car_model"] = car.name
review["car_year"]= int(car.year.strftime("%Y"))
json_payload = {"review": review}
url = "https://4fbfebf7.us-south.apigw.appdomain.cloud/api/review"
post_request(url=url, json_payload=json_payload, dealer_id=dealer_id)
return redirect("djangoapp:dealer_details", dealer_id=dealer_id)
else:
return redirect("/djangoapp/login")
And this:
def post_request(url, json_payload, **kwargs):
json_data = json.dumps(json_payload, indent=4)
print(f"{json_data}")
try:
# Call get method of requests library with URL and parameters
response = requests.post(url, params=kwargs, json=json_data)
except Exception as e:
# If any error occurs
print("Network exception occurred")
print(f"Exception: {e}")
print(f"With status {response.status_code}")
print(f"Response: {response.text}")
I am receiving Response is not valid 'application/json' error as you can see here.
Meanwhile, when I copy the exact same JSON to IBM Cloud to test my APIs, all is working fine and the record is created as you can see here.
I guess it's a very silly mistake, but where?
When you pass json to request.post it should be a serializable object (not already serialized)
def post_request(url, json_payload, **kwargs):
# json_data = json.dumps(json_payload, indent=4). << delete
print(f"{json_payload}")
try:
# Call get method of requests library with URL and parameters
response = requests.post(url, params=kwargs, json=json_payload)
except Exception as e:
# If any error occurs
print("Network exception occurred")
print(f"Exception: {e}")
print(f"With status {response.status_code}")
print(f"Response: {response.text}")

FLASK REST API returns 400 on POST

I'm building a REST API for a simple Todo application using flask and SQLAlchemy as my ORM. I am testing my API using Postman. I'm on a windows 10 64-bit machine.
A GET request works and returns the data that I've entered into my database using python.
I'd like to try to add a task now. But when I POST my request, I receive an error.
My route in flask looks like this.
#add task
#app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():
if not request.json or not 'title' in request.json:
raise InvalidUsage('Not a valid task!', status_code=400)
task = {
'title': request.json['title'],
'description': request.json['description'],
'done': False
}
Todo.add_todo(task)
return jsonify({'task': task}), 201
And the method it's calling on the Todo object looks like this.
def add_todo(_title, _description):
new_todo = Todo(title=_title, description=_description , completed = 0)
db.session.add(new_todo)
db.session.commit()
What I've tried
I thought that maybe the ' in my Postman Params was causing an issue so I removed them. But I still get the same error.
Then I thought that maybe the way that Postman was sending the POST was incorrect so I checked to make sure that the Content-Type headers was correct. It is set to application/json
Finally, to confirm that the issue was that flask didn't like the request, I removed the check in the add task route to make sure the request had a title. So it looks like this.
if not request.json:
And I get the same error. So I think that the problem must be with how I'm actually sending the POST rather than some kind of formatting issue.
My entire code looks like this.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import json
from flask import jsonify
from flask import request
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(300), unique=False, nullable=False)
description = db.Column(db.String(), unique=False, nullable=False)
completed = db.Column(db.Boolean, nullable=False)
def json(self):
return {'id': self.id,'title': self.title, 'description': self.description, 'completed': self.completed}
def add_todo(_title, _description):
new_todo = Todo(title=_title, description=_description , completed = 0)
db.session.add(new_todo)
db.session.commit()
def get_all_tasks():
return [Todo.json(todo) for todo in Todo.query.all()]
def get_task(_id):
task = Todo.query.filter_by(id=_id).first()
if task is not None:
return Todo.json(task)
else:
raise InvalidUsage('No task found', status_code=400)
def __repr__(self):
return f"Todo('{self.title}')"
class InvalidUsage(Exception):
status_code = 400
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
#app.route('/')
def hello_world():
return 'Hello to the World of Flask!'
#get all tasks
#app.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return_value = Todo.get_all_tasks()
return jsonify({'tasks': return_value})
#get specific task
#app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
task = Todo.get_task(task_id)
#if len(task) == 0:
#raise InvalidUsage('No such task', status_code=404)
return jsonify({'task': task})
#add task
#app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():
if not request.json or not 'title' in request.json:
raise InvalidUsage('Not a valid task!', status_code=400)
task = {
'title': request.json['title'],
'description': request.json['description'],
'done': False
}
Todo.add_todo(task)
return jsonify({'task': task}), 201
#update task
#app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['PUT'])
def update_task(task_id):
task = [task for task in tasks if task['id'] == task_id]
if len(task) == 0:
raise InvalidUsage('No provided updated', status_code=400)
if not request.json:
raise InvalidUsage('request not valid json', status_code=400)
if 'title' in request.json and type(request.json['title']) != unicode:
raise InvalidUsage('title not unicode', status_code=400)
if 'description' in request.json and type(request.json['description']) != unicode:
raise InvalidUsage('description not unicode', status_code=400)
if 'done' in request.json and type(request.json['done']) is not bool:
raise InvalidUsage('done not boolean', status_code=400)
task[0]['title'] = request.json.get('title', task[0]['title'])
task[0]['description'] = request.json.get('description', task[0]['description'])
task[0]['done'] = request.json.get('done', task[0]['done'])
return jsonify({'task': task[0]})
#delete task
#app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
task = [task for task in tasks if task['id'] == task_id]
if len(task) == 0:
raise InvalidUsage('No task to delete', status_code=400)
tasks.remove(task[0])
return jsonify({'result': True})
#app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
if __name__ == '__main__':
app.run(debug=True)
EDIT:
Turns out I wasn't setting the request type in POSTMAN correctly. I've updated it to 'application/json' in the header. Now I'm receiving a different error.
Bad Request Failed to decode JSON object: Expecting value: line 1
column 1 (char 0)
I've tried all the previous steps as before but I continue to get this error.
EDIT 2:
Per a response below, I tried putting the values into the body of the POST. But I still get back a 400 response.
From the image [second postman screenshot] it looks like you pass data in query string but create_task() expects them in request body.
Either replace all occurrences of request.json with request.args in create_task() (to make it work with query params) or leave it as it is and send data in request body.
curl -X POST http://localhost:5000/todo/api/v1.0/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Learn more flask","description":"its supper fun"}'
Also, take a look at Get the data received in a Flask request.
EDITED
Update your add_todo to something like
#classmethod
def add_todo(cls, task):
new_todo = cls(title=task["title"], description=task["description"], completed=0)
db.session.add(new_todo)
db.session.commit()
Related: generalised insert into sqlalchemy using dictionary.

Middleware logging limitation Django 1.11

Background:
I have integration test which is working fine, but later on failed when I
added the customized middleware
def test_mobile_update_customer(self):
user = User.objects.create(username='warhead')
self.client.force_authenticate(user=user)
mommy.make(Customer, family_name='IBM', created_user=self.soken_staff, updated_user=self.soken_staff)
data = {
"family_name": "C0D1UM"
}
customer = Customer.objects.first()
res = self.client.patch(reverse('api:customer-detail', kwargs={'pk': customer.id}), data=data)
self.assertEqual(200, res.status_code)
customer.refresh_from_db()
self.assertEqual('C0D1UM', customer.family_name)
self.assertEqual('spearhead', customer.created_user.username)
self.assertEqual('warhead', customer.updated_user.username)
Problem:
The middleware break it with Exception
File "/Users/el/Code/norak-cutter/soken/soken-web/soken_web/middleware.py", line 47, in process_request
data['PATCH'] = json.loads(request.body)
File "/Users/el/.pyenv/versions/soken/lib/python3.6/site-packages/django/http/request.py", line 264, in body
raise RawPostDataException("You cannot access body after reading from request's data stream")
django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
The problem is data has been read before the RESTful api do the job.
Then the program raises an exception.
def process_request(self, request):
if request.path.startswith('/api/'):
data = collections.OrderedDict()
data["user"] = request.user.username
data["path"] = request.path
data["method"] = request.method
data["content-type"] = request.content_type
if request.method == 'GET':
data['GET'] = request.GET
elif request.method == 'POST':
data['POST'] = request.POST
# https://stackoverflow.com/questions/4994789/django-where-are-the-params-stored-on-a-put-delete-request
# elif request.method == 'PUT':
# data['PUT'] = json.loads(request.body)
# test_mobile_update_customer
# raise RawPostDataException("You cannot access body after reading from request's data stream")
# django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
# elif request.method == 'PATCH':
# data['PATCH'] = json.loads(request.body)
elif request.method == 'DELETE':
pass
self.__uuid = str(uuid.uuid4())
request_logger.info(f'{self.__uuid} {json.dumps(data)}')
Update2
Attempt:
I change client constructor refer to https://github.com/encode/django-rest-framework/issues/2774
def test_mobile_update_customer(self):
user = User.objects.create(username='warhead')
# self.client.force_authenticate(user=user)
from django.test import Client
client = Client()
client.force_login(user)
mommy.make(Customer, family_name='IBM', created_user=self.soken_staff, updated_user=self.soken_staff)
data = {
"family_name": "C0D1UM"
}
customer = Customer.objects.first()
res = client.patch(reverse('api:customer-detail', kwargs={'pk': customer.id}), data=data, content_type='application/json')
self.assertEqual(200, res.status_code)
customer.refresh_from_db()
self.assertEqual('C0D1UM', customer.family_name)
self.assertEqual('spearhead', customer.created_user.username)
self.assertEqual('warhead', customer.updated_user.username)
It does not work. The Client misinterpret the payload
>>> res.content
b'{"detail":"JSON parse error - Expecting property name enclosed in double quotes: line 1 column 2 (char 1)"}'
Workaround:
I don't know this is correct for all cases or not, but I workaround by this
test.py
https://gist.github.com/elcolie/88eb7c90cfca1c369a020ac232c7fbcc
middleware.py
https://gist.github.com/elcolie/5d9b1a2f890a0efcb46fdb95c0e90908
result.py
https://gist.github.com/elcolie/298e595b404c1a5839ed8dd584d2f07f
Question:
How do I do integration test of PATCH with the same time that testcase will not break by my middleware?
Right now I have to choose either one of them.

'str' object has no attribute 'csrf_exempt' while returning response from a django view?

I have this strange problem with my view which is returning response in json format.My view looks like this:
def CheckPlayer(request,client_id):
if request.method == 'GET':
try:
user = User.objects.get(id = client_id)
except:
return Error(message = "User doesnot exists.")
message = request.GET.get('message','')
if not message:
return Error(message = "Argument Missing.")
response = {}
result = MakingRequest(message)
result = json.loads(result)
if result['failure'] == '0':
response['failure'] = '0'
else:
response['failure'] = '1'
return HttpResponse(json.dumps(response), mimetype="application/javascript")
else:
return Error()
def MakingRequest(message):
values = {'message':message}
rObjects = Ram.objects.all()
temp = []
for i in rObjects:
temp.append(i.appId)
values['registration_ids'] = temp
param = json.dumps(values)
req = urllib2.Request("https://android.googleapis.com/gcm/send", param)
req.add_header( 'Content-Type' , 'application/json' )
req.add_header( 'Authorization' , 'key=7FcEMnl0FRTSBjhfjfhjfHi1Rmg04Ns' )
response = urllib2.urlopen(req)
return response.read()
I have tested it on my local server it works perfectly , but if I run it on my server (nginx,gunicorn,django-mongoDB,mongoDB) then it gives me this Error.I know about this error that if a view doesnot return HttpResponse from a view then it djangi raises error "Nonetype object has no attribute csrf_exempt' " but in my case i am returning response which is in json format but still its giving me error.Please help me
You aren't returning an HttpResponse when you return Error(), so Django can't apply the decorator to it.

Categories