David Johnson wrote:

I want to create an instance of a class that does not reside in the Zope DB, and yet manage it through the ZMI.

Yes, I'm working on that as well.

That is what I have done so far. I created a package called “customers”, which has an instance in the Zope DB. It has a view which lists all customers from an RDBMS. It creates a link for each customer with the customer ID embedded in it.

I would like to create, on the fly, a customer instance from the customer ID in the URL. Based upon the ID I could set all the attributes of the instance by loading it from a database.

Hmm, I didn't try your approach, sorta faking it with record ids inserting the view construction. My approach was to create a container component representing the SQL table and another component representing instances of records within that table.

class VentureSet(
    Persistent, SiteManagerContainer, Contained, SimpleSQLTable):

class Venture(
    Location, RelationalRow):

While the container, VentureSet, is persistent within the ZODB, what it *contains* is constructed dynamically from SQL queries. The ZODB persistent data is just the connection name itself, nothing more, and the SQL table name is a class-static attribute. Between those two, each XXXSet class can find its data table.

  def keys(self):
      """Return a sequence-like object containing the names
         associated with the objects that appear in the folder
      query = "SELECT v_id FROM %s WHERE subset = '%s'" \
            % (self.__sqltable__, self.subset)
      return [row.v_id for row in self(query)]

The call to self is something I clipped from SQLMethods, to convert the connection name into an actual connection object, doing a utility lookup, submitting the query and returning the records:

    def __call__(self, query):
            connection = self.getConnection()
        except KeyError:
            raise AttributeError, (
                "The database connection '%s' cannot be found." % (

        cache = getCacheForObject(self)
        location = getLocationForCache(self)
        if cache and location:
            _marker = object()
            result = cache.query(location, {'query': query}, default=_marker)
            if result is not _marker:
                return result
        result = queryForResults(connection, query)
        if cache and location:
            cache.set(result, location, {'query': query})
        return result

Here is where a query to the VentureSet container wraps the rows read from the SQL table into Zope 3 components:

    def values(self):
        """Return a sequence-like object containing the objects that
           appear in the folder.
query = "SELECT * FROM %s WHERE subset = '%s'" % (self.__sqltable__, self.subset)
        ventures = []
        for row in self(query):
            venture = Venture(row.v_id, row.legalname, row)
            locate(venture, self, row.v_id)
        return ventures

The instances of Venture, which represent the records in the table are non-persistent as far as the ZODB goes. I did inhert from Location so that they have a name (primary key) and location (ref to the VentureSet container) but those attributes are assigned after an SQL query to instantiate the object.

  class Venture(Location, RelationalRow):

The RelationalRow class is my own little lazy accumulator of set-attributes that get turned into an UPDATE SQL statement at commit time, using:


This all gives me a ZMI-manageable object folder of SQL records, and the URLS look something like (I have similar ledgerset/ledger component pair underneath the vntureset/venture pair):


One trick with this is you have to provide a NameChooser class, from Philipp's Zope 3 book, to have the VentureSet container defer to the Venture item for its name, since in Zope 3 items can have names that differ from the name by which it is known within its container. The NameChooser class is straight from the book.

I hope this answers your question and if not, I'm glad to share my sourcecode.

I'm looking now at whether I should abandon my home-grown ORM and switch to sqlos/SQLObject although for what little mine does so far, it's much simpler.


Zope3-users mailing list

Reply via email to