I'm trying to implement a REST API with Flask-RESTful. My API contains 4 requests: GET, POST, PUT, DELETE. Everything works perfectly except for the PUT request. It keeps returning Status Code 404, which the requested URL is not found. I'm using Postman to test my API. Here is my code. Can any show me where did I do it wrong? Thank you!
# Small API project using Flask-RESTful
from flask import Flask, request
from flask_restful import Resource, Api, reqparse
from flask_jwt import JWT, jwt_required
app = Flask(__name__)
# Encrypted key
app.secret_key = "Nam Dao"
api = Api(app)
students = []
class Student(Resource):
parser = reqparse.RequestParser()
parser.add_argument("major", type=str, required=True, help="This field cannot be left blank")
def get(self, name):
# filter function will return a filter object
# next function will get the "next" student in the filter object.
# if the Next function does not return anything => return None.
for student in students:
if student["name"] == name:
return student, 200
return {"message": "student not found"}, 404
def post(self, name):
if next(filter(lambda x: x["name"] == name, students), None) is not None:
return {"message": f"A student with the name {name} already exists"}, 400
request_data = self.parser.parse_args()
print(request_data)
student = {"name": name, "major": request_data["major"]}
students.append(student)
return student, 201
def delete(self, name):
for student in students:
if student["name"] == name:
students.remove(student)
return {"message": "Item deleted"}, 200
return {"message": "No student found"}, 204
def put(self, name):
data = request.get_json()
for student in students:
if student["name"] == name:
student["major"] = data["major"]
return {"message": "Student Major Changed"}, 200
student = {"name": data["name"], "major": data["major"]}
students.append(student)
return {"message": "Student Added"}, 200
api.add_resource(Student, "/student/<string:name>")
class StudentsList(Resource):
def get(self):
return {"students": students}, 200
api.add_resource(StudentsList, "/students")
if __name__ == '__main__':
app.run(debug=True)
I recheck my URL again and I was missing a letter, that's why it returns 404 Status Code. It was a very silly mistake. Everything works just fine
Related
i have a question regarding using pytest. These are my very 1st tests. I have 2 views which
i want to test (simplest possible way).
Views:
class MenuView(View):
def get(self, request):
return render(request, 'diet_app/menu.html')
class CuisineDetailsView(View):
def get(self, request, id):
cuisine = Cuisine.objects.get(id=id)
recipes = cuisine.recipe_set.all()
return render(request, 'diet_app/cuisine_details.html', {'cuisine': cuisine, 'recipes': recipes})
Here are my tests:
def test_menu(client):
url = reverse('menu')
response = client.get(url)
assert response.status_code == 200
#pytest.mark.django_db
def test_cuisine_details_view(client):
url = reverse('cuisine-details')
response = client.get(url)
assert response.status_code == 200
Urls:
path('menu/', MenuView.as_view(), name='menu'),
path('cuisine_details/<int:id>/', CuisineDetailsView.as_view(), name='cuisine-details'),
1st test (menu view) is working properly
2nd test (cuisine details view) shows error
.NoReverseMatch: Reverse for 'cuisine-details' with no arguments not found. 1 pattern(s) tried: ['cuisine_details\\/(?P<id>
I know i should probably put somethere ID argument but tried few options and havent succeed. Will be grateful for any help/advise
You must pass the id as argument to the reverse function.
#pytest.mark.django_db
def test_cuisine_details_view(client):
url = reverse('cuisine-details', kwargs={'id': 123})
response = client.get(url)
assert response.status_code == 200
I have this Flask View which takes a POST and GET requests
Goal is to do something with the Data from the POST request
and use it for the GET request
for example this AJAX GET Request
$.getJSON({url: '/uploadajax'}).done(result =>console.log(result));
which waits to return the processed data from the POST request
I was able to pass the data to the AJAX call by
declaring the global variable result and changed it in the function
and use it as a return value for the GET Request
Question here: is there a cleaner way to perform this task ?
result = 0
# ------------upload-file-----------------------------------------#
#flask_class.route('/uploadajax', methods=['POST', 'GET'])
def receave_file():
if request.method == 'POST':
uploaded_file = request.files['file']
# filename = secure_filename(uploaded_file.filename)
if uploaded_file.filename != "":
filename = secure_filename(uploaded_file.filename)
file_ext = os.path.splitext(filename)[1] # was macht das ?
if file_ext not in Config.ALLOWED_EXTENSIONS:
abort(400)
# file kann auch net gespeichert werden
uploaded_file.save(os.path.join(flask_class.instance_path, 'uploads', filename))
# ------------------------------------- #
df = pd.read_excel(uploaded_file)
columns = df.columns.to_list()
global result
result = json.dumps(columns)
# return result
print("shoud return somehting")
# ---------------------------------------- #
return '', 204
# ---------------------------------------- #
else:
return "false"
else:
# GET REQUEST
if len(result) > 1:
return result
else:
return '', 404
# return render_template('index.html')
Yes, there is :)
Have a look at the following code:
class LocalStore:
def __call__(self, f: callable):
f.__globals__[self.__class__.__name__] = self
return f
# ------------upload-file-----------------------------------------#
#flask_class.route('/uploadajax', methods=['POST', 'GET'])
#LocalStore() # creates store for this unique method only
def receave_file():
if request.method == 'POST':
LocalStore.post_headers= request.headers
LocalStore.post_body = request.body
LocalStore.post_json = request.get_json()
LocalStore.post_params = request.params
LocalStore.answer_to_everything = 42
print("POST request stored.")
return jsonify({"response": "Thanks for your POST!"})
else:
try:
print("This is a GET request.")
print("POST headers were:", LocalStore.post_headers)
print("POST params were :", LocalStore.post_params)
print("POST body was :", LocalStore.post_body)
print("The answer is :", LocalStore.answer_to_everything)
return jsonify({"postHeadersWere": LocalStore.post_headers})
except AttributeError:
return jsonify({"response":"You have to make a POST first!"})
I created a special class which "injects" its reference into the __globals__ dictionary of the method. If you type the class name in the method, it will be the object reference, not the class reference. Be aware of that!
You then just need to add #LocalStore underneath the #app.route(...) of your application because the store needs to be routed with the method...
I think it's a quite elegant way that saves you the definition of 5 global variables for 5 different methods
Attempting to check an in-memory list, plant_list[] against a JSON payload from an api.
If the incoming payload's dict name matches inside of plant_list the if should fire off.
Instead my script only returns null
Please point out my mistakes.
The JSON sent over the api call is:
{ "name":"swizz", "days": "7", "price": 2.00 }
Source Code
from flask import Flask, request
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
#app.route('/', methods=['GET', 'POST'])
def home():
return 'Tiny'
plant_list = []
class Plant(Resource):
def get(self, name):
return {'Name':name}, 200
def post(self, name):
payload = request.get_json()
for x in range(0, len(plant_list)):
if payload['name'] == plant_list[x]['name']:
return {'message': f'''Item {payload['name']} already stored in database.'''}
else:
plant_list.append(payload)
return plant_list, 201
api.add_resource(Plant, '/plant/<string:name>')
if __name__ == '__main__':
app.run(port=9004, debug=True)
You can test for key in dict simply by: if key_name in my_dict:... So, if "price" in plant_list[x]:
You are setting plant_list as a global. If you want to use that inside a Class, you should define it inside the function in your class:
plant_list = []
class Plant(Resource):
....
def post(self, name):
global plant_list
....
Not sure why you need it as a global variable. Perhaps you want:
class Plant(Resource):
plant_list = []
....
def post(self, name):
....
I am new to Flask and I need some help for my school work.
I am trying to build a simple ToDo list system using flask-restful.
My current code looks like this:
class ToDoList(Resource):
'''TODO LIST'''
operation = ['delete']
decorators = [auth.login_required, advertise('operation')]
def post(self):
"""remove all item in the TODO list"""
operation = request.args.get('op')
if operation == 'delete':
collection2.delete_many({})
return {'Success': 'OK'}, 200
return {'Error':'Illegal Operation'}, 400
def get(self):
"""return a list of the TODO name"""
list_1 = collection2.find()
list_2 = []
for each in list_1:
list_2.append(JSONEncoder().encode(each))
return {'list':list_2}, 200
It works, but I want only the post method to require authentication, and get method without authentication so anyone can acquire the list without login. I am using the flask-restful I don't know how to give the decorators separately to each function.
I used flaskrestplus to do basic authentication. All the required authorizations are provided as an authorizations dictionary. Then they are passed to the API.
Also the authorizations can be applied at the method level using
#api.doc(security='basicAuth')
The validation logic (can be ldap validation or db validation) can be writted in a decorator called requires_Auth. This decorator is invoked using
decorators = [requires_Auth]
Complete code
from flask import Flask, request
from flask_restplus import Api, Resource
from functools import wraps
def requires_Auth(f):
#wraps(f)
def decorator(*args, **kwargs):
auth = request.authorization
if auth:
print "inside decorator", auth.username,auth.password
return f(*args, **kwargs)
else:
return "Login required!!!!",401
return decorator
authorizations = {
'basicAuth': {
'type': 'basic',
'in': 'header',
'name': 'Authorization'
}
}
api = Api(app, version='1.0',
authorizations=authorizations
)
ns = api.namespace('/', description='Authentication API')
#ns.route('/withDecorator')
class HelloWorldWithDecorator(Resource):
decorators = [requires_Auth]
#api.doc(security='basicAuth')
def get(self):
return {'hello': 'world'}
api.add_namespace(ns)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5001)
From Flask-RESTful documentation [1]:
Alternatively, you can specify a dictionary of iterables that map to HTTP methods and the decorators will only apply to matching requests.
def cache(f):
#wraps(f)
def cacher(*args, **kwargs):
# caching stuff
return cacher
class MyResource(restful.Resource):
method_decorators = {'get': [cache]}
def get(self, *args, **kwargs):
return something_interesting(*args, **kwargs)
def post(self, *args, **kwargs):
return create_something(*args, **kwargs)
In your case it would be:
method_decorators = {'post': [auth.login_required]}
I learn unit test with Django. How to write test for this function? I need this example to understand.
#login_required
def datas(request):
queryset = Data.objects.filter(user=request.user)
if queryset.count() == 0:
return redirect('/data/')
return render_to_response('data_list.html',
{'data': queryset},
context_instance=RequestContext(request))
#imports here
class YourTestCase(TestCase):
fixtures = ['user-data.json']
def setUp(self):
self.client = Client()
def test_empty_datas(self):
self.client.login(username='something', password='something')
response = self.client.get('/path/to/view/') # or reverse by name
self.assertEqual(response.status_code, 302,
'View did not redirect on empty queryset.')
def test_populated_datas(self):
self.client.login(username='something', password='something')
Data.objects.create(some_field=some_value)
response = self.client.get('/path/to/view/') # or reverse by name
self.assertEqual(response.status_code, 200,
'View did not return a 200.')
...and so on. user-data would need to contain at least one user, otherwise you won't be able to authenticate.