views:

8996

answers:

8

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. :)

+1  A: 

I am not an expert on the python world but I have been using django which is an excellent web framework and can be used to create a restful framework.

Jeremy B.
+4  A: 

See Python Web Frameworks wiki.

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

gimel
+8  A: 

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.

S.Lott
I separated my rest-api into a different app (in django settings INSTALLED_APPS). Keeps you from having to do this weird view naming...
drozzy
Awful repetition of d = someUsefulThing... Even Django guys suggest DRY.
temoto
@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.
S.Lott
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.
temoto
@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.
S.Lott
See the accepted answer. The result expression {'message': 'Hello, ' + name + '!'} is written once for all presentations.
temoto
@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?
S.Lott
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.
temoto
@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.
S.Lott
Did you try to write that long expression? (twice in sibling functions) ?
temoto
@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.
S.Lott
Okay, i guess it needs paper and visual contact to explain my point.
temoto
@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.
S.Lott
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)` ?
temoto
@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.
S.Lott
`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.
temoto
+4  A: 

I really like CherryPy. Here's an example of a restful web service:

import cherrypy
from cherrypy import expose

class Converter:
    @expose
    def index(self):
        return "Hello World!"

    @expose
    def fahr_to_celc(self, degrees):
        temp = (float(degrees) - 32) * 5 / 9
        return "%.01f" % temp

    @expose
    def celc_to_fahr(self, degrees):
        temp = float(degrees) * 9 / 5 + 32
        return "%.01f" % temp

cherrypy.quickstart(Converter())

This emphasizes what I really like about CherryPy; this is a completely working example that's very understandable even to someone who doesn't know the framework. If you run this code, then you can immediately see the results in your web browser; e.g. visiting http://localhost:8080/celc_to_fahr?degrees=50 will display 122.0 in your web browser.

Eli Courtwright
That's a nice example, but there's nothing RESTful about it.
Wahnfrieden
@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.
lilbyrdie
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".
verveguy
+15  A: 

Something I don't like about CherryPy and Django is that, by default, they treat GET and POST as if they were the same thing. In a proper RESTful API HTTP-verbs are very important, and unless you're very careful and do explicit checks at every request handler, you'll end up falling into a REST anti-pattern.

One framework that gets it right is web.py. When combined with the mimerender library, it allows 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!
martin
This is incorrect, Django has full support for recognizing POST vs GET and limiting views to only certain methods.
Wahnfrieden
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
@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?
martin
+3  A: 

Take a look at

z8000
A: 

I strongly recommend TurboGears or Bottle:

TurboGears:

  • less verbose than django
  • more flexible, less HTML-oriented
  • but: less famous

Bottle:

  • very fast
  • very easy to learn
  • but: minimalistic and not mature
Federico
+1  A: 

Here is a discussion in CherryPy docs on REST: http://docs.cherrypy.org/dev/progguide/REST.html

In particular it mentions a built in CherryPy dispatcher called MethodDispatcher, which invokes methods based on their HTTP-verb identifiers (GET, POST, etc...).

nir