Recommendations of Python REST (web services) framework?

241,263

Solution 1

Something to be careful about when designing a RESTful API is the conflation of GET and POST, as if they were the same thing. It's easy to make this mistake with Django's function-based views and CherryPy's default dispatcher, although both frameworks now provide a way around this problem (class-based views and MethodDispatcher, respectively).

HTTP-verbs are very important in REST, and unless you're very careful about this, you'll end up falling into a REST anti-pattern.

Some frameworks that get it right are web.py, Flask and Bottle. When combined with the mimerender library (full disclosure: I wrote it), they allow you to write nice RESTful webservices:

import web
import json
from mimerender import mimerender

render_xml = lambda message: '<message>%s</message>'%message
render_json = lambda **args: json.dumps(args)
render_html = lambda message: '<html><body>%s</body></html>'%message
render_txt = lambda message: message

urls = (
    '/(.*)', 'greet'
)
app = web.application(urls, globals())

class greet:
    @mimerender(
        default = 'html',
        html = render_html,
        xml  = render_xml,
        json = render_json,
        txt  = render_txt
    )
    def GET(self, name):
        if not name: 
            name = 'world'
        return {'message': 'Hello, ' + name + '!'}

if __name__ == "__main__":
    app.run()

The service's logic is implemented only once, and the correct representation selection (Accept header) + dispatch to the proper render function (or template) is done in a tidy, transparent way.

$ curl localhost:8080/x
<html><body>Hello, x!</body></html>

$ curl -H "Accept: application/html" localhost:8080/x
<html><body>Hello, x!</body></html>

$ curl -H "Accept: application/xml" localhost:8080/x
<message>Hello, x!</message>

$ curl -H "Accept: application/json" localhost:8080/x
{'message':'Hello, x!'}

$ curl -H "Accept: text/plain" localhost:8080/x
Hello, x!

Update (April 2012): added information about Django's class-based views, CherryPy's MethodDispatcher and Flask and Bottle frameworks. Neither existed back when the question was asked.

Solution 2

Surprised no one mentioned flask.

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

Solution 3

We're using Django for RESTful web services.

Note that -- out of the box -- Django did not have fine-grained enough authentication for our needs. We used the Django-REST interface, which helped a lot. [We've since rolled our own because we'd made so many extensions that it had become a maintenance nightmare.]

We have two kinds of URL's: "html" URL's which implement the human-oriented HTML pages, and "json" URL's which implement the web-services oriented processing. Our view functions often look like this.

def someUsefulThing( request, object_id ):
    # do some processing
    return { a dictionary with results }

def htmlView( request, object_id ):
    d = someUsefulThing( request, object_id )
    render_to_response( 'template.html', d, ... )

def jsonView( request, object_id ):
    d = someUsefulThing( request, object_id )
    data = serializers.serialize( 'json', d['object'], fields=EXPOSED_FIELDS )
    response = HttpResponse( data, status=200, content_type='application/json' )
    response['Location']= reverse( 'some.path.to.this.view', kwargs={...} )
    return response

The point being that the useful functionality is factored out of the two presentations. The JSON presentation is usually just one object that was requested. The HTML presentation often includes all kinds of navigation aids and other contextual clues that help people be productive.

The jsonView functions are all very similar, which can be a bit annoying. But it's Python, so make them part of a callable class or write decorators if it helps.

Solution 4

See Python Web Frameworks wiki.

You probably do not need the full stack frameworks, but the remaining list is still quite long.

Solution 5

Take a look at

Share:
241,263
McArthey
Author by

McArthey

Updated on December 23, 2020

Comments

  • McArthey
    McArthey over 3 years

    Is there a list somewhere of recommendations of different Python-based REST frameworks for use on the serverside to write your own RESTful APIs? Preferably with pros and cons.

    Please feel free to add recommendations here. :)

  • Asela Liyanage
    Asela Liyanage almost 15 years
    That's a nice example, but there's nothing RESTful about it.
  • Asela Liyanage
    Asela Liyanage almost 15 years
    This is incorrect, Django has full support for recognizing POST vs GET and limiting views to only certain methods.
  • Martin Blech
    Martin Blech over 14 years
    I meant that, by default, Django treats POST and GET as if they were the same thing, which is very inconvenient when you are doing RESTful services as it forces you to do: if request.method == 'GET': do_something() elif request.method == 'POST': do_something_else() web.py doesn't have that problem
  • Martin Blech
    Martin Blech over 14 years
    @Wahnfrieden: If there is native support in Django for handling different HTTP verbs separately (by "native" I mean not needing "if request.method==X"), could you please point me to some documentation?
  • lilbyrdie
    lilbyrdie over 14 years
    @Wahnfrieden: Could you help the rest of us out by clarifying why you do not think the above is RESTful? From my point of view, it looks like a classic example of REST and doesn't appear to break any of the rules or constraints of a RESTful system.
  • verveguy
    verveguy over 14 years
    In simple terms, what the CherryPy example above is doing is exposing methods as "HTTP callable" remote procedures. That's RPC. It's entirely "verb" oriented. RESTful architectures focus on the resources managed by a server and then offer a very limited set of operations on those resources: specifically, POST (create), GET (read), PUT (update) and DELETE (delete). The manipulation of these resources, in particular changing their state via PUT, is the key pathway whereby "stuff happens".
  • Andriy Drozdyuk
    Andriy Drozdyuk about 14 years
    I separated my rest-api into a different app (in django settings INSTALLED_APPS). Keeps you from having to do this weird view naming...
  • temoto
    temoto almost 14 years
    Awful repetition of d = someUsefulThing... Even Django guys suggest DRY.
  • user1066101
    user1066101 almost 14 years
    @temoto: If y = someUsefulThing(...) is an "Awful repetition", then all references to all functions and methods is "awful". I fail to understand how to avoid referencing a function more than once.
  • temoto
    temoto almost 14 years
    Please, be fair. I wasn't talking about function reference only. I quoted the statement, which includes expression which includes arguments. When you need to change arguments passed to someUsefulThing, there's a chance that one forgets to do so in all calls.
  • user1066101
    user1066101 almost 14 years
    @temoto: "When you need to change arguments passed to someUsefulThing, there's a chance that one forgets to do so in all calls"? What? How is that "awful"? That's a trivial consequence of referencing a function more than once. I'm failing to understand what you're talking about and how function reference is "awful" since it's inescapable.
  • temoto
    temoto almost 14 years
    See the accepted answer. The result expression {'message': 'Hello, ' + name + '!'} is written once for all presentations.
  • user1066101
    user1066101 almost 14 years
    @temoto: what? I can't understand your point. How is referencing a function more than once "awful". The code written "once and for all" uses the + function two times. Isn't that just as "awful" as using someUsefulThing two times? I cannot understand what distinction you are making. Can you clarify, please?
  • temoto
    temoto almost 14 years
    Your htmlView and jsonView functions serve different representations for same data, right? So someUsefulThing(request, object_id) is a data retrieval expression. Now you have two copies of same expression in different points in your program. In the accepted answer, the data expression is written once. Replace your someUsefulThing call with a long string, like paginate(request, Post.objects.filter(deleted=False, owner=request.user).order_by('comment_count')) and look at the code. I hope it will illustrate my point.
  • user1066101
    user1066101 almost 14 years
    @temoto: I guess you object to referencing some functions more than once. I can't understand that. Somehow someUsefulThing has a special status what + does not have. This appears to be based on the length of the name or the number of arguments or some other criteria which I can't discern.
  • temoto
    temoto almost 14 years
    Did you try to write that long expression? (twice in sibling functions) ?
  • user1066101
    user1066101 almost 14 years
    @temoto: Using a function more than once means that sometimes, the name of the function (however long) must get repeated. That's basically how function references work. The name gets repeated. As I showed in my answer, two places can reference a function and the name will get repeated. I understand that you object to this, but my example shows repeating a long expression. I can't find a problem with it because I don't know what the problem is.
  • temoto
    temoto almost 14 years
    Okay, i guess it needs paper and visual contact to explain my point.
  • user1066101
    user1066101 almost 14 years
    @temoto: All it needs is a clear definition of why function references are so evil. someUsefulThing cannot be used twice. + can be used twice. Please provide some distinction that allows be to determine what functions can be used twice and what functions can't be used twice. If it's so complex that it can't be defined in a sentence, then post it to your blog.
  • temoto
    temoto almost 14 years
    You keep repeating that it's about function references. I keep repeating that it's not. Yes, many function references is fine (like we use unicode and min so many times, it's fine). Many duplicate expressions is not (like repeating "debug: " + str(this), this is evil). Do you understand difference between str and "debug: " + str(foo) ?
  • user1066101
    user1066101 almost 14 years
    @temoto: "Do you understand difference between str and "debug: " + str(foo) ?" Apparently not. Somehow one expression is bad and one expression is good. I cannot see what rule or metric or principle separates one from the other. They're expressions. One expression is acceptable to you and one is not acceptable to you. I cannot discern how you are making this decision. I cannot parse any rule -- it appears like a random decision to me. Please provide a clear definition that I can use without resorting to asking you which is better.
  • temoto
    temoto almost 14 years
    str does not evaluate to useful expression. str(arguments) does. In some other language (i.e. C or Java) the former would not even be an expression.
  • ncoghlan
    ncoghlan over 12 years
    The conflation of POST and GET does not apply to Django's Class Based Views (added in 1.3), but I believe is valid for the earlier releases.
  • Derek Litz
    Derek Litz over 12 years
    Answer is incorrect about CherryPy. From Docs: "REST (Representational State Transfer) is an architectural style that is well-suited to implementation in CherryPy." - docs.cherrypy.org/dev/progguide/REST.html
  • milovanderlinden
    milovanderlinden about 12 years
    The links mentioned are no longer available
  • Anthony
    Anthony about 12 years
    The links have been updated - try again.
  • Buttons840
    Buttons840 about 12 years
    Many of the top answers to this question provide examples. An example would be nice for this answer.
  • Petrus Theron
    Petrus Theron about 12 years
    Can you decouple the route configuration ('/') from the function like in web.py?
  • dumbmatter
    dumbmatter about 12 years
  • Martin Blech
    Martin Blech about 12 years
    Flask wasn't out there when the question was asked...
  • Chris Morgan
    Chris Morgan about 12 years
    Django's class-based views which were introduced in 1.3 deal with method differentiation well.
  • Martin Blech
    Martin Blech about 12 years
    @ChrisMorgan: got it, I have updated the answer to reflect this new feature.
  • michel.iamit
    michel.iamit over 11 years
    Especially the browesable interface is saving a lot of time while developing! Many other advantages, so everyone starting rest implementation should have a look. I started with tastypie, but switched completely to django-rest-framework
  • tabdulradi
    tabdulradi over 11 years
    You can write more RESTfull APIs using CherryPy docs.cherrypy.org/stable/progguide/REST.html
  • Alex Bitek
    Alex Bitek about 11 years
    Flask doesn't work with Python 3.x
  • eric.frederich
    eric.frederich about 11 years
    @MartinBlech would you mine updating this example so it works with your latest from Git?
  • Sean Vieira
    Sean Vieira almost 11 years
    Flask.dev now supports Python 3
  • Asken
    Asken over 10 years
    @MartinBlech I can't get it to work either. Adding @mimerender (... gives me mimerender is not callable
  • mb21
    mb21 over 10 years
    Flask supports Python 3.3 or higher.
  • mVChr
    mVChr over 10 years
    I had to create a complete REST API in a single night for a project I was working on after already working a full day at my job. I went with Flask after reading this question and finding this article and was able to knock it out of the park while still getting a full night's sleep once I was done. Thank you.
  • avi
    avi over 10 years
    noob here, how this is a RESTful?
  • Calvin
    Calvin over 10 years
    With Pyramid you can make use of Cornice, which provides useful helpers for building and documenting REST web services.
  • user161642
    user161642 over 10 years
    Check out flask-restful.readthedocs.org/en/latest for a Flask plugin to help with implementing a REST API in Flask.