For my own personal work and reasons, I’ve been moving as much of my non-browser based development into the world of Python as I can manage. Any reasons why are sort of beyond the scope of this and I might write them out sometime later. But for the time being, I wanted to discuss the question of Python web development.
Being a contrarian by nature, I automatically did not want to use Django as it is widely the most popular and utilized Python web framework. When I started looking into Python, I wanted it to work just like the PHP I had cut my teeth on, so that drew me to mod_python and its Python Server Pages. But mod_python has not been supported or developed for a long time and the technology it is based around are a bit outmoded. So I was forced to look at WSGI compatible applications in the family of Django and others.
Now my requirements for a framework, after working with raw PHP when I first got started and then a custom framework are rather straightforward. I want a solid database abstraction layer (DAL) that functions in an object-relational mapping (think: akin to Hibernate, and feeling natural to the flow of the rest of the language I’m using, supporting its natural data structures and the like) while isolating me from my choice of database or storage. I also want it to have very solid and integerated JSON and non-HTML support. I was spoiled with the proprietary PHP framework I used by its solid and seamless integration of a JSON output mechanism. I also want it to do all the heavy lifting for me, while leaving me feeling powerful and still in control of the flow of my program.
So I stumbled across web2py and have been toying with that. I have also recently been convinced to give Django a try. The following are my impressions – those of a solo developer working on relatively simple pages and sites while laying out a plan and framework for a larger project, so consider the opinions in that light.
ORM – Object Relational Mapping
Both web2py and Django have excellent Object-Relational mapping layers in their database libraries. For both frameworks the developer will define an object type which the framework will transform into a SQL table or tables. In Django you do this by creating a class which extends Django’s Model class. The fields you define all need to be instances of the different field types. Then, you fetch the objects or create them like natural Python objects, calling the .save() method when you want to write out the changes to the database. This is a bit more Pythonic than the web2py mechanism of calling on a method of the DAL object.
With web2py’s method, however, all models are contained on the explicitly declared DAL object (often named “db”) and accessed that way. So to access the person objects you refer to db.person. In Django you would simply refer to the Person class you’ve declared. In my thinking, containing all of the database-driven models on a database object feels safer than having them be free-standing classes. However, the Django model classes are more Pythonic in their feeling. Creating a Django model for a person would look like
person = Person()
person.name = "John Doe"
person.gender = "Male"
person.occupation = "gardener"
In web2py, this would look like
db.person.insert(name="John Doe", gender="Male", occupation="gardener")
Obviously the Django method feels more like manipulating a regular Python object, but I like the namespacing of the web2py which makes it explicitly obvious we are doing a database transaction. If only they were brought together somehow!
Another way Django wins out here is with ManyToMany fields. These are the types of relationships where, for instance, Bob and Alice together own a Ford and a Chevy. Each car is owned by two people and each person owns two cars. In Django, when defining the person, I would simply add a field called car that was an instance of a many-to-many relationship, and then I could access any cars a person owns by accessing a property on my object (I believe through something like person.cars_set) which is a list of the cars that person owns. Similarly Django automatically creates the reverse relationship on a car by linking it to an owner. In web2py, the developer needs to explicitly create the person-car ownership table and give it the two foreign key links.
However, web2py exposes an interesting mechanism here to create what are essentially views in the database from a Pythonic interface which makes dealing with the resultant relationship very easily and can be extended to any other frequently used relationship or query. Given a definition of person, dog and ownership like this
db.define_table('person', Field('name')) db.define_table('dog', Field('name')) db.define_table('ownership', Field('person', db.person), Field('dog', db.dog))
I can then define the view of all people with dogs and all dogs with owners like this
person_dog = db((db.person.id == db.ownership.person) & (db.dog.id == db.ownership.dog))
Now I can iterate on all of the person-dog pairings by a call like
for row in person_dog.select(): print row.person.name, row.dog.name
And I can search for all of Bob’s dogs by asking for
for row in person_dog(db.person.name=="Bob").select(): print row.dog.name
and similarly for the owners of Fido by changing my arguments to person_dog().
This view functionality is very powerful, just as SQL views can be very powerful. Wrapped up inside of the DAL as it is, I don’t know if these are actual SQL views, but they make dealing with some very common relationships within your models very easy. It need not be related to many-to-many relationships. I haven’t come across an equivalent pre-defined mechanism within Django, though it is probably achievable by creating member methods on a model class.
I have to say, though, overall Django wins out as more Pythonic in the DAL and the encapsulation of the many-to-many relationships, even if I slightly prefer web2py for its namespacing of the database objects and its powerful views which come more or less for free.
I was surprised, when I looked at the list of supported Django databases, that I only saw listed MySQL, Oracle, Postgres and SQLite. Although these are all the databases I have used myself, and therefore it is sufficient for my own work, I must say I am surprised at how short the list is. I didn’t see any mention in the documentation of supporting third-party contribs or anything else like that, although Django says you are free to incorporate any ORM layer you want. web2py, on the other hand, claims native support for SQLite, MySQL, Postgres and Oracle in addition to MS SQL, FireBird, DB2, Ingres and the Google App Engine if you happen to be running in that environment.
If you really require your DAL to be database-agnostic and usable anywhere, web2py holds about twice as many cards as Django here. Very impressive, and very useful, especially if you use or want to use Google App Engine. There are a few others mentions of GAE support throughout the framework documentation, so it seems to be something web2py takes very seriously as a support point for their software.
In Django, when a request comes in for your controller (Django calls these Views), your method is called just as it would have been called in a traditional Python app. If you want access to your own models or extra libraries or anything of that type, you need to import them. If you want to return a value to the user that is not an error message, you can to call a templating engine, retrieve its results as a string, construct an HttpResponse() object and pass it the string which will be returned to the user. If there is a problem with your database access, you need to raise your own 500 errors. I have to design an app-specific way of determining what format of output the user is looking for (HTML, XML, RSS, JSON?). After I have defined a database schema, or changed it, I have to manually issue some command line operations to inform Django that it needs to update the schema. To me, these represent areas where Django isn’t even a finished framework. There is too much here for me to do, if I just want to get my application up and running.
In web2py, I’ll address each of the above issues and where I think the web2py offering is stronger than Django’s.
- Extra imports: In web2py, the base functionality is already imported for me in my controllers (Django’s views). My database object with all my models, web2py’s request and response objects, and a few basic system environment objects. These are functionality basic enough to dynamic web programming that to me a framework which does not include them already is asking more of me than it ought. Perhaps a bit PHP-like of my thinking, but I consider this a major plus for web2py.
- Response values and errors: Of course, if I want to call methods like redirects or errors in web2py, the response object is already created. Instead of raising an error myself, I can just tell the response object what error code to return. And instead of being limited to returning an instance of HttpResponse() the way Django requires, in web2py I can return a string or a dictionary. If my response has to be wrapped in an HttpResponse() anyway, why make the programmer explicitly create it? The different types of return values are handled differently by web2py based on…
- Output format: With Django, I need to create and retrieve a string myself, pass that to my HttpResponse() object, and return that from my view. In web2py, there is a proper View/Controller separation. If I want to return a string, I am free to do that, and that string will be returned verbatim to the user. However, if I return a dictionary, web2py quietly calls the view (Django would call it a template) associated with the current controller, expanding each of the keys of my dictionary to be a global variable inside of the view. If the request came in with a .html extension or no extension, the associated HTML view is rendered
However, if I want the dict object directly encoded, I need only append a “.json” onto my URL and the associated JSON view will be called. The included default JSON view will generically encode the values of the dict into a JSON string for return to the user – no work needed by me. I don’t even need to call a JSON library to stringify the dictionary. One view, transparently available in both HTML and JSON. Likewise I can add a .rss to the URL for the RSS view to be rendered instead. The same with a .xml extension.
Of course, as the programmer, I am free to override the default or force a particular view format to be rendered, also I can request a view other than the default view, but there are defaults and they are straightforward to work with, whereas with Django, I have to specify each of those values manually.
- Changes to schema: Now this one I’m not so certain about. If you change the value of your database models at any time, web2py will automatically attempt to adjust your database schema the next time you load the models file. This can be a blessing or a curse. The blessing is that your ORM API is never out of sync with your database. All you do is update the model file, refresh your page, and you have the new models. On the other hand, lots of developers are wary of anything touching their database. The explicit invocation of a migration path, including manual supervision, the way that Django allows is nice. Web2py has the same manual shell interface available for running a custom upgrade path, so it’s not like the framework is forcing you to accept the upgrade, and it also has a way to specify custom automatic upgrading and conversions. But it could be worrisome for some types of changes and migrations (particularly moving columns or altering their types). Still, the hybrid of automatic, behind-the-scenes work done by the framework and the power of the manual operations gives web2py an advantage from my point of view.
Any experience with either? What do you think?
30 Comments to Python – web2py or Django
Leave a Reply
Most Viewed Posts
- Python – web2py or Django 43,996 views
- A better @Ubuntu MinGW experience 5,029 views
- web2py, WebSockets and Socket.IO: Part I – Basic Display 4,240 views
- If Programs Were Landscaping: Programming Languages 4,144 views
- @web2py, WebSockets and Socket.IO – Part III: Socket.IO 3,882 views
- @web2py, WebSockets and Socket.IO: Part II – AJAX + WebSockets 3,732 views