0

I am writing my Python API using Flask. This API accept only 1 parameter called questionID. I would like it to accept a second parameter called lastDate. I tried to look around on how to add this parameter, but couldn't find a good method to do this. My current code looks as follows:

from flask import Flask, request
from flask_restful import Resource, Api, reqparse
from sqlalchemy import create_engine
from json import dumps
from flask_jsonpify import jsonify
import psycopg2
from pandas import read_sql
connenction_string = "DB Credentials'";

app = Flask(__name__)
api = Api(app)

class GetUserAnswers(Resource):
    def get(self, questionID):
        conn = psycopg2.connect(connenction_string);
        cursor = conn.cursor();

        userAnswers = read_sql('''
        select * from <tablename> where questionid = ''' + "'" + questionID + "' order by timesansweredincorrectly desc limit 15" +'''
        ''', con=conn)

        conn.commit();
        conn.close();

        result = {}
        for index, row in userAnswers.iterrows():
            result[index] = dict(row)
        return jsonify(result)

api.add_resource(GetUserAnswers, '/GetUserAnswers/<questionID>')

if __name__ == '__main__':
     app.run(port='5002')

Question 1: I'm guessing I can accept the second parameter in the get definition. If this is not true, how should I accept the second parameter?

Question 2: How do I modify the api.add_resource() call to accept the second parameter?

Question 3: I currently use http://localhost:5002/GetUserAnswers/<some question ID> to call this API from the browser. How would this call change with a second parameter?

I have never developed an API before, so any help would be much appreciated.

Patthebug
  • 4,647
  • 11
  • 50
  • 91
  • 2
    Does this answer your question? [Multiple parameters in in Flask approute](https://stackoverflow.com/questions/15182696/multiple-parameters-in-in-flask-approute) –  Jan 08 '20 at 20:14

2 Answers2

2

If you want to add multiple parameters within the url path for example:

http://localhost:5002/GetUserAnswers/<question_id>/answers/<answer_id>

Then you need to add multiple parameters to your get method:

def get(self, question_id, answer_id):
    # your code here

But if you instead want to add multiple query parameters to the url for example: http://localhost:5002/GetUserAnswers/<question_id>?lastDate=2020-01-01&totalCount=10>

Then you can use request arguments:

def get(self, question_id):
    lastDate = request.args.get('lastDate')
    totalCount = request.args.get('totalCount')
    # your code here
D Dhaliwal
  • 552
  • 5
  • 23
1

Consider several adjustments to your code:

  1. For simpler implementation as you have, use decorators in Flask API and avoid need to initialize and call the class object;
  2. Use parameterization in SQL and avoid the potentially dangerous and messy string concatenation;
  3. Avoid using the heavy data analytics library, pandas, and its inefficient row by row iterrows loop. Instead, handle everything with cursor object, specifically use DictCursor in psycopg2;

Refactored Python code (adjust assumption of how to use lastDate):

#... leave out the heavy pandas ...

app = Flask(__name__)

@app.route('/GetUserAnswers', methods= ['GET'])
def GetUserAnswers():
    questionID = request.args.get('questionID', None)
    lastDate = request.args.get('lastDate', None)

    conn = psycopg2.connect(connenction_string)
    cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)

    userAnswers = '''SELECT * FROM <tablename> 
                     WHERE questionid = %s
                       AND lastdate = %s
                     ORDER BY timesansweredincorrectly DESC
                     LIMIT 15
                 '''
    # EXECUTE SQL WITH PARAMS
    cur.execute(userAnswers, (questionID, lastDate))

    # SAVE TO LIST OF DICTIONARIES
    result = [dict(row) for row in cur.fetchall()]
    cur.close()
    conn.close()

    return jsonify(result)


if __name__ == '__main__':
     app.run(port='5002')

Browser Call

http://localhost:5002/GetUserAnswers?questionID=8888&lastDate=2020-01-08
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • 1
    I wouldn't recommend using decorators, especially if you're using flask_restful specifically for this purpose. Adding resources and their routes in the main file and linking classes from external files is 10x more convenient in the long run than having routes in decorators spread around all over the place. Having to import app into external files might end up causing circular imports deadlock right away. You modified and assumed changes in the original question quite a lot just to avoid using path arguments, which is obviously what he is trying to do. (using flask_restful) – c8999c 3f964f64 Jan 16 '20 at 09:18
  • Interesting points, @c8999c3f964f64! Admittedly, I am not a keen Flask developer and as prefaced for simpler implementation I recommended the decorator approach but makes sense the use of classes for extended applications. But certainly I stand behind the latter approaches of SQL parameterization and non-Pandas use of which I am keen on! – Parfait Jan 16 '20 at 15:47
  • absolutely, and I agree on those too. But the answer below has a more direct approach to solve the "another argument" to the argument in the path - question. – c8999c 3f964f64 Jan 17 '20 at 08:03