I created a CRUD endpoint wit Flask but when I try to GET data, I receive a 404 error. I tried to access this endpoint with 'http://127.0.0.1:5002/albums/beck//' and 'http://127.0.0.1:5002/albums/beck' but still get a 404. Since I supplied 'beck' as the artist name I thought the get method would run fine. I think I added the resource incorrectly.
class Artistdetails(Resource):
def get(self, artist_name):
conn = db_connect.connect()
# Protect against SQL injection
restricted_char = "!=<>*0&|/\\"
for char in restricted_char:
artist_name = artist_name.replace(char, "")
query_db = conn.execute("SELECT DISTINCT album FROM album WHERE artist='{0}'".format(artist_name.title()))
result = jsonify({'artistAlbumList': [i[0] for i in query_db.cursor.fetchall()]})
return result
def put(self, artist_name, album_name, album_name_new):
conn = db_connect.connect()
# Protect against SQL injection
restricted_char = "!=<>*0&|/\\"
for char in restricted_char:
artist_name = artist_name.replace(char, "")
query_db = conn.execute("UPDATE album SET album='{0}' WHERE artist='{1}' AND"
" album='{2}'".format(artist_name.title(), album_name.title(), album_name_new.title()))
result = jsonify({'putAlbumId': [i[0] for i in query_db.cursor.fetchall()]})
return result, 201
def post(self, artist_name, album_name):
conn = db_connect.connect()
# Protect against SQL injection
restricted_char = "!=<>*0&|/\\"
for char in restricted_char:
artist_name = artist_name.replace(char, "")
query_db = conn.execute("INSERT INTO album (album, artist) VALUES"
" ({0},{1})".format(artist_name.title(), album_name.title()))
result = jsonify({'postAlbumId': [i[0] for i in query_db.cursor.fetchall()]})
return result, 201
def delete(self, artist_name, album_name):
conn = db_connect.connect()
# Protect against SQL injection
restricted_char = "!=<>*0&|/\\"
for char in restricted_char:
artist_id = artist_name.replace(char, "")
album_id = album_name.replace(char, "")
query_db = conn.execute("DELETE FROM album WHERE"
" artist_id='{0}' AND album_id='{1}'".format(artist_name, album_name)
)
result = jsonify({'deleteAlbumId': [i[0] for i in query_db.cursor.fetchall()]})
return result, 204
Create API routes
api.add_resource(Api, '/')
api.add_resource(Albums, '/albums')
api.add_resource(Artistdetails, '/albums/<string:artist_name>/<string:album_name>/<string:album_name_new>')
api.add_resource(Genreyear, '/albums/yr')
api.add_resource(Genrenum, '/albums/genre')
api.add_resource(Artists, '/artists')
This line:
api.add_resource(Artistdetails,
'/albums/<string:artist_name>/<string:album_name>/<string:album_name_new>')
It adds a path to the Flask router that makes it expect /albums/<artist_name>/<album_name>/<album_name_new>, whereas you're trying to request /albums/<artist_name>, which doesn't match anything.
A quick fix for you would be:
api.add_resource(Artistdetails, '/albums/<string:artist_name>')
However, you might instead want to support query string parameters for your search interface so that requests look more like this:
/albums?artist=<string>&album_name=<string>
To do that, the documentation for Flask-RESTful reqparse would be useful.
Related
This is the first time I'm creating an API for android retrofit. I modified this code according to the snippet I got online. The main functionality of the post method is to take the given parameters and store it in the sqlite3 database.
My schema of the following two tables:
sqlite> .schema spending
CREATE TABLE spending(
ID INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT ,
reason TEXT ,
amount INTEGER
);
CREATE TABLE receiving(
ID INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT ,
from_reason TEXT ,
amount INTEGER
);
from flask import Flask, request
from flask_restful import Resource, Api
from sqlalchemy import create_engine
from flask import jsonify
db_connect = create_engine('sqlite:///api.db')
app = Flask(__name__)
api = Api(app)
class AddSpending(Resource):
def add_spending(self):
try:
_json = request.json
_date = _json['date']
_reason = _json['reason']
_amount = _json['amount']
# validate the received values
if _date and _reason and _amount and request.method == 'POST':
#do not save password as a plain text
#_hashed_password = generate_password_hash(_password)
# save edits
sql = "INSERT INTO spending(date, reason, amount) VALUES(%s, %s, %d)"
data = (_date, _reason, _amount)
#conn = mysql.connect()
conn = db_connect.connect()
cursor = db_connect.cursor()
conn.cursor()
conn.execute(sql, data)
conn.commit()
#resp = jsonify('Spending added successfully!')
#resp.status_code = 200
return
else:
return 404
except Exception as e:
print(e)
finally:
cursor.close()
conn.close()
api.add_resource(AddSpending, '/spending_up',methods=['POST']) # Route_3
When a user passes data through this parameter. The data should be stored in the database
I think the problem is that you called you method as add_spending and shoud be named as post
change def add_spending(self) by def post(self)
the code for your api should look like that, without the methods='POST'
class AddSpending(Resource):
def post(self):
try:
_json = request.json
_date = _json['date']
_reason = _json['reason']
_amount = _json['amount']
# validate the received values
if _date and _reason and _amount and request.method == 'POST':
#do not save password as a plain text
#_hashed_password = generate_password_hash(_password)
# save edits
sql = "INSERT INTO spending(date, reason, amount) VALUES(%s, %s, %d)"
data = (_date, _reason, _amount)
#conn = mysql.connect()
conn = db_connect.connect()
cursor = db_connect.cursor()
conn.cursor()
conn.execute(sql, data)
conn.commit()
#resp = jsonify('Spending added successfully!')
#resp.status_code = 200
return
else:
return 404
except Exception as e:
print(e)
finally:
cursor.close()
conn.close()
api.add_resource(AddSpending, '/spending_up') # Route_3
UPDATE
I just tried with a code similar to yours and worked
ANOTHER UPDATE
your repo code
I'm trying to create a user login system with flask-login, however i am having difficulties with querying email addresses using Google's query functions.
I can grab the ID, but since a user wont know their ID when logging in, this isn't very useful.
An overview of what the code excerpt is trying to do (I've hardcoded values for the purpose of getting a proof-of-concept working):
username = 'abc#gmail.com'
check database for username
Return true if match is found
In this guide, at 9'48", the user writes (what I assume is) a sqlAlchemy query. What would be the equivalent of this query using Googles NDB query functions?
class User(ndb.Model):
name = ndb.StringProperty()
email = ndb.StringProperty()
password = ndb.StringProperty()
#login_manager.user_loader
def load_user(user_id):
return User.get_by_id(int(user_id))
#app.route('/', methods=['GET', 'POST'])
def testlogin():
#user = User.query(getattr(User, 'email') == 'abc#gmail.com')
#login_user(user)
check = 'abc#gmail.com'
query = User.query(User.email == check)
#print query
#query = User.query()
#queryuser = User.query().filter(User.email == 'abc#gmail.com')
#login_user(queryuser)
checkTable = User.get()
#checkTable.email = check
checkTable_key = checkTable
#query = checkTable_key.get()
print str(checkTable_key)
results = User.query().fetch() #this returns a list / array
#print(results)
#print "query all" + str(queryall)
#for result in results:
#print "query all" + str(result.email)
return "logged in "
check = 'abc#gmail.com'
query = User.query(User.email == check).get() # this returns the User object, or None if not found
if query:
return True
else:
return False
# or try
return (query != None)
# or, just return the User object?
# if you just want to see if the user exists, you should do a keys_only query, which is free:
query = User.query(User.email == check).fetch(1, keys_only=True)
Context
I've written a python script designed to run on a server. The script accepts a user input to make a search. I.E the user types in an arbitrary string and the database returns all usernames with a similar string.
Description of problem
I'm very uncertain about the security of the input. The program uses a stored procedure and a parameterised procedure, but despite this, if a user types in nothing i.e a blank string or if they enter something like % then the script returns every single username in the database.
Code
import json
import mysql.connector
class json_read():
def __init__(self,name):
self.name = name
def json_caller(self):
with open(self.name) as f:
f = json.load(f)[0]
return f
f = json_read("database_connect.json")
config = f.json_caller()
def mysql_connect(func):
def wrapper(*args, **kwargs):
try:
cnx = mysql.connector.connect(**config)
cursor = cnx.cursor()
cursor.execute(*args, **kwargs)
result = cursor.fetchall()
print("\nConnection is stable # " + func.__name__)
print(result)
except:
print("\nConnection failed # " + func.__name__)
return wrapper
class query_dbh():
f2 = json_read("queries.json")
def __init__(self, index):
self.index = self.f2.json_caller()[index]
#mysql_connect
def query(*args, **kwargs):
pass
search_query = query_dbh("Search_uid").index
search_param = [raw_input("Search: ") + "%"]
query(search_query,(search_param))
Queries are kept in a JSON file and loaded by the script
[
{
"Select_names" : "SELECT first,last FROM user",
"Select_id" : "SELECT id FROM user",
"Order_names" : "SELECT first, last FROM user ORDER BY first ASC",
"Search_uid" : "SELECT uid FROM user WHERE uid LIKE %s"
}
]
Where Search_uid is the query being used.
I had a question pertaining to mysql as being used in Python. Basically I have a dropdown menu on a webpage using flask, that provides the parameters to change the mysql queries. Here is a code snippet of my problem.
select = request.form.get('option')
select_2 = request.form.get('option_2')
conn = mysql.connect()
cursor = conn.cursor()
query = "SELECT * FROM tbl_user WHERE %s = %s;"
cursor.execute(query, (select, select_2))
data = cursor.fetchall()
This returns no data from the query because there are single qoutes around the first variable, i.e.
Select * from tbl_user where 'user_name' = 'Adam'
versus
Select * from tbl_user where user_name = 'Adam'.
Could someone explain how to remove these single qoutes around the columns for me? When I hard code the columns I want to use, it gives me back my desired data but when I try to do it this way, it merely returns []. Any help is appreciated.
I have a working solution dealing with pymysql, which is to rewrite the escape method in class 'pymysql.connections.Connection', which obviously adds "'" arround your string. maybe you can try in a similar way, check this:
from pymysql.connections import Connection, converters
class MyConnect(Connection):
def escape(self, obj, mapping=None):
"""Escape whatever value you pass to it.
Non-standard, for internal use; do not use this in your applications.
"""
if isinstance(obj, str):
return self.escape_string(obj) # by default, it is :return "'" + self.escape_string(obj) + "'"
if isinstance(obj, (bytes, bytearray)):
ret = self._quote_bytes(obj)
if self._binary_prefix:
ret = "_binary" + ret
return ret
return converters.escape_item(obj, self.charset, mapping=mapping)
config = {'host':'', 'user':'', ...}
conn = MyConnect(**config)
cur = conn.cursor()
When I try to return the value at a specific position from my database and store the value to a text file I get the following error:
Argument must be a string or a number, not 'ResultProxy'.
int(expire) and str(expire) won't convert a 'ResultProxy'.
def expire():
today = datetime.date.today()
day = today.strftime('%d %b %y')
conn = engine.connect()
sql = text('select account.expire from account where account.user_name = "%s"'%('Bob'))
expire = conn.execute(sql)
filename = 'mysite/expiry.txt'
read = open(filename, 'r')
target = open(filename, 'w')
target.truncate()
target.write(str(expire))
target.write("\n")
target.close()
read = open(filename, 'r')
daysleft = read
return render_template('expire.html', daysleft=daysleft)
how do I convert the ResultProxy into a string?
Executing a query always returns a list of rows, a ResultProxy in SQLAlchemy's case. You are trying to write this result object to the file, rather than the actual result. Since it looks like you only expect one result, just make sure there's one result to write.
results = conn.execute(sql)
if results:
expire = results[0]
# write it to the file
Or if you expect multiple results, loop over them.
results = conn.execute(sql)
for expire in results:
# write it to the file
Here is my suggestion on how you can do it in Flask-SQLAlchemy.
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
Create the model that SQLAlchemy uses. I am assuming your table has 3 fields, a primary key, a user_name, and the expires field, which I assume is an integer from your use of this field as daysleft.
class Account(db.Model):
__tablename__ = 'account' # Sets the actual name of the table in the db
user_id = db.Column(db.String, primary_key=True)
user_name = db.Column(db.String)
expire = db.Column(db.Integer)
Here is your function that will use the model.
def expire():
today = datetime.date.today()
day = today.strftime('%d %b %y')
username = 'Bob'
Query the Account model (which is connected to the db via SQLAlchemy), filtering on the user_name field, and asking only for the first record returned, if any.
account = db.session(Account).filter_by(user_name=username).first()
filename = 'mysite/expiry.txt'
read = open(filename, 'r')
target = open(filename, 'w')
target.truncate()
If the previous query didn't return any rows, account will be None. Otherwise, it will be a dict with each returned field as a property. You can access the value of the expire field using account.expire.
if account != None:
target.write(str(account.expire))
target.write("\n")
target.close()
read = open(filename, 'r')
daysleft = read
return render_template('expire.html', daysleft=daysleft)