Skip to content

Instantly share code, notes, and snippets.

@miguelgrinberg
Last active February 12, 2025 21:09
Show Gist options
  • Save miguelgrinberg/5614326 to your computer and use it in GitHub Desktop.
Save miguelgrinberg/5614326 to your computer and use it in GitHub Desktop.
The code from my article on building RESTful web services with Python and the Flask microframework. See the article here: http://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask
#!flask/bin/python
from flask import Flask, jsonify, abort, request, make_response, url_for
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__, static_url_path = "")
auth = HTTPBasicAuth()
@auth.get_password
def get_password(username):
if username == 'miguel':
return 'python'
return None
@auth.error_handler
def unauthorized():
return make_response(jsonify( { 'error': 'Unauthorized access' } ), 403)
# return 403 instead of 401 to prevent browsers from displaying the default auth dialog
@app.errorhandler(400)
def bad_request(error):
return make_response(jsonify( { 'error': 'Bad request' } ), 400)
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify( { 'error': 'Not found' } ), 404)
tasks = [
{
'id': 1,
'title': u'Buy groceries',
'description': u'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': u'Learn Python',
'description': u'Need to find a good Python tutorial on the web',
'done': False
}
]
def make_public_task(task):
new_task = {}
for field in task:
if field == 'id':
new_task['uri'] = url_for('get_task', task_id = task['id'], _external = True)
else:
new_task[field] = task[field]
return new_task
@app.route('/todo/api/v1.0/tasks', methods = ['GET'])
@auth.login_required
def get_tasks():
return jsonify( { 'tasks': map(make_public_task, tasks) } )
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods = ['GET'])
@auth.login_required
def get_task(task_id):
task = filter(lambda t: t['id'] == task_id, tasks)
if len(task) == 0:
abort(404)
return jsonify( { 'task': make_public_task(task[0]) } )
@app.route('/todo/api/v1.0/tasks', methods = ['POST'])
@auth.login_required
def create_task():
if not request.json or not 'title' in request.json:
abort(400)
task = {
'id': tasks[-1]['id'] + 1,
'title': request.json['title'],
'description': request.json.get('description', ""),
'done': False
}
tasks.append(task)
return jsonify( { 'task': make_public_task(task) } ), 201
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods = ['PUT'])
@auth.login_required
def update_task(task_id):
task = filter(lambda t: t['id'] == task_id, tasks)
if len(task) == 0:
abort(404)
if not request.json:
abort(400)
if 'title' in request.json and type(request.json['title']) != unicode:
abort(400)
if 'description' in request.json and type(request.json['description']) is not unicode:
abort(400)
if 'done' in request.json and type(request.json['done']) is not bool:
abort(400)
task[0]['title'] = request.json.get('title', task[0]['title'])
task[0]['description'] = request.json.get('description', task[0]['description'])
task[0]['done'] = request.json.get('done', task[0]['done'])
return jsonify( { 'task': make_public_task(task[0]) } )
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods = ['DELETE'])
@auth.login_required
def delete_task(task_id):
task = filter(lambda t: t['id'] == task_id, tasks)
if len(task) == 0:
abort(404)
tasks.remove(task[0])
return jsonify( { 'result': True } )
if __name__ == '__main__':
app.run(debug = True)
@alirezaAsadi2018
Copy link

alirezaAsadi2018 commented Feb 25, 2022

@miguelgrinberg Thnks for ur great job.
I think you'd better rename this function name to sth better as it conflicts with this function name.
I suggest using def bad_request(error) instead.

@Antoh1
Copy link

Antoh1 commented Nov 12, 2022

Awesome step by step tutorial, Thank you miguel.

@EljonesA
Copy link

Thank you. exactly what i needed as a complete beginner

yeah, right guide.

@DimitriiVM
Copy link

Не работает ваш код, есть ошибка:
if len(task) == 0:
TypeError: object of type 'filter' has no len()
Нужно преобразовать в список и потом проверять пустой ли он

@msichal132
Copy link

Can you please help me with multiple parallel requests?
I can call many requests at once but only 5 or 6 are processed at a time.

from flask import Flask
import asyncio
app = Flask(__name__, static_url_path = "")

delaycounter=0
@app.route('/delay')
async def test_route():
    global delaycounter
    print(f"start {delaycounter}")
    delaycounter=delaycounter+1
    await asyncio.sleep(5)
    print(f"end{delaycounter}")
    return "ok"
    
if __name__ == '__main__':
    app.run(debug = True, port=8080)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment