I don't know Ruby very well. What I have heard from others tells me that I probably wouldn't prefer it over Python. Python has made me value clarity, explicitness and conciseness over pure expressive power; they have different values[^pep-20] which are certainly both valid in their own ways, but nevertheless are different.
Pylons is proving to be a solid web development framework, but being an amalgamation of third-party modules, there are parts of it that are, unfortunately, lacking. There are two, in particular, with bad documentation, awkward usage, and un-Pythonic idioms - assuming they have idioms worth using at all.
To name names: Routes, and WebHelpers.
No offense to the authors (really!); they're just sore thumbs. There's a reason for this. The common thread joining these two modules1 is that they are attempts to port Ruby on Rails components to Python Frameworks. It's awkward - and it shows.
This is something that Django does very well. It's a Python Web Application Framework, and as such, the Python in it idiomatic, clear, and familiar. That being said, the level of coupling in Django is a problem, but there is no doubt in my mind that the URL configuration, for example is better done2 than in Routes.
One aspect (bug? feature? Both?) of Django is that there is no built-in AJAX. The Django devs have their reasons for this, such as not wanting to cause JavaScript Library wars, but it has the end result of making AJAX in Django a very much roll-your-own feel. WebHelpers, then, doesn't have a very high standard to hit. It just has to do better than nothing - and I'm not sure it does! Using a separate, AJAX controller and jQuery, I had a dead-simple AJAX solution (that worked). The code wasn't integrated into the framework, I'll admit. The thing is, though, that jQuery makes AJAX & DOM manipulation so incredibly painless that there wasn't enough code to make abstraction of the AJAX calls needed. I don't think I had more than 6 non-comment non-blank lines of JavaScript for any of my pages.
The same is just not going to happen with WebHelpers /
Scriptaculous. Scriptaculous (and Prototype, on which it
is based) are excellent3 frameworks for DOM scripting - but
that's the problem. DOM scripting is, as the jQuery home page puts
it, tedious.
JQuery excels at the kind of quick, dead-simple
JavaScript that I had been using. Unless you're Google or Yahoo and
building completely AJAX-based web apps, I think that simplicity of
implementation outweighs framework integration. If you don't
need to use a framework to do AJAX, don't; if you have to ask if you
need one, you don't.
-
Another thread joining them, in a bad way - WebHelpers depends on Routes for some parts of it's functionality. Buh-bye, loosely coupled! That being said, I'll admit I don't know how I would do URL generation without coupling. I probably just wouldn't. I have less than ten URL paths to worry about - why not simply implement URL generation methods in the class definition? This is the get_absolute_url() approach, and it works. ↩
-
I also like that the controllers - called views in Django - are implemented as functions in a module rather than methods to an object. That was certainly how I thought of them, and the repeated controller name in Pylons violates DRY. DRY isn't holy, but it's nice, and I miss the religiousness that Django stuck to it with. ↩
-
Not really. Not Scriptaculous, at least. I say
excellent
because for the purposes of the argument, they could be, and the point would still be valid. But I'm not fond of how Scriptaculous' home page in particular brings my computer to a crawl. The flashy, jittery home page is something I wish they had left in 90's. There's a reason they got rid of the HTML tags for that kind of behavior; why re-implement the same annoyances in (browser-freezing) JavaScript? ↩
Comments
621 spam comments omitted.
I am no longer accepting new comments.
Ben Bangert
#284, 2007-09-04T20:26:34Z
WebHelpers is a direct Ruby port, and as such is prone to a great many errors the original has. I'm not terribly pleased with it, but the concept of having helper functions is good, as Django also has them (though integrated into the templating language itself, ack).
Routes however, is a very different re-implementation of how Rails maps URL's to classes. URL mapping is not Ruby specific, nor is it a port (the Ruby code was un-readable anyways). Mapping related sections of a URL to a class has advantages over the Django style of mapping URLs to a single function. For example, for an admin section in your site, its easy to restrict access to the URL's under it by having a class function that is always called before the method is.
Routes already has a quite a few features not present in Rails, and is continuing to add more. I'd suggest taking a look at my recent blog post on Routes 2.0 regarding its future.
Generally, I always considered Django's mapping to be a huge sore thumb. The framework aims to make things easy, then you get stuck writing regular expressions just to connect a URL?? Keep in mind that regular expressions are terribly easy to screw up as soon as they get even moderately complicated.
So yes, I agree on the WebHelpers bit, its a sore thumb. However, Routes is not an attempt to port the Ruby version. It's totally fresh code, with WSGI middleware, built from the ground up in Python with a massive test library. It can be used as a stand-alone library, and has been used for quite a few WSGI apps without even using Pylons (unlike Django's tightly bound regexp mapping, which feels like Rails in how bound up the whole thing is).
It is DRY, if you map to classes, the class needs to have a name (of course), so naming it SomethingController isn't a DRY violation (which is all subjective as there's no laws of DRY anywhere). If you want to get religious on DRY, notice how many times you write request/Response in your Django functions.
The documentation for Routes is rather extensive I thought, I spent quite a bit of time writing it. What is it missing?
James Bennett
#292, 2007-09-05T08:19:50Z
Ben,
It's worth pointing out in response to your example of requiring login that the Django equivalent is a decorator -- 'login_required' (there's also a 'staff_member_required' and a generic 'permission_required'). And Django allows you to decorate view functions directly in the URLs module, which makes it trivially easy to decorate a view in one case but not in another.
It's also perfectly possible to write a class and route to its methods, with custom behavior in the class to apply anything you'd like (the RESTful Web Services book includes a Django example which does just that).
Adam Gomaa
#299, 2007-09-05T13:24:13Z
Hey Ben!
The documentation thing is more directed at WebHelpers than at Routes. Routes still needs more examples of use within Pylons, but that's more of a problem with the Pylons documentation than with Routes. The Routes docs also don't prioritize what needs to be known first well enough - but we'll get to that in a second.
I would disagree with putting controllers in a class. A controller is not a class. It doesn't represent an object, and it's not intuitive to make that abstraction. Quite the opposite: A controller handles an input - a request object, and the URL - and returns and appropriate object to the calling function. That just screams "function" to me, but not "class".
I really like Django's URL mapping, and it's the single biggest reason I would have to use Django. Is it more verbose than Routes, in theory? Absolutely. But:
It uses regular expressions, which are a first level of simple validation. I can use the same base url for per-month archives and blog posts, since I know the per-month will be something like "^\d{4}/[a-z]{3}/$". Can I do this in Routes? Quite possibly. But with regexps, I don't have to learn another DSL to do it.
It can be very easily modularized. I don't usually have more than 6 or 7 URLs per file, which either specify endpoints or include other url files. In contrast, my config/routing.py file in a Pylons project I'm workig on has about 50 lines of just Routes mappings, because each URL needs slightly different keyword arguments. So even though, in theory, I'd only need one Routes mapping, I end up needing a ton of them. I only use "id" as the arg when I actually use the ID, which isn't very often; I often use other ones like "abbrev", "year", etc. With Django, you do this as a matter of course; argument keyword is defined in the regexp. With Routes, I do the same thing: map.connect('/blah/blah/:my_kwarg', controller="foo", action="bar"). Is there a way to make that shorter, or to put some of them in a function other than routing.make_map()? I hope so, but it's not one of the first things in the tutorial for Routes, which means that Average Joe Programer (whom I someday aspire to be) will never get to it. Django, on the other hand, makes modularizing the URL configuration the second thing you learn about them - the first being the simplest case of how to specify them.
I wouldn't consider Django's use of 'request' a violation of DRY, because what you actually mean is request. There's nothing else that could go there, and you can't make it dissapear. Explicit is better than implicit; but you should only have to be explicit once. The URLs and request-passing both do this superbly. In contrast, Pylons tries to be implicit about both, and ends up causing more headaches for programmers who prefer explicitness.