Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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.ext.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 not_found(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)
@DeepakDinakaran22

This comment has been minimized.

Show comment
Hide comment
@DeepakDinakaran22

DeepakDinakaran22 Feb 4, 2016

Very useful...Thanks

Very useful...Thanks

@girisagar46

This comment has been minimized.

Show comment
Hide comment

Thanks :)

@nmud19

This comment has been minimized.

Show comment
Hide comment
@nmud19

nmud19 Mar 4, 2016

Thanks man!

nmud19 commented Mar 4, 2016

Thanks man!

@Rsimpossible

This comment has been minimized.

Show comment
Hide comment
@Rsimpossible

Rsimpossible Apr 27, 2016

I am getting error while executing app.py

C:\Python27\todo-api\flask>python app.py
Traceback (most recent call last):
File "app.py", line 3, in
from flask.ext.httpauth import HTTPBasicAuth
File "C:\Python27\Scripts\flask\lib\site-packages\flask\exthook.py", line 87, in load_module
raise ImportError('No module named %s' % fullname)
ImportError: No module named flask.ext.httpauth

I am getting error while executing app.py

C:\Python27\todo-api\flask>python app.py
Traceback (most recent call last):
File "app.py", line 3, in
from flask.ext.httpauth import HTTPBasicAuth
File "C:\Python27\Scripts\flask\lib\site-packages\flask\exthook.py", line 87, in load_module
raise ImportError('No module named %s' % fullname)
ImportError: No module named flask.ext.httpauth

@matrixfox

This comment has been minimized.

Show comment
Hide comment
@matrixfox

matrixfox Apr 28, 2016

@Rsimpossible What does that error message say?

flask/bin/pip install flask-httpauth
https://github.com/miguelgrinberg/flask-httpauth

@Rsimpossible What does that error message say?

flask/bin/pip install flask-httpauth
https://github.com/miguelgrinberg/flask-httpauth

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost May 15, 2016

@app.errorhandler(400)
def not_found(error):
return make_response(jsonify( { 'error': 'Bad request' } ), 400)

@app.errorhandler(404)
def not_found(error):
return make_response(jsonify( { 'error': 'Not found' } ), 404)

in the above code function name is same for both 400 and 404.

ghost commented May 15, 2016

@app.errorhandler(400)
def not_found(error):
return make_response(jsonify( { 'error': 'Bad request' } ), 400)

@app.errorhandler(404)
def not_found(error):
return make_response(jsonify( { 'error': 'Not found' } ), 404)

in the above code function name is same for both 400 and 404.

@somukempraj

This comment has been minimized.

Show comment
Hide comment
@somukempraj

somukempraj May 20, 2016

Very helpful code and thanks .I'm getting "error": "Unauthorized access" in the browser while I run rest-server.py .Can you help me get rid of this please?

Very helpful code and thanks .I'm getting "error": "Unauthorized access" in the browser while I run rest-server.py .Can you help me get rid of this please?

@shivam-kotwalia

This comment has been minimized.

Show comment
Hide comment
@shivam-kotwalia

shivam-kotwalia Jul 18, 2016

Thanks miguelgrinberg for this wonderful code !!

For other how are getting the error for flask.ext.httpauth is depricated
So just change flask.ext.httpauth to flask_httpauth in line number 3

Thanks miguelgrinberg for this wonderful code !!

For other how are getting the error for flask.ext.httpauth is depricated
So just change flask.ext.httpauth to flask_httpauth in line number 3

@asavalia

This comment has been minimized.

Show comment
Hide comment
@asavalia

asavalia Sep 5, 2016

Really, it's very helpfull... 👍

asavalia commented Sep 5, 2016

Really, it's very helpfull... 👍

@SandhiyaSelvaraj

This comment has been minimized.

Show comment
Hide comment
@SandhiyaSelvaraj

SandhiyaSelvaraj Sep 19, 2016

Thank you so much Man !!!! It's really helpful !!!

Thank you so much Man !!!! It's really helpful !!!

@Muthama-Kahohi

This comment has been minimized.

Show comment
Hide comment
@Muthama-Kahohi

Muthama-Kahohi Jan 10, 2017

This was super helpful. It got me acquainted with api using flask very easily. Keep up the good work Miguel

This was super helpful. It got me acquainted with api using flask very easily. Keep up the good work Miguel

@rishabhricky

This comment has been minimized.

Show comment
Hide comment
@rishabhricky

rishabhricky Mar 21, 2017

How can I convert my code in API : Please follow this link I have posted detailed question on Stack Overflow

http://stackoverflow.com/questions/42928251/making-my-python-code-as-an-restful-api-python-flask

How can I convert my code in API : Please follow this link I have posted detailed question on Stack Overflow

http://stackoverflow.com/questions/42928251/making-my-python-code-as-an-restful-api-python-flask

@Project-MAR

This comment has been minimized.

Show comment
Hide comment
@Project-MAR

Project-MAR Mar 24, 2017

Great guide, Thank you very much.

Great guide, Thank you very much.

@vboyk

This comment has been minimized.

Show comment
Hide comment
@vboyk

vboyk Apr 26, 2017

flask.ext.httpauth package is marked as deprecated (Python 3.6). Use flask_httpauth instead:
from flask_httpauth import HTTPBasicAuth

vboyk commented Apr 26, 2017

flask.ext.httpauth package is marked as deprecated (Python 3.6). Use flask_httpauth instead:
from flask_httpauth import HTTPBasicAuth

@4admin2root

This comment has been minimized.

Show comment
Hide comment

just I want

@savanmorya

This comment has been minimized.

Show comment
Hide comment
@savanmorya

savanmorya May 29, 2017

very easy to understand, best for beginners

savanmorya commented May 29, 2017

very easy to understand, best for beginners

@vrangasayee

This comment has been minimized.

Show comment
Hide comment
@vrangasayee

vrangasayee Jun 1, 2017

Awesome writeup for beginners. I was trying to teach my son to write a restful api. I am definitely going to point him to this.

Awesome writeup for beginners. I was trying to teach my son to write a restful api. I am definitely going to point him to this.

@robinvd

This comment has been minimized.

Show comment
Hide comment
@robinvd

robinvd Aug 2, 2017

on python36

return jsonify( { 'tasks': map(make_public_task, tasks) } )

has type 'map' and get_tasks(), panics with

  File "/nix/store/mhmw7rqpvr7vg16z4gz3wan58bxbk2an-python3-3.6.1/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'map' is not JSON serializable

any fix for this?

robinvd commented Aug 2, 2017

on python36

return jsonify( { 'tasks': map(make_public_task, tasks) } )

has type 'map' and get_tasks(), panics with

  File "/nix/store/mhmw7rqpvr7vg16z4gz3wan58bxbk2an-python3-3.6.1/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'map' is not JSON serializable

any fix for this?

@MarcAragones

This comment has been minimized.

Show comment
Hide comment
@MarcAragones

MarcAragones Oct 27, 2017

Hey @robinvd, I fixed the issue by wrapping map with list:

return jsonify( { 'tasks': list(map(make_public_task, tasks)) } )

And the same happens with filter:

task = list(filter(lambda t: t['id'] == task_id, tasks))

MarcAragones commented Oct 27, 2017

Hey @robinvd, I fixed the issue by wrapping map with list:

return jsonify( { 'tasks': list(map(make_public_task, tasks)) } )

And the same happens with filter:

task = list(filter(lambda t: t['id'] == task_id, tasks))
@SonawaneMayur

This comment has been minimized.

Show comment
Hide comment
@SonawaneMayur

SonawaneMayur Jan 4, 2018

Really useful article. Thanks.

Really useful article. Thanks.

@JackOHara

This comment has been minimized.

Show comment
Hide comment
@JackOHara

JackOHara Feb 8, 2018

Great stuff. To the point. Cheers!

Great stuff. To the point. Cheers!

@trssssrt

This comment has been minimized.

Show comment
Hide comment
@trssssrt

trssssrt Mar 2, 2018

Great tutorial. This code does give an error, but it may be related to the newest version of python.

The fixes are easy though:

  1. the map fails on line 54. So change it to your suggestion on the blog post: return jsonify({'tasks': [make_public_task(task) for task in tasks]})
    Or use @MarcAragones solution above.
  2. changing unicode to str (Python 3.6 made this change)

And with those modifications, the code works!

trssssrt commented Mar 2, 2018

Great tutorial. This code does give an error, but it may be related to the newest version of python.

The fixes are easy though:

  1. the map fails on line 54. So change it to your suggestion on the blog post: return jsonify({'tasks': [make_public_task(task) for task in tasks]})
    Or use @MarcAragones solution above.
  2. changing unicode to str (Python 3.6 made this change)

And with those modifications, the code works!

@bjcom1299

This comment has been minimized.

Show comment
Hide comment
@bjcom1299

bjcom1299 Jun 8, 2018

When I try to test POST method by curl, I get the result:

HTTP/1.0 405 METHOD NOT ALLOWED
Content-Type: text/html
Allow: HEAD, OPTIONS, GET

what's wrong with me? Please help.

Thanks.

When I try to test POST method by curl, I get the result:

HTTP/1.0 405 METHOD NOT ALLOWED
Content-Type: text/html
Allow: HEAD, OPTIONS, GET

what's wrong with me? Please help.

Thanks.

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