Extracting json dict from POST request - python

I want to make use of webhook provided by Townscript to update the is_paid field in my model. The format of the data is (a json dict):- link(Under server notification API column). A piece of useful information on the link was: We will send the data in following format using post parameter data.
Here is the python code:
def payment(request):
if request.method == "POST":
posts=request.POST["data"]
result=json.dumps(posts) #converting incoming json dict to string
paymentemail(result) # emailing myself the string (working fine)
data = posts
try:
user = Profile.objects.filter(email=data['userEmailId']).first()
user.is_paid = True
user.save()
except Exception as e: paymentemail(str(e)) # emailing myself the exception
return redirect('/home')
else:
.......
The two emails correspoinding to the paymentemail() function in the above code were:
"{\"customQuestion1\":\"+9175720*****\",\"customAnswer205634\":\"+917572******\",
\"customQuestion2\":\"**** University\",\"customQuestion205636\":\"College Name\",\"ticketPrice\":**00.00,
\"discountAmount\":0.00,\"uniqueOrderId\":\"***********\",\"userName\":\"********\",
\"customQuestion205634\":\"Contact Number\",\"eventCode\":\"**********\",\"registrationTimestamp\":\"12-12-2019 22:22\",
\"userEmailId\":\"***********#gmail.com\",etc....}"
I understand that the backslashes are for escaping the quotation marks.
Second email: (which is the exception)
string indices must be integers
Does that mean that data=request.POST['data'] gives me a string thus leading to an error when I use data['userEmailId']? How do I deal with this error?

def payment(request):
if request.method == "POST":
posts=request.POST
result=json.dumps(posts) #converting incoming json dict to string
paymentemail(result) # emailing myself the string (working fine)
try:
user = Profile.objects.filter(email=posts['userEmailId']).first()
user.is_paid = True
user.save()
except Exception as e: paymentemail(str(e)) # emailing myself the exception
return redirect('/home')
else:
.......
I changed posts=request.POST["data"] since the will get only the value of key "data", instead I use posts=request.POST so that we will get the entire request as key-value pair (dict).
user = Profile.objects.filter(email=posts['userEmailId']).first()

Related

How to save http request to django?

I have a device that do some calculations and then i want to send it with help of request to my site:
import requests
params = {'data1': '47237582'}
r =requests.get("http://127.0.0.1:8000/", data = params)
print(r)
I have a django site. How can i save the value of my request for later display?
def read(request):
if request.method == 'GET':
msg = request.GET['data1']
And how can i save it to database?
First, create a model called Message, fields maybe: msg and from, it actually depends on how you want to store the msg,
Then after getting the msg with msg = request.GET['data1'] store the msg with msg_obj = Message.objects.create(msg=msg, ...other-fields if any)
If you are gonna have different keys (i.e columns) for each request, then I say you use MongoDB, which will allow you save different set of key-values in a single document.

Twilio: Create two outgoing calls and join the conference using Python

I am trying to create a conferencing app with max 2 speakers using Twilio using Python/Django. However, in the docs I found out that you can do this by having inbound calls. but, my business model doesnt work like that. Is there a way for this to work like:
My Twilio number calls number 1
My Twilio number calls number 2
Twilio brings two channels to a new conference
I've tried this solution:
Twilio how to make two outbound calls and join(conference) them using node js
but it didn't help me much..
Here's my code:
#csrf_exempt
def conference(request):
print("success")
response = VoiceResponse()
dial = Dial()
dial.conference('Rooxm 1234')
response.append(dial)
print(response)
return HttpResponse('')
def call(number):
client = Client(TWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKEN)
call = client.calls.create(
url='https://<blahblah_removed_purposefully>.ngrok.io/conf/',
to='+' + str(number),
from_='<removed_my_twilio_num>'
)
print(call.sid)
def index(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = CallForm(request.POST)
# check whether it's valid:
if form.is_valid():
#print(dir(form.data.values))
call(form.cleaned_data['inline'])
call(form.cleaned_data['outline'])
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = CallForm()
return render(request, 'CallForm.html', {'form': form})
This gave me an error message during the call which is:
"An application error has occurred. Goodbye"
And I also get this in the debugger:
"Error on line 1 of document : Premature end of file. "
Any idea?
Okay so, I figured this out. The only thing that was needed to make that setup work is I had to modify the response, add the xml string there and then set the content_type of the returned object.
return HttpResponse(str(response),content_type='application/xml')

Raise custom exception for incorrect header Flask

I am building a Flask REST API which does not have a front-end yet. I've successfully developed a GET request for a Flask view that fetches all the subscribers from the SQL database. I'm using the same Flask view to INSERT VALUES through POST requests to the database. To sanitize the input, I've used (%s) as placeholder for the values. Below are excerpts from the code:
#main.py
#app.route('/api/subscribe/', methods=['GET', 'POST'])
def subscribe():
if request.method == 'GET':
try:
data = DB.get_subscription_list()
return Response(json.dumps({'result': data}, cls=DateTimeEncoder, sort_keys=True), mimetype='application/json')
except Exception as e:
return e
elif request.method == 'POST':
try:
email = request.form.get('email')
data = DB.add_email(email)
return Response(json.dumps({'result': 'Email added to database'}, cls=DateTimeEncoder, sort_keys=True), mimetype='application/json')
except Exception as e:
return e
#dbhelper.py
def add_email(self,data):
connection = self.connect()
try:
#The following adds an email entry to the 'users' table.
query = "INSERT INTO users (email) VALUES (%s);"
with connection.cursor() as cursor:
cursor.execute(query,data)
connection.commit()
except pymysql.ProgrammingError as e:
print(e)
raise e
finally:
connection.close()
I have edited this question with more precise information and specific to my problem.
I am currently testing the API on RESTClient and Postman.
When I send a POST request with header {'Content-Type':'application/x-www-form-urlencoded'}, the value inserts successfully. Output: {"result": "Email added to database"}. If this header is not used, I get a Programmingerror exception below:
(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%s)' at line 1")
How can I raise a custom exception for the incorrect header entry so that it raises this custom exception rather than show a syntax error?Of course, this is not a syntax error as the value is inserted successfully when using the urlencoded header.
I solved this issue by creating custom error handlers for each error status code. I realized that the mimetype {'Content-Type':'application/x-www-form-urlencoded'} header is required when I use request.form.get(). This is because the information in the form is encoded when it is sent to the server. I have now used request.json.get() as I would like to send the data in json instead, the header now required is {'Content-Type':'application/json'} mimetype. So, now my view looks like this:
#subscribe.py
#app.route('/api/users/subscribe', methods=["GET", "POST"])
def subscribe():
if request.method == "GET":
try:
data = DB.get_subscription_list()
return Response(json.dumps({'result': data}, cls=DateTimeEncoder, sort_keys=True), mimetype='application/json', status=200)
except Exception:
return Response(json.dumps({'error': 'Could not fetch subscribers from database.'}), status=500)
elif request.method == "POST":
try:
email = request.json.get("email")
data = DB.add_email(email)
return Response(json.dumps({'result': 'Email added to database.'}, cls=DateTimeEncoder, sort_keys=True), mimetype='application/json', status=201)
except Exception:
return Response(json.dumps({'error': 'Could not add to database.'}), status=500)
Notice, the custom error exception above for each method. I also created custom error handlers in cases where an error appears with a status code and I haven't explicitly defined an exception for them . For example, if I get an error with status code 500 which renders in html, the error handler below will show a custom error in json. I've added similar error handlers for status codes 405, 401 etc. so that I always get custom errors injson.
#app.errorhandler(500)
def method_not_allowed(error):
return make_response(jsonify({'error': 'An error occurred during a request'}), 500)
Similarly, I added an exception for database queries:
#dbhelper.py
try:
#The following adds an email entry to the 'users' table.
uri = url_for('subscribe', email=email, _external=True)
query = "INSERT INTO users (email) VALUES (%s);"
with connection.cursor() as cursor:
cursor.execute(query,email)
connection.commit()
except Exception:
return Response(json.dumps({'error':'Database connection error.'}), status=500)
Now, I was getting json exceptions when I don't send data in json or invalid data in my POST requests. There are no Programmingerrors anymore as I have corrected my code to respond with desired results.
A more organized way is to define custom error classes in a separate errors.py file and call the exception functions whenever necessary.

Returning error indication when flask POST Request fails

I'm trying to create a simple flask application that takes a string from an iOS application and stores it in a local data base. I'm a bit confused whats happening in the return portion of the submitPost() function. I'm trying to return a dictionary that contains a BOOL that indicates whether the Post request executed fully. However, i'm not sure how to variate between returning a 0 or a 1.
//Function that handles the Post request
#app.route('/submitPost/', methods=['POST'])
def submitPost():
post = Post.from_json(request.json)
db.session.add(post)
db.session.commit()
return jsonify(post.to_json), {'Success': 1}
Try the below. This way if an exception is thrown when trying to insert data it will be caught and the transaction will be rolled back as well as a response being send back.
you could also replace the zero and one with True & False depending on your preference
Below code hasn't been tested
#app.route('/submitPost/', methods=['POST'])
def submitPost():
try:
post = Post.from_json(request.json)
db.session.add(post)
db.session.commit()
return jsonify({'Success': 1})
except Exception: # replace with a more meaningful exception
db.session.rollback()
return jsonify{'Failure': 0}

How to POST multiple FILES using Flask test client?

In order to test a Flask application, I got a flask test client POSTing request with files as attachment
def make_tst_client_service_call1(service_path, method, **kwargs):
_content_type = kwargs.get('content-type','multipart/form-data')
with app.test_client() as client:
return client.open(service_path, method=method,
content_type=_content_type, buffered=True,
follow_redirects=True,**kwargs)
def _publish_a_model(model_name, pom_env):
service_url = u'/publish/'
scc.data['modelname'] = model_name
scc.data['username'] = "BDD Script"
scc.data['instance'] = "BDD Stub Simulation"
scc.data['timestamp'] = datetime.now().strftime('%d-%m-%YT%H:%M')
scc.data['file'] = (open(file_path, 'rb'),file_name)
scc.response = make_tst_client_service_call1(service_url, method, data=scc.data)
Flask Server end point code which handles the above POST request is something like this
#app.route("/publish/", methods=['GET', 'POST'])
def publish():
if request.method == 'POST':
LOG.debug("Publish POST Service is called...")
upload_files = request.files.getlist("file[]")
print "Files :\n",request.files
print "Upload Files:\n",upload_files
return render_response_template()
I get this Output
Files:
ImmutableMultiDict([('file', <FileStorage: u'Single_XML.xml' ('application/xml')>)])
Upload Files:
[]
If I change
scc.data['file'] = (open(file_path, 'rb'),file_name)
into (thinking that it would handle multiple files)
scc.data['file'] = [(open(file_path, 'rb'),file_name),(open(file_path, 'rb'),file_name1)]
I still get similar Output:
Files:
ImmutableMultiDict([('file', <FileStorage: u'Single_XML.xml' ('application/xml')>), ('file', <FileStorage: u'Second_XML.xml' ('application/xml')>)])
Upload Files:
[]
Question:
Why request.files.getlist("file[]") is returning an empty list?
How can I post multiple files using flask test client, so that it can be retrieved using request.files.getlist("file[]") at flask server side ?
Note:
I would like to have flask client I dont want curl or any other client based solutions.
I dont want to post single file in multiple requests
Thanks
Referred these links already:
Flask and Werkzeug: Testing a post request with custom headers
Python - What type is flask.request.files.stream supposed to be?
You send the files as the parameter named file, so you can't look them up with the name file[]. If you want to get all the files named file as a list, you should use this:
upload_files = request.files.getlist("file")
On the other hand, if you really want to read them from file[], then you need to send them like that:
scc.data['file[]'] = # ...
(The file[] syntax is from PHP and it's used only on the client side. When you send the parameters named like that to the server, you still access them using $_FILES['file'].)
Lukas already addressed this,just providing these info as it may help someone
Werkzeug client is doing some clever stuff by storing requests data in MultiDict
#native_itermethods(['keys', 'values', 'items', 'lists', 'listvalues'])
class MultiDict(TypeConversionDict):
"""A :class:`MultiDict` is a dictionary subclass customized to deal with
multiple values for the same key which is for example used by the parsing
functions in the wrappers. This is necessary because some HTML form
elements pass multiple values for the same key.
:class:`MultiDict` implements all standard dictionary methods.
Internally, it saves all values for a key as a list, but the standard dict
access methods will only return the first value for a key. If you want to
gain access to the other values, too, you have to use the `list` methods as
explained below.
getList call looks for a given key in the "requests" dictionary. If the key doesn't exist, it returns empty list.
def getlist(self, key, type=None):
"""Return the list of items for a given key. If that key is not in the
`MultiDict`, the return value will be an empty list. Just as `get`
`getlist` accepts a `type` parameter. All items will be converted
with the callable defined there.
:param key: The key to be looked up.
:param type: A callable that is used to cast the value in the
:class:`MultiDict`. If a :exc:`ValueError` is raised
by this callable the value will be removed from the list.
:return: a :class:`list` of all the values for the key.
"""
try:
rv = dict.__getitem__(self, key)
except KeyError:
return []
if type is None:
return list(rv)
result = []
for item in rv:
try:
result.append(type(item))
except ValueError:
pass
return result

Categories

Resources