In Rails and Django, persistence is provided through object classes that inherit from a Model class, and represent rather directly tables in a relational database, and instances of those objects represent rows in those tables. Saving object changes to the database is explicit, and implemented using the obvious SQL INSERT or UPDATE mechanisms.
In Zope, persistence is provided through object classes that inherit from a Persistent class, and a single object database (ZODB) is used to store the data. A transaction model with the possibility of conflicts and retries is provided.
Our experience to date with the ZODB frameworks is that the performance is less than stellar, and that implementors have to build separate indexes (which are a great source of database conflict errors and retries) to speed up searching for data; whereas the relational database setups can have standard database indexes and query optimizations used to address performance issues with the persistent storage. [While one can access relational databases from within Zope, that is not the usual development/persistence model, and the support is not done in an object-oriented framework.]
Also, the relational database persistence packages (Django and Rails) have tools to reverse-engineer object definitions from existing databases, which makes it much easier to integrate directly with your existing database applications.
django has a regular-expression mapping system which maps URLs to Python function calls. This is a fast, low-overhead system; but it is by design not defined to what objects a given URL calls.
Zope puts object instances in container objects, and the container hierarchy has a root. This leads to path-based URL mapping where http://site/a/b/c/d maps to the "d" method on the object tied to 'c' in the object tied to 'b' in the object tied to 'a' in the root container object -- that is it works like a file-system.
Rails by default maps everything as http://site/controller/action/id where the controller is the name of the controller class, and the action is a template + method name. However, they've recently added a Routing object class which lets you define URL mappings.
Zope comes with 2 template packages, DTML and TAL. DTML is an SGML template language, while TAL is an XML based one. TAL has one advantage above all the other notations here in that it can be loaded and saved cleanly in nearly all HTML editors (i.e. without damaging the active parts of the template), so a web designer using, say, DreamWeaver could edit the template and make it look nice. The disadvantage of both the Zope template notations is the difficulty in editing them, and the implied context in which they operate, which makes getting the right data into the template renderer sometimes difficult.
Ruby comes with 3 template notations, one (ostensibly) for web pages (although it can be used for plain text, also) , one for XML pages, and one for javascript pages. The latter two have many shortcuts designed to allow you to do less typing, or to integrate more nicely with the specific notation. All of them require knowing at least some Ruby syntax to understand/edit/modify the page templates.
Django uses one "universal" page template format, which has well defined notation and tags, but which one can extend with added tags and "filters".
Django wins for
Ruby has a "scaffold" generator script, which generates a code fragment and template files to browse and edit a given database table. The templates are then editable/customizable.
Django has an automatic "admin" tool, which generates screens on the fly from model classes. The behavior of this generation (and whether it is available at all) is modifiable by adding class data to the model class definition.
If you use packages like Archetypes in Zope, you get edit and view screens for your data types; but not otherwise.
In Zope, with suitable imports and so on you can approximate the execution environment of your code in the web environment, but you must begin and end object database transactions, etc. by hand.
In Ruby and Django, you can initiate a session, choose where session data is stored, and get session data back later in the session.
This is the one category where Zope pretty much wins.
Zope has (with Archetypes and CMFFormController) a mechanism for generating forms, specifying validation scripts/tools and displaying errors discovered by validators. If you want a form which is not generated by an Archetypes data type, you have a somewhat complicated page template to write, or you can use PloneFormGen or Formulator...
Rails has forms calls that can be used in a page template to iterate through a form specification and render fields; and their scaffold tool will generate such templates from Models.
django has forms calls that directly render HTML for an abstract form/ field type, as well as a form/model interface type that you can inherit from and specify a Model, where it will render the form for editing data for that table automatically.
Of these alternatives, I think django has the nicer interface.
Rails makes very heavy use of language features peculiar to Ruby, so that one needs to understand Ruby semantics very well to follow it. We have very few people at Fermilab with any Ruby experience.
Django is written in, and seems to promote, very straightforward Python code, and we have lots of experienced Python programmers here at Fermilab.
Advantage: Django.
Zope is hindered by its ZODB implementation, which has very poor write performance and is difficult to index effectively.
Therefore, considering the following: