I am trying to test a Flask script I have written, by adding, amending, and deleting information. However, if I run anything other than methods=["GET"], I get an error saying that the method is not allowed.
The script I am running is:
#!flask/bin/python
from flask import Flask, jsonify, request, abort, make_response
from flask_cors import CORS
import csv
stocks = [
{"id": 1, "Stock": "BoI", "Price": 300},
{"id": 2, "Stock": "Apple", "Price": 499}
]
Next_ID = len(stocks)
app = Flask(__name__, static_url_path="", static_folder=".")
CORS(app, support_credentials=True)
#app.route("/")
#app.route("/Stocks")
def getAll():
return jsonify(stocks)
# curl http://127.0.0.1:5000
#app.route("/Stocks/<int:id>")
def getById(id):
sel_stock = list(filter(lambda s: s["id"] == id, stocks))
if len(sel_stock) == 0:
return jsonify ({}), 204
else:
return jsonify(sel_stock[0])
# curl http://127.0.0.1:5000/1
#app.route("/Stocks", methods=["POST"])
def create(id):
global Next_ID
if not request.json:
abort(400)
stock = {
"id": Next_ID,
"Stock": request.json["Stock"],
"Price": request.json["Price"],
}
Next_ID += 1
stocks.append(stock)
return jsonify(stock)
# return str(Next_ID)
#app.route("/Stocks", methods=["PUT"])
def update(id):
sel_stock = list(filter(lambda s: s["id"] == id, stocks))
if len(sel_stock) == 0:
abort(400)
sel_stock = sel_stock[0]
if not request.json:
abort(400)
reqJSON = request.json
if reqJSON["Stock"]:
sel_stock["Stock"] = reqJSON["Stock"]
if reqJSON["Price"]:
sel_stock["Price"] = reqJSON["Price"]
return jsonify(sel_stock)
#app.route("/Stocks", methods=["DELETE"])
def delete(id):
return "in delete"
if __name__ == "__main__":
app.run(debug = True)
If I run curl commands to get all the information or get the information by ID number, there is no issue. However, If I try the following curl command I get an error:
curl -i -H "Content-Type:application/json" -X POST -d "{\"Stock\":\"IBM\",\"Price\":\123}" http://127.0.0.1:5000/Stocks
TypeError: create() missing 1 required positional argument: 'id'
You do not need to pass id to your POST method since its url does not have id as parameter:
#app.route("/Stocks", methods=["POST"])
def create(): # removed id
"""some code here"""
Related
I've below code of Flask calling FastAPI end point. But the response not getting generated(Error: 422) when the FastAPI end point is being invoked and FastAPI end point log says INFO:127.0.0.1:56904 - "POST /runquery HTTP/1.1" 422 Unprocessable Entity
Inputs to FASTAPI end point
query : select c2,c2,c4,c5 from table_tab
env= iAT
Flask Code
import requests
app = Flask(__name__)
#app.route("/", methods=["GET", "POST"])
def index():
query_id = ""
query = ""
status = ""
env = ""
if request.method == "POST":
query = request.form["query"]
env = request.form["env"]
data = {"query": query, "env": env}
print(data)
headers = {'Content-Type': 'application/json'}
**response = requests.post("http://localhost:8000/runquery", json=data,headers=headers)**
print(response)
if response.status_code != 200:
return f'Error: {response.status_code}'
else:
print(response.json())
response_data = response.json()
query_id = response_data["query_id"]
status = response_data["status"]
return render_template("index.html", query_id=query_id, query=query, status=status, env=env)
if __name__ == "__main__":
app.run(debug=True) ```
**Here is the FastAPI end point code**
``` #app.post("/runquery")
async def runquery(query: str, env: str):
print('.....')
delay = 60
parsed_query = sqlparse.parse(query)[0]
if parsed_query.get_type() == "SELECT":
pass
else:
return JSONResponse(content={"error": "Only SELECT queries are allowed"}, status_code=422)
# return {"error": "Only SELECT queries are allowed"}
registry_scheduled = ScheduledJobRegistry(queue=que)
registry_started = StartedJobRegistry(queue=que)
# registry_completed = FinishedJobRegistry(queue=que)
# registry_failed = FailedJobRegistry(queue=que)
if len(registry_scheduled.get_job_ids()) >= 2:
return JSONResponse(content={"Info": "GQs are already in-progress. Try after some time"})
# return {"Info": "GQs are already in-progress. Try after some time"}
if len(registry_started.get_job_ids()) >= 2:
return JSONResponse(content={"Info": "GQs are already in-progress.Try after some time"})
# return {"Info": "GQs are already in-progress.Try after some time"}
# Generate a unique ID for the query
query_id = str(uuid.uuid4())
print('query_id..........', query_id)
directory_path = os.path.join(home_path, query_id)
print(directory_path)
if not os.path.exists(directory_path):
os.makedirs(directory_path)
file_path = os.path.join(directory_path, "query.sql")
with open(file_path, "w") as f:
f.write(query)
# job = que.enqueue(exec_query, query, env, sqlfile)
job = que.enqueue_in(timedelta(seconds=delay), exec_query, query, env, query_id)
r.set(query_id, job.id)
# Return the query ID to the user
return JSONResponse(content={"query_id": query_id, "query": query, "stauts": "OK"},
status_code=202) ```
422 is Unprocessable Entity, it means that your request sent has wrong format parameter in the body. You must use Pydantic class to validate json.
Change FastAPI endpoint in this way :
from pydantic import BaseModel
class Data(BaseModel):
query: str
env: str
#app.post("/runquery")
async def runquery(data: Data):
...
I am working with Postman for the first time and GET request to my simple API is working fine, but when I try POST request it gives me an error and I have no idea where is the problem. Can you please advise?
API function:
#app.route('/customer', methods=['POST'])
def create_customer():
request_data = request.get_json()
new_customer = {
"email": request_data['email'],
"username": request_data['username'],
"name": request_data['name'],
"newsletter_status": request_data['newsletter_status'],
"trips": []
}
for customer in customers:
if customer['username'] == new_customer['username']:
return jsonify({'error': 'username already exist'})
customers.append(new_customer)
return jsonify(new_customer)
Screenshots from postman
This I put in the body + error message
Headers set up - Content-Type application/json
I think your new customer variables should not be in string format, I don't know why they are and also try my option for getting the request body:
import json
def create_customer():
request_data = json.loads(request.body)
new_customer = {
email = request_data['email'],
username = request_data['username'],
name = request_data['name'],
newsletter_status = request_data['newsletter_status'],
trips: []
}
for customer in customers:
if customer['username'] == new_customer['username']:
return jsonify({'error': 'username already exist'})
customers.append(new_customer)
return jsonify(new_customer)
Yur code works fine , i just tested with postman:
from flask import jsonify
from flask import Flask
from flask import request
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello, World!'
#app.route('/customer', methods=['POST'])
def create_customer():
customers=[]
customers.append(request.get_json())
request_data = request.get_json()
new_customer = {
"email": request_data['email'],
"username": request_data['username']+"HHH",
"name": request_data['name'],
"newsletter_status": request_data['newsletter_status'],
"trips": []
}
print(new_customer['username'])
print(customers[0]['username'])
for customer in customers:
if customer['username'] == new_customer['username']:
return jsonify({'error': 'username already exist'})
customers.append(new_customer)
return jsonify(new_customer)
Can you check what is exactly send in postman ?
you can do it by clicking console ( can get from left botom corner or by pressing ctrl+alt+c ) >request > requestbody
I'm working on a project using Python(3.7) and Falsk, in which I'm implementing testing for my API. For a POST request, when I submit the request client using test_client with the proper payload it returns an error as:
AttributeError: 'Response' object has no attribute 'items'
Here's what I have done so far:
From view function:
if request.method == "POST":
data = request.get_json()
new_author = data['author']
new_title = data["title"]
new_sDescription = data["shortDescription"]
new_Url = data["thumbnailUrl"]
new_status = data["status"]
new_pageCount = data["pageCount"]
....
if None not in (new_author, new_title, new_Url, new_status, new_pageCount
, new_sDescription):
return f"Book with the id: {cursor.lastrowid} created successfully", 201
** Test Fixture:**
#pytest.fixture
def client():
app.config['TESTING'] = True
db_fd, app.config['DATABASE'] = tempfile.mkstemp()
with app.test_client() as client:
with app.app_context():
create_table(app.config['DATABASE'])
yield client
** Function to send request:**
def api_post_book(client):
payload = {
"author": "Someone",
"pageCount": 590,
"shortDescription": "A book from PyTest.",
"status": "PUBLISH",
"thumbnailUrl": "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/adzic.jpg",
"title": "PyTest is a Fun."
}
return client.post('/api',
data=jsonify(payload), follow_redirects=True)
** Function to test: **
def test_api_post_book(client):
resp = api_post_book(client)
assert resp.status_code == 201
If I remove jsonify it returns another error as:
TypeError: 'NoneType' object is not subscriptable
This should work:
return client.post('/api', json=payload, follow_redirects=True)
Or if you absolutely want to use jsonify, use:
return client.post('/api', json=jsonify(payload).get_json(), follow_redirects=True)
You need to keep in mind that jsonify returns a Response object and not json.
I'm trying to create a simple API and server with MongoDB and Flask in python(pycharm). I'm testing the methods with Postman and so far the GET and DELETE methods work. I'm having troubles with the POST method mainly(for adding an entity). I have 2 classes
repository class
class ExercisesRepository:
def __init__(self):
self.client = MongoClient('localhost', 27017)
self.client.drop_database("exercise_database") # clear everything that was before
self.db = self.client.exercise_database # create database
self.exercises = self.db.exercises # create table in the database
def get_all(self):
return [{
'id': str(exercise['_id']),
'type': exercise['type'],
'calories': exercise['calories']
} for exercise in self.exercises.find()]
def add(self, exercise):
exercise = {key: exercise[key] for key in exercise}
exercise['calories'] = int(exercise['calories']) #line 24
self.exercises.insert_one(exercise) # automatically generates an ObjectId for the exercise
return 200
def update(self, exercise_id, exercise):
my_query = {"_id": ObjectId(exercise_id)}
new_values = {"$set": {"type": exercise["type"], "calories": exercise["calories"]}}
self.exercises.update_one(my_query, new_values)
return 200
def delete(self, exercise_id):
self.exercises.remove(ObjectId(exercise_id))
return 200
def check_database_content(self):
for exercise in self.exercises.find():
pprint.pprint(exercise)
server class
from ExercisesRepository import ExercisesRepository
from flask import Flask
from flask import request
from flask import jsonify
import sys
app = Flask(__name__)
exerciseRepo = ExercisesRepository()
exerciseRepo.add({'type': 'Yoga', 'calories': 500})
exerciseRepo.add({'type': 'Walk', 'calories': 300})
exerciseRepo.add({'type': 'Run', 'calories': 100})
#app.route('/')
def hello_world():
return 'Hello World!'
#app.route("/exercises", methods=['GET', 'POST'])
def exercises():
if request.method == 'GET':
return jsonify(exerciseRepo.get_all())
elif request.method == 'POST':
print(request.form, file=sys.stderr)
return jsonify(exerciseRepo.add(request.form)) #line 31
#app.route('/exercises/<exercise_id>', methods=['PUT', 'DELETE'])
def exercises_id(exercise_id):
if request.method == 'PUT':
print(request.form, file=sys.stderr)
return jsonify(exerciseRepo.update(exercise_id, request.form))
elif request.method == 'DELETE':
return jsonify(exerciseRepo.delete(exercise_id))
if __name__ == '__main__':
app.run()
When I try to make a POST call in postman with a JSON like this :
{
"type": "Aerobic",
"calories": 500
} I get the following message in postman: 500 Internal Server Error
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
and in Pycharm console:
File "server.py", line 31, in exercises
return jsonify(exerciseRepo.add(request.form))
server\ExercisesRepository.py", line 24, in add
exercise['calories'] = int(exercise['calories'])
KeyError: 'calories'
127.0.0.1 - - [05/Jan/2020 13:01:50] "POST /exercises HTTP/1.1" 500 -
I'm pretty new to python and this is my first try to make an api so if you could explain as much as possible it would be very helpful. Thanks!
If you send data as JSON then you have to get it using request.json, not request.form
from flask import Flask, request, jsonify
app = Flask(__name__)
#app.route("/exercises", methods=['GET', 'POST'])
def exercises():
print('form:', request.form)
print('json:', request.json)
return jsonify(request.json) #line 31
if __name__ == '__main__':
app.run()
When you send as JSON
import requests
r = requests.post('http://localhost:5000/exercises', json={'type': 'Run', 'calories': 100})
then server shows
form: ImmutableMultiDict([])
json: {'type': 'Run', 'calories': 100}
When you send as form data
import requests
r = requests.post('http://localhost:5000/exercises', data={'type': 'Run', 'calories': 100})
then server shows
form: ImmutableMultiDict([('type', 'Run'), ('calories', '100')])
json: None
form sends data as string type=Run&calories=100 in body,
json sends data as string {'type': 'Run', 'calories': 100} in body.
You can see it if you display request's body
import requests
r = requests.post('https://httpbin.org/post', data={'type': 'Run', 'calories': 100})
print(r.request.body)
r = requests.post('https://httpbin.org/post', json={'type': 'Run', 'calories': 100})
print(r.request.body)
Result
type=Run&calories=100
b'{"type": "Run", "calories": 100}'
This question already has answers here:
How to send requests with JSON in unit tests
(2 answers)
Closed 4 years ago.
I am trying to write a test case for my post end point, but I seriously do not know what I am doing wrong.
views.py
The user can post a product by typing the name, the price and category I have hard coded:
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
products = []
class Product(Resource):
def post(self, name):
if next(filter(lambda x: x['name'] == name, products), None):
return {'message':
"A product with name '{}' already exists."
.format(name)}, 400
data = request.get_json()
product = {'id': len(products) + 1,
'name': name, 'price': data['price'],
'category': data['category']}
products.append(product)
return product, 201
When I run it I get the JSON data with a 201 OK response.
{
"price": 50,
"category": "stationery",
"name": "pencil",
"id": 2
}
But when I test it:
test_product.py
import unittest
import json
from app import create_app
class ProductTestCase(unittest.TestCase):
"""This is the class for product test cases"""
def setUp(self):
self.app = create_app('testing')
self.client = self.app.test_client
def test_to_add_product(self):
"""Test method to add product"""
"""Is this how the json payload is passed"""
add_product = self.client().post('/api/v1/product/pencil',
data={
"price": 50,
"name": "pencil",
"category": "stationery"
},
content_type='application/json')
self.assertEqual(add_product.status_code, 201)
I get the following error:
E AssertionError: 400 != 201
app/tests/v1/test_product.py:22: AssertionError
What does this mean, and how can I fix it?
EDIT
folowing this answer,How to send requests with JSONs in unit tests, this worked for me:
def test_to_add_product(self):
"""Test method to add product"""
add_product = self.client().post('/api/v1/product/pencil',
data=json.dumps(
dict(category='category',
price='price')),
content_type='application/json')
self.assertEqual(add_product.status_code, 201)
HTTP 400 means "bad request". It indicates that the request you're sending to your API endpoint is somehow invalid.
I don't see where you're passing any JSON data to your endpoint in your test. If JSON data is required (it appears to be since you have data = request.get_json() in your code) that would explain the bad request response.
If you include a valid JSON payload in your test you should see the response you expect.