flask-restful: how to parse parameters when connected to sql server db? - python

I want to create a REST API using python flask. So anytime I do this:
localhost/customers?cust_country=USA
I want to fetch every row from table 'customers' where everyone is from USA.
This is the script I've so far:
from flask import Flask, request
from flask_restful import Resource, Api
from sqlalchemy import create_engine
from flask_restful import reqparse
e = create_engine("mssql+pyodbc://....")
app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('cust_country', type = 'string')
class Dep(Resource):
def get(self):
conn = e.connect()
args = parser.parse_args()
query = conn.execute("select * from customers where cust_country = ?", [args['cust_country']])
print(query)
return {'custid': [i[0] for i in query.cursor.fetchall()]}
api.add_resource(Dep, '/customers')
if __name__ == '__main__':
app.run()
I'm getting this error:
C:\Users\x>curl 127.0.0.1:5000/customers?cust_country=USA 404 Not
Found Not Found The requested URL was not found on
the server. If you entered the URL manually please check your
spelling and try again.
======================
part 2: sending multiple parameters:
from flask import Flask
from flask_restful import Resource, Api
from flask_restful import reqparse
from sqlalchemy import create_engine
e = create_engine("x")
parser = reqparse.RequestParser()
parser.add_argument('cust_country', type = str)
parser.add_argument('cust_name', type = str)
app = Flask(__name__)
api = Api(app)
class Dep(Resource):
def get(self):
args = parser.parse_args()
conn = e.connect()
query = conn.execute("select cust_id from customers where cust_country = ? and cust_name = ?", [args['cust_country'], args['cust_name']])
return {'custid': [i[0] for i in query.cursor.fetchall()]}
api.add_resource(Dep, '/customers')
if __name__ == '__main__':
app.run()
this is what I'm using in my curl:
curl "127.0.0.1:5000/customers?cust_country=USA&cust_name=Wascals"

One error that you may be facing is by using
parser.add_argument('cust_country', type = 'string') which is wrong.
It should be parser.add_argument('cust_country', type = str).
Also I would suggest to use app.run(debug=True) as it will help you debug simple errors.

Related

send filename and file to python api for upload image

followings are my code for server and client where i want to pass filename in server api so that server and save the image as sender filename.
server.py
from flask import Flask
from flask_restful import Resource, Api, reqparse
import werkzeug
app = Flask(__name__)
api = Api(app)
class UploadImage(Resource):
def post(self):
parser = reqparse.RequestParser()
#parser.add_argument('FNAME', required=True)
parser.add_argument('file', type=werkzeug.datastructures.FileStorage, location='files')
args = parser.parse_args()
imageFile = args['file']
#filename = args['FNAME']
#print(filename)
imageFile.save('test.jpg')
api.add_resource(UploadImage, '/uploadimage')
if __name__ == '__main__':
#app.run() # run our Flask app
from waitress import serve
print("Running....")
serve(app,host="0.0.0.0",port=8080)
print("Stopped....")
client.py
import requests
import json
dfile = open("test.jpg", "rb")
url = "http://127.0.0.1:8080/uploadimage"
test_res = requests.post(url, files = {"file": dfile})
print(test_res)
if test_res.ok:
print(" File uploaded successfully ! ")
else:
print(" Please Upload again ! ")
The commented-out code you had would work with a small tweak, but if you're going by the standard Flask-RESTful documentation, it doesn't mention that you need to set the location on the argument.
With werkzeug-2.1.0, the flask-restful reqparse now has a 'bug' where if reqparse attempts to access an argument using the default location parameter, it will raise an exception on the server side. See this Github issue for more information.
So you need to add your filename (or FNAME or whatever) argument, but with location="form" specified like in the following Flask app that will accept an image in the file field, and a filename field to specify the name to save it as.
from flask import Flask
from flask_restful import Resource, Api, reqparse
import werkzeug
app = Flask(__name__)
api = Api(app)
upload_parser = reqparse.RequestParser(bundle_errors=True)
upload_parser.add_argument(
'file',
required=True,
type=werkzeug.datastructures.FileStorage,
location='files'
)
# note that location="form" is specified
upload_parser.add_argument(
"filename",
required=True,
type=str,
location="form"
)
class UploadImage(Resource):
def post(self):
args = upload_parser.parse_args()
image = args.file
image.save(args.filename)
api.add_resource(UploadImage, '/uploadimage')
if __name__ == '__main__':
app.run()
# you can add back your waitress.serve stuff here
# but I didn't want to bother in my test environment
And now I can hit it with a request and have it save my image:
curl http://localhost:5000/uploadimage \
-F "filename=my-image.png" \
-F "file=#/path/to/image.png"

Flask: How can I send '+' symbol into a GET query parameter

I'm using this code to get a query parameter, that is a 10 digit number that includes a '+' in the begin (it's a phone number, i.e. +541143214321). I can get the number ok, but it comes with and empty space instead of the '+'.
This is the code:
#!/usr/bin/env python3
import json
from flask import Flask, request
from flask_restful import Resource, Api, reqparse
app = Flask(__name__)
api = Api(app)
class Run_Test(Resource):
def get(self, enviro):
parser = reqparse.RequestParser()
parser.add_argument('phone_number', type=str, required=False)
args = parser.parse_args()
phone_number = args['phone_number']
print("phone_number: ", phone_number, flush=True)
def start():
host = '0.0.0.0'
port = 9090
app.run(host=host, port=port, debug=True)
api.add_resource(Run_Test, '/run-test/<enviro>')
The request I run is, from Postman:
GET http://localhost:9090/run-test/pre?phone_number='+541143214321'
The result I get:
phone_number: ' 541143214321'
The same with '', with "" and without them. Any suggestion?
Thanks in advance.

How to store JSON from API with pymsql? Chatbot Dialogflow with python

I have some problem. I make chatbot dialogflow with fulfillment python and mysql. The chatbot must have connection to mysql. There are 2 tables in my database, inbox and outbox. Table inbox contain of user input and table outbox contain of response from API (like omdbapi.com). How to store user input from dialoglow and data json from API with pymsql for chatbot? Here the code. The code work, but missing of connection to database. I will grateful if you help my task. Thank you
from flask import Flask, request, jsonify
import os
import dialogflow
import requests
import json
from db_con import *
app = Flask(__name__)
if __name__ == "__main__":
app.run()
#app.route('/get_movie_detail', methods=['POST'])
def get_movie_detail():
data = request.get_json(silent=True)
movie = data['queryResult']['parameters']['movie']
api_key = os.getenv('OMDB_API_KEY')
movie_detail = requests.get('http://www.omdbapi.com/?t={0}&apikey={1}'.format(movie, api_key)).content
movie_detail = json.loads(movie_detail)
response = """
Title : {0}
Released: {1}
Actors: {2}
Plot: {3}
Runtime: {4}
Genre: {5}
""".format(movie_detail['Title'], movie_detail['Released'], movie_detail['Actors'], movie_detail['Plot'], movie_detail['Runtime'], movie_detail['Genre'])
reply = {
"fulfillmentText": response,
}
return jsonify(reply)

connecting mongodb server via seperate class

I am using flask to create simple api. The api simply returns values from mongoDB. Everything works great if i do the connection within same function. I am not doing connection simply at start of file because i am using uwsgi and nginx server on ubuntu. If i do that then there will be a problem of fork.
However, I have to use this connection with other api so thought to make a seperate class for connection and each api will simply call it . I m using this functionality to make codes manageable. However when i try the these codes it always shows internal server error. I tried making this function static too , still the error exists.
Note - I have replaced mongodb address with xxx as i am using mongodbatlas account here
from flask import Flask
from flask import request, jsonify
from flask_pymongo import pymongo
from pymongo import MongoClient
from flask_restful import Resource, Api, reqparse
app = Flask(__name__)
api = Api(app)
#client = MongoClient("xxx")
#db = client.get_database('restdb')
#records = db.stars
class dbConnect():
def connect(self):
client = MongoClient("xxx")
db = client.get_database('restdb')
records = db.stars
return records
class Order(Resource):
def get(self):
#client = MongoClient("xxx")
#db = client.get_database('restdb')
#records = db.stars
#star = records
star = dbConnect.connect
output = []
for s in star.find():
output.append({'name' : s['name'], 'distance' : s['distance']})
return jsonify({'result' : output})
api.add_resource(Order, '/')
if __name__ == "__main__":
app.run(host='0.0.0.0')
ERROR {"message": "Internal Server Error"}
Preliminary investigation suggests that you haven't instantiated your dbConnect class. Also, you haven't called the method connect properly.
class Order(Resource):
def get(self):
db = dbConnect() # This was missing
star = db.connect() # This is how you make method call properly.
output = []
for s in star.find():
output.append({'name' : s['name'], 'distance' : s['distance']})
return jsonify({'result' : output})
Also class dbConnect() should be declared as class dbConnect:.

Flask RESTful API: Issue with db connection pool

I'm trying to create REST API endpoints using flask framework. This is my fully working script:
from flask import Flask, jsonify
from flask_restful import Resource, Api
from flask_restful import reqparse
from sqlalchemy import create_engine
from flask.ext.httpauth import HTTPBasicAuth
from flask.ext.cors import CORS
conn_string = "mssql+pyodbc://x:x#x:1433/x?driver=SQL Server"
auth = HTTPBasicAuth()
#auth.get_password
def get_password(username):
if username == 'x':
return 'x'
return None
app = Flask(__name__)
cors = CORS(app)
api = Api(app)
class Report(Resource):
decorators = [auth.login_required]
def get(self):
parser = reqparse.RequestParser()
parser.add_argument('start', type = str)
parser.add_argument('end', type = str)
args = parser.parse_args()
e = create_engine(conn_string)
conn = e.connect()
stat = """
select x from report
"""
query = conn.execute(stat)
json_dict = []
for i in query.cursor.fetchall():
res = {'x': i[0], 'xx': i[1]}
json_dict.append(res)
conn.close()
e.dispose()
return jsonify(results=json_dict)
api.add_resource(Report, '/report')
if __name__ == '__main__':
app.run(host='0.0.0.0')
The issue is that I get results when I call this API only for a day or so after which I stop getting results unless I restart my script (or sometimes even my VM) after which I get results again. I reckon there is some issue with the database connection pool or something but I'm closing the connection and disposing it as well. I have no idea why the API gives me results only for some time being because of which I have to restart my VM every single day. Any ideas?
Per my experience, the issue was caused by coding create_engine(conn_string) to create db pool inside the Class Report so that always do the create & destory operations of db pool for per restful request. It's not correct way for using SQLAlchemy ORM, and be cause IO resouce clash related to DB connection, see the engine.dispose() function description below at http://docs.sqlalchemy.org/en/rel_1_0/core/connections.html#sqlalchemy.engine.Engine:
To resolve the issue, you just need to move e = create_engine(conn_string) to the below of the code conn_string = "mssql+pyodbc://x:x#x:1433/x?driver=SQL Server" and remove the code e.dispose() both in the Class Report, see below.
conn_string = "mssql+pyodbc://x:x#x:1433/x?driver=SQL Server"
e = create_engine(conn_string) # To here
In the def get(delf) function:
args = parser.parse_args()
# Move: e = create_engine(conn_string)
conn = e.connect()
and
conn.close()
# Remove: e.dispose()
return jsonify(results=json_dict)

Categories

Resources