[z3-checkins] r31671 - in z3/sqlos/branch/kobold-sqlos/src/sqlos: . ftests interfaces testing

kobold at codespeak.net kobold at codespeak.net
Sat Aug 26 11:39:22 CEST 2006


Author: kobold
Date: Sat Aug 26 11:39:20 2006
New Revision: 31671

Added:
   z3/sqlos/branch/kobold-sqlos/src/sqlos/browser.py   (contents, props changed)
   z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/joins.txt
   z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/joins_browser.txt
   z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/mono_containers.txt
Modified:
   z3/sqlos/branch/kobold-sqlos/src/sqlos/configure.zcml
   z3/sqlos/branch/kobold-sqlos/src/sqlos/container.py
   z3/sqlos/branch/kobold-sqlos/src/sqlos/ftesting.zcml
   z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/containers.txt
   z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/localutilities.txt
   z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/test_doctest.py
   z3/sqlos/branch/kobold-sqlos/src/sqlos/interfaces/__init__.py
   z3/sqlos/branch/kobold-sqlos/src/sqlos/interfaces/container.py
   z3/sqlos/branch/kobold-sqlos/src/sqlos/testing/sampleperson.py
   z3/sqlos/branch/kobold-sqlos/src/sqlos/zsqlobject.py
Log:
Implemented SQLObjectMonoContainer and SQLOSContainer for joined SQLOS objects, as well as test and ftests.


Added: z3/sqlos/branch/kobold-sqlos/src/sqlos/browser.py
==============================================================================
--- (empty file)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/browser.py	Sat Aug 26 11:39:20 2006
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2004 Enfold Systems LLC. All rights reserved.
+# Copyright (c) 2005-2006 Brian Sutherland. All rights reserved.
+# Copyright (c) 2006 Fabio Tranchitella. All rights reserved.
+#
+# This software is distributed under the terms of the Zope Public
+# License (ZPL) v2.1. See COPYING.txt for more information.
+#
+##############################################################################
+"""Browser package for sqlos
+
+$Id$
+"""
+
+from zope.interface import implements
+from zope.app.form.browser.add import AddView
+from zope.app.traversing.interfaces import ITraversable, TraversalError
+
+from sqlos.interfaces.container import ISQLObjectJoinContainer
+
+
+class SQLOSAddView(AddView):
+    """Custom AddView for SQLOS objects"""
+
+    def create(self, *args, **kw):
+        for container in (self.context, self.context.__parent__):
+            if hasattr(container, '_filters'):
+                kw.update(container._filters)
+        return self._factory(*args, **kw)
+
+
+class SQLOSContainerTraversable(object):
+    """Traverses containers via `__getitem__`."""
+
+    implements(ITraversable)
+    __used_for__ = ISQLObjectJoinContainer
+
+    def __init__(self, container):
+        self._container = container
+
+    def traverse(self, name, furtherPath):
+        container = self._container
+        try:
+            v = container[name]
+        except KeyError:
+            raise TraversalError(name)
+        return v

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/configure.zcml
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/configure.zcml	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/configure.zcml	Sat Aug 26 11:39:20 2006
@@ -38,6 +38,33 @@
       factory=".container.SQLObjectNameChooser"
       />
 
+  <!-- ISQLObjectJoinContainer Views -->
+
+  <content class=".container.SQLOSContainer">
+    <implements interface="zope.app.container.interfaces.IContentContainer" />
+    <factory
+        id="sqlos.container.SQLOSContainer"
+        title="SQLOS join container"
+        description="A container for SQLOS joined instances" />
+    <require
+        permission="zope.View"
+        interface="zope.app.container.interfaces.IReadContainer"
+        />
+  </content>
+
+  <adapter
+      provides="zope.app.container.interfaces.INameChooser"
+      for="sqlos.interfaces.container.ISQLObjectMonoContainer"
+      permission="zope.Public"
+      factory=".container.SQLObjectMonoNameChooser"
+      />
+
+  <adapter
+      factory=".browser.SQLOSContainerTraversable"
+      provides="zope.app.traversing.interfaces.ITraversable"
+      for=".interfaces.container.ISQLObjectJoinContainer"
+      />
+
   <!-- Default view for containers. Should we really be specifying this??-->
 
   <browser:defaultView
@@ -45,6 +72,11 @@
       name="contents.html"
       />
 
+  <browser:defaultView
+      for="sqlos.interfaces.container.ISQLObjectJoinContainer"
+      name="contents.html"
+      />
+
   <browser:page
       name="contents.html"
       menu="zmi_views" title="Contents"
@@ -54,6 +86,15 @@
       attribute="contents"
       />
 
+  <browser:page
+      name="contents.html"
+      menu="zmi_views" title="Contents"
+      for=".interfaces.container.ISQLObjectJoinContainer"
+      permission="zope.ManageContent"
+      class="zope.app.container.browser.contents.Contents"
+      attribute="contents"
+      />
+
   <class class=".adapter.MySQLAdapter">
     <require
         permission="zope.Public"

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/container.py
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/container.py	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/container.py	Sat Aug 26 11:39:20 2006
@@ -14,6 +14,8 @@
 import random
 
 from sqlobject import *
+from sqlos.zsqlobject import SQLOS
+
 from persistent import Persistent
 from zope.interface import implements
 from zope.component import IFactory
@@ -28,8 +30,7 @@
 from zope.app.exception.interfaces import UserError
 
 from sqlos.interfaces import ISQLObject, ISQLObjectIsolated, IISQLObject
-from sqlos.interfaces.container import ISQLObjectContainer
-from sqlos.interfaces.container import IIsolatedSQLContainer
+from sqlos.interfaces.container import ISQLObjectContainer, IIsolatedSQLContainer, ISQLObjectMonoContainer, ISQLObjectJoinContainer
 
 def contained(obj, parent=None, name=None):
     """An implementation of zope.app.container.contained.contained
@@ -78,10 +79,20 @@
         raise UserError("Cannot find a name") # XXX better message, i18n?
 
 
+class SQLObjectMonoNameChooser(NameChooser):
+    """Name chooser for SQLObjectMonoContainer and SQLOSContainer objects"""
+
+    def chooseName(self, name, obj):
+        return str(obj.id)
+
+
 class SQLObjectContainer(Persistent, Contained):
 
     implements(ISQLObjectContainer)
 
+    _monocontainer = False
+    _filters = None
+
     def __init__(self):
         pass
 
@@ -95,6 +106,7 @@
                 # ignore it
                 if utility is not None:
                     yield name, utility
+                    if self._monocontainer: break
 
     def keys(self):
         """ Return a sequence-like object containing the names
@@ -116,8 +128,10 @@
         (name, object) for the objects that appear in the folder.
         """
         for utility_name, utility in self._getAllowedIISQLObjectUtilities():
-            for obj in utility.select():
-                name = '%s.%s' % (utility_name, obj.id)
+            for obj in (self._filters and utility.selectBy(**self._filters) or utility.select()):
+                if self._monocontainer:
+                    name = isinstance(obj.id, basestring) and obj.id or str(obj.id)
+                else: name = '%s.%s' % (utility_name, obj.id)
                 yield (name, contained(obj, parent=self, name=name))
 
     def __getitem__(self, name):
@@ -140,20 +154,25 @@
                 ...
             KeyError: ...
         """
-        if not isinstance(name, basestring):
-            raise KeyError, "%s is not a string" % name
-        try:
-            parts = name.split('.')
-            id = parts[-1]
-            factoryName = '.'.join(parts[:-1])
-        except ValueError:
-            raise KeyError, name
+        if not self._monocontainer:
+            if not isinstance(name, basestring):
+                raise KeyError, "%s is not a string" % name
+            try:
+                parts = name.split('.')
+                id = parts[-1]
+                factoryName = '.'.join(parts[:-1])
+            except ValueError:
+                raise KeyError, name
+        else: factoryName, id = None, name
 
         for utility_name, utility in self._getAllowedIISQLObjectUtilities():
-            if factoryName != utility_name:
-                continue
+            if factoryName and factoryName != utility_name: continue
             try:
-                obj = utility.get(id)
+                obj = utility.get(utility.sqlmeta.idType(id))
+                if self._filters:
+                    for key in self._filters:
+                        if getattr(obj, key) != self._filters[key]:
+                            raise KeyError, name
                 return contained(obj, parent=self, name=name)
             except (SQLObjectNotFound, ValueError):
                 # SQlObject raises ValueError if the key is not correct
@@ -179,7 +198,7 @@
         """Return the number of objects in the folder."""
         i = 0
         for utility_name, utility in self._getAllowedIISQLObjectUtilities():
-            i += utility.select().count() # optimal, does not get all objects
+            i += (self._filters and utility.selectBy(**self._filters) or utility.select()).count()
         return i
 
     def __delitem__(self, name):
@@ -254,3 +273,85 @@
             if self.container_id in obj.domains:
                 return obj
         raise KeyError, name
+
+
+class SQLObjectMonoContainer(SQLObjectContainer):
+    """Mount point for ZSQLObject objects, with multiple item type support
+
+    Test the interface:
+
+        >>> from zope.interface.verify import verifyObject, verifyClass
+        >>> verifyClass(ISQLObjectContainer, SQLObjectMonoContainer)
+        True
+        >>> verifyClass(ISQLObjectMonoContainer, SQLObjectMonoContainer)
+        True
+        >>> c = SQLObjectMonoContainer()
+        >>> verifyObject(ISQLObjectContainer, c)
+        True
+        >>> verifyObject(ISQLObjectMonoContainer, c)
+        True
+    """
+    implements(ISQLObjectMonoContainer)
+
+    _monocontainer = True
+
+
+class SQLOSContainer(SQLOS):
+    """SQLOS subclass for use in Zope 3 as both content and container
+
+    First, make a test data base:
+
+        >>> from sqlos import testing
+        >>> testdb = testing.TestDB([SQLOSContainer])
+
+    Test the interface:
+
+        >>> from zope.interface.verify import verifyObject, verifyClass
+        >>> verifyClass(ISQLObject, SQLOSContainer)
+        True
+        >>> s = SQLOSContainer()
+        >>> verifyObject(ISQLObject, s)
+        True
+        >>> verifyClass(ISQLObjectJoinContainer, SQLOSContainer)
+        True
+        >>> s = SQLOSContainer()
+        >>> verifyObject(ISQLObjectJoinContainer, s)
+        True
+
+    And finally call tearDown and cleanup:
+
+        >>> testdb.tearDown()
+    """
+
+    implements(ISQLObjectJoinContainer)
+
+    def __getitem__(self, name):
+        for j in self.sqlmeta.joins:
+            if name != j.joinDef.name: continue
+            for i, container in self._allowed_joins:
+                if not i.implementedBy(j.otherClass): continue
+                c = container()
+                c._filters = {j.joinColumn[:-3] + 'ID': self.id}
+                return contained(c, parent=self, name=name)
+        raise KeyError, name
+
+    def keys(self):
+        for j in self.sqlmeta.joins:
+            yield j.joinDef.name
+
+    def items(self):
+        for key in self.keys():
+            yield (key, self[key])
+
+    def values(self):
+        for key, obj in self.items():
+            yield obj
+
+    def __contains__(self, name):
+        return name in self.keys()
+
+    def __iter__(self):
+        return iter(self.keys())
+
+    def __len__(self):
+        return len(self.sqlmeta.joins)

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/ftesting.zcml
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/ftesting.zcml	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/ftesting.zcml	Sat Aug 26 11:39:20 2006
@@ -48,7 +48,7 @@
        low level testing -->
 
   <sqlos:factory
-      id="Dog"
+      id="sqlos.somename.Dog"
       component=".testing.sampleperson.Dog"
       description="A Sample Dog"
       />
@@ -82,11 +82,14 @@
         interface="sqlos.testing.sampleperson.IPerson"
         set_schema="sqlos.testing.sampleperson.IPerson"
         />
-
     <require
         permission="zope.View"
         interface="sqlos.interfaces.ISQLObject"
         />
+    <require
+        permission="zope.View"
+        interface="zope.app.container.interfaces.IReadContainer"
+        />
   </content>
 
   <browser:addform
@@ -118,6 +121,50 @@
       view="AddPerson.html"
       />
 
+  <!-- Define a Sample Dog with some views so we can test through the ZMI -->
+
+  <content class=".testing.sampleperson.Dog">
+    <require
+        permission="zope.ManageContent"
+        interface="sqlos.testing.sampleperson.IDog"
+        set_schema="sqlos.testing.sampleperson.IDog"
+        />
+    <require
+        permission="zope.View"
+        interface="sqlos.interfaces.ISQLObject"
+        />
+  </content>
+
+  <browser:addform
+      schema="sqlos.testing.sampleperson.IDog"
+      content_factory="sqlos.testing.sampleperson.Dog"
+      class=".browser.SQLOSAddView"
+      keyword_arguments="fullname"
+      label="New Sample Dog"
+      name="AddDog.html"
+      permission="zope.ManageContent"
+      />
+
+  <browser:editform
+      schema="sqlos.testing.sampleperson.IDog"
+      name="edit.html"
+      menu="zmi_views"
+      label="Edit a Sample Dog"
+      permission="zope.ManageContent"
+      />
+
+  <browser:defaultView
+      for="sqlos.testing.sampleperson.IDog"
+      name="edit.html"
+      />
+
+  <browser:addMenuItem
+      title="SampleDog"
+      factory="sqlos.somename.Dog"
+      permission="zope.ManageContent"
+      view="AddDog.html"
+      />
+
   <!-- Set up a MultiContainer which can contain the Sample People-->
 
   <!--we have to register an adding view for the container, we just use the
@@ -160,4 +207,45 @@
       permission="zope.ManageContent"
       />
 
+  <!-- Dog Container -->
+
+  <browser:view
+      name="+"
+      menu="zmi_actions" title="Add SQLObjects"
+      for="sqlos.testing.sampleperson.IDogContainer"
+      permission="zope.ManageContent"
+      class="zope.app.container.browser.adding.Adding"
+      >
+    <browser:page name="index.html" attribute="index"/>
+    <browser:page name="action.html" attribute="action"/>
+  </browser:view>
+
+  <content class="sqlos.testing.sampleperson.SampleDogContainer">
+    <factory
+        id="sqlos.testing.sampleperson.SampleDogContainer"
+        title="SQLObject Dog Container"
+        description="A persistent container for SQL-backed Dog Objects"
+        />
+    <allow attributes="_filters" />
+    <require
+        permission="zope.View"
+        interface="zope.app.container.interfaces.IReadContainer"
+        />
+    <require
+        permission="zope.ManageContent"
+        interface="zope.app.container.interfaces.IWriteContainer"
+        />
+    <require
+        permission="zope.View"
+        attributes="select"
+        />
+  </content>
+
+  <browser:addMenuItem
+      class="sqlos.testing.sampleperson.SampleDogContainer"
+      title="SQLObject Dog Container"
+      description="A persistent container for SQL-backed Dog Objects"
+      permission="zope.ManageContent"
+      />
+
 </configure>

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/containers.txt
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/containers.txt	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/containers.txt	Sat Aug 26 11:39:20 2006
@@ -124,7 +124,7 @@
 
 Now we add sally's dog:
 
-    >>> fido = sampleperson.Dog(fullname='Fido', owner='sally')
+    >>> fido = sampleperson.Dog(fullname='Fido', owner=sally)
     >>> len(multicontainer)
     2
     >>> fido in [i for i in multicontainer.values()]

Added: z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/joins.txt
==============================================================================
--- (empty file)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/joins.txt	Sat Aug 26 11:39:20 2006
@@ -0,0 +1,67 @@
+Using joins between SQLOS objects
+=================================
+
+First let's set up some tables in the database:
+
+    >>> from sqlos.testing.sampleperson import createTestingTables
+    >>> from sqlos.testing.sampleperson import SamplePerson, Dog, SamplePersonMonoContainer
+    >>> createTestingTables()
+
+Create a new container, and add a SamplePerson object to it:
+
+    >>> container = SamplePersonMonoContainer()
+    >>> len(container)
+    0
+    >>> from zope.app import zapi
+    >>> from sqlos.interfaces import IISQLObject
+    >>> SamplePerson = zapi.getUtility(IISQLObject,
+    ...                                u'sqlos.somename.SamplePerson',
+    ...                                context=container)
+    >>> john = SamplePerson(username='john', fullname='John Black', password='johnpass')
+    >>> len(container)
+    1
+
+Create an external person with his dog:
+
+    >>> mark = SamplePerson(username='mark', fullname='Mark Brown', password='markpass')
+    >>> lessie = Dog(fullname='lessie', owner=mark)
+
+We have a dog in the system, but it is not owned by John:
+
+    >>> len(john.dogs)
+    0
+    >>> len(john['dogs'])
+    0
+ 
+Now, create a new dog owned by John:
+
+    >>> Dog = zapi.getUtility(IISQLObject,
+    ...                             u'sqlos.somename.Dog',
+    ...                             context=container)
+    >>> fido = Dog(fullname='fido', owner=john)
+
+Check if the dog is really owned by John:
+
+    >>> fido.owner == john
+    True
+    >>> [i for i in john.dogs] == [fido]
+    True
+
+Get the container for the join and test it:
+
+    >>> dogs = john['dogs']
+    >>> from zope.interface.verify import verifyObject
+    >>> from sqlos.interfaces.container import ISQLObjectContainer
+    >>> verifyObject(ISQLObjectContainer, dogs)
+    True
+    >>> len(dogs)
+    1
+    >>> fido.id in dogs
+    True
+    >>> lessie.id in dogs
+    False
+
+CleanUp:
+
+    >>> from sqlos.testing.sampleperson import dropTestingTables
+    >>> dropTestingTables()

Added: z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/joins_browser.txt
==============================================================================
--- (empty file)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/joins_browser.txt	Sat Aug 26 11:39:20 2006
@@ -0,0 +1,72 @@
+===============================
+Adding SQLOS objects with joins
+===============================
+
+First let's set up some tables in the database:
+
+    >>> from sqlos.testing.sampleperson import createTestingTables
+    >>> from sqlos.testing.sampleperson import SamplePerson, Dog, SamplePersonContainer
+    >>> createTestingTables()
+
+Then get a browser:
+
+    >>> from zope.testbrowser import Browser
+    >>> browser = Browser()
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+    >>> browser.handleErrors = False
+
+Go to the main interface and add a SQLObject MultiContainer
+
+    >>> browser.open('http://localhost/manage')
+    >>> 'New Sample Person' not in str(browser.contents)
+    True
+    >>> browser.getLink('SQLObject Multi Container').click()
+    >>> browser.getControl(name='new_value').value = 'multicontainer1'
+    >>> browser.getControl('Apply').click()
+
+Now add a Sample Person to the container:
+
+    >>> browser.getLink('multicontainer1').click()
+    >>> browser.getLink('SamplePerson').click()
+    >>> browser.getControl(name='field.fullname').value = 'Bob'
+    >>> browser.getControl(name='field.username').value = 'bob'
+    >>> browser.getControl(name='field.password').value = 'obo'
+    >>> browser.getControl('Add').click()
+
+Lets try to add a dog inside the person:
+
+    >>> browser.getLink('sqlos.somename.SamplePerson.1').click()
+    >>> print browser.contents
+    <BLANKLINE>
+    <!DOC...
+    ...
+    ...Full Name...
+    ...
+    ...Bob...
+    ...
+    ...Username...
+    ...
+    ...bob...
+    ...
+    ...Password...
+    ...
+    ...obo...
+    ...
+    >>> browser.open('http://localhost/multicontainer1/sqlos.somename.SamplePerson.1/dogs')
+    >>> browser.getLink('SampleDog').click()
+    >>> browser.getControl(name='field.fullname').value = 'Fido'
+    >>> browser.getControl('Add').click()
+    >>> browser.open('http://localhost/multicontainer1/sqlos.somename.SamplePerson.1/dogs/1')
+    >>> print browser.contents
+    <BLANKLINE>
+    <!DOC...
+    ...
+    ...Name...
+    ...
+    ...Fido...
+    ...
+
+CleanUp:
+
+    >>> from sqlos.testing.sampleperson import dropTestingTables
+    >>> dropTestingTables()

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/localutilities.txt
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/localutilities.txt	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/localutilities.txt	Sat Aug 26 11:39:20 2006
@@ -67,7 +67,7 @@
     ...         '''create table dog (
     ...                   id integer primary key,
     ...                   fullname varchar(50) not null,
-    ...                   owner varchar(20) not null)''')
+    ...                   owner_id integer not null)''')
     >>> c = cursor.execute(
     ...         '''create table sample_isolated_person (
     ...                   id integer primary key,

Added: z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/mono_containers.txt
==============================================================================
--- (empty file)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/mono_containers.txt	Sat Aug 26 11:39:20 2006
@@ -0,0 +1,106 @@
+Functional test for SQLOSContainer objects
+==========================================
+
+First, prepare the testing environment:
+
+    >>> from sqlos.testing.sampleperson import SamplePerson, SamplePersonMonoContainer
+    >>> from sqlos.interfaces import ISQLObject
+    >>> from sqlos.interfaces.container import ISQLObjectMonoContainer
+    >>> from zope.interface.verify import verifyObject
+    >>> container = SamplePersonMonoContainer()
+    >>> verifyObject(ISQLObjectMonoContainer, container)
+    True
+
+We are not in the business of letting errors pass silently, so looking inside if
+we get a database error (it must be of the type DatabaseException):
+
+    >>> [i for i in container.items()]
+    Traceback (most recent call last):
+        ...
+    DatabaseException: ...
+
+So let's create some database tables if not already there:
+
+    >>> from sqlos.testing.sampleperson import createTestingTables
+    >>> createTestingTables()
+
+We should now be able to look inside an empty container:
+
+    >>> [i for i in container.keys()]
+    []
+    >>> [i for i in container.items()]
+    []
+    >>> [i for i in container]
+    []
+    >>> [i for i in container.values()]
+    []
+    >>> len(container)
+    0
+
+Lets create some objects:
+
+    >>> people = [{'username': 'harry',
+    ...            'fullname': 'Harry the Hack',
+    ...            'password': 'harrypass'},
+    ...           {'username': 'sally',
+    ...            'fullname': 'Sally the Wack',
+    ...            'password': 'sallypass'}]
+    >>> from zope.app import zapi
+    >>> from sqlos.interfaces import IISQLObject
+    >>> SamplePerson = zapi.getUtility(IISQLObject,
+    ...                                u'sqlos.somename.SamplePerson',
+    ...                                context=container)
+    >>> harry = SamplePerson(**people[0])
+    >>> len(container)
+    1
+    >>> sally = SamplePerson(**people[1])
+    >>> len(container)
+    2
+
+Lets see whats inside:
+
+    >>> [i[0] for i in container.items()]
+    ['1', '2']
+    >>> [i[1] for i in container.items()] == [harry, sally]
+    True
+    >>> [i for i in container.values()] == [harry, sally]
+    True
+    >>> [i for i in container.keys()]
+    ['1', '2']
+    >>> [i for i in container]
+    ['1', '2']
+
+Let's test to see what the container does with bad id's (must raise KeyError):
+
+    >>> container[3]
+    Traceback (most recent call last):
+        ...
+    KeyError: ...
+
+You can get() as well:
+
+    >>> container.get(1) == harry
+    True
+    >>> container.get('sss', 'default')
+    'default'
+
+Setitem passes but is really a no-op:
+
+    >>> container['sss'] = 'yyy'
+    >>> len(container)
+    2
+
+Finally let's delete harry:
+
+    >>> del container[1]
+    >>> len(container)
+    1
+    >>> container[1]
+    Traceback (most recent call last):
+        ...
+    KeyError: 1
+
+CleanUp:
+
+    >>> from sqlos.testing.sampleperson import dropTestingTables
+    >>> dropTestingTables()

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/test_doctest.py
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/test_doctest.py	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/ftests/test_doctest.py	Sat Aug 26 11:39:20 2006
@@ -20,9 +20,13 @@
 
 def test_suite():
     filelist = [readme,
+                'joins_browser.txt',
                 'adding.txt',
                 'connection.txt',
                 'containers.txt',
                 'localutilities.txt',
-                'isolated_containers.txt']
+                'isolated_containers.txt',
+                'mono_containers.txt',
+                'joins.txt',
+                ]
     return FunctionalDocFileSuite(*filelist)

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/interfaces/__init__.py
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/interfaces/__init__.py	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/interfaces/__init__.py	Sat Aug 26 11:39:20 2006
@@ -17,6 +17,7 @@
 from zope.schema.vocabulary import SimpleVocabulary
 from zope.schema import Choice, List
 from zope.app.annotation.interfaces import IAttributeAnnotatable
+from zope.app.container.interfaces import IContained
 from sqlobject import NoDefault
 from sqlobject.dbconnection import DBConnection, DBAPI
 from sqlobject import _mysql, _postgres, _sybase
@@ -268,7 +269,7 @@
         similar to select()
         """
 
-class ISQLObject(Interface):
+class ISQLObject(IContained):
 
     # XXX - _idName moved to sqlmeta
     #_idName = Attribute('Primary Key')

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/interfaces/container.py
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/interfaces/container.py	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/interfaces/container.py	Sat Aug 26 11:39:20 2006
@@ -15,9 +15,13 @@
 from zope.app.container.interfaces import IContainerNamesContainer
 from zope.app.container.interfaces import IReadContainer, IContainer
 
+from sqlos.interfaces import ISQLObject
+
+
 class ISQLObjectReadContainer(IReadContainer, IAttributeAnnotatable):
     """ An SQLObject Container """
 
+
 class ISQLObjectContainer(IContainer, IContainerNamesContainer,
                           IAttributeAnnotatable):
     """ An SQLObject Container """
@@ -27,7 +31,16 @@
 
     __setitem__.precondition = ItemTypePrecondition()
 
+
 class IIsolatedSQLContainer(ISQLObjectContainer):
     # TODO Attribute -> zope.schema.* - jinty
     container_id = Attribute("The id of the containers, this is a filter on the"
                              "database table.")
+
+
+class ISQLObjectMonoContainer(IContainer, IContainerNamesContainer, IAttributeAnnotatable):
+    """A single-factory SQLObject container"""
+
+
+class ISQLObjectJoinContainer(ISQLObject, IReadContainer):
+    """Interface for SQLOS objects which expose joins"""

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/testing/sampleperson.py
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/testing/sampleperson.py	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/testing/sampleperson.py	Sat Aug 26 11:39:20 2006
@@ -1,13 +1,13 @@
 from sqlobject import *
 import transaction
-from zope.interface import implements, classProvides, Interface
+from zope.interface import implements, classProvides, Interface, Attribute
 from zope.schema import TextLine, Text, Datetime
 from zope.app.container import constraints
 
 from sqlos.zsqlobject import SQLOS
 from sqlos.interfaces import ISQLSchema, IISQLObjectIsolated, ISQLObjectIsolated
-from sqlos.interfaces.container import ISQLObjectContainer
-from sqlos.container import SQLObjectContainer, SQLIsolatedContainer
+from sqlos.interfaces.container import ISQLObjectContainer, ISQLObjectJoinContainer
+from sqlos.container import SQLObjectContainer, SQLIsolatedContainer, SQLObjectMonoContainer, SQLOSContainer
 
 def createTestingTablesSubscriber(obj):
     # An event subscriber that can be used to create the testing tables
@@ -38,6 +38,7 @@
                         description=u"The user's login")
     password = TextLine(title=u'Password',
                         description=u"The user's password")
+    dogs = Attribute('Dogs')
 
 
 class IPersonContainer(ISQLObjectContainer):
@@ -50,19 +51,46 @@
     constraints.containers(IPersonContainer)
 
 
+class IDog(ISQLSchema):
+
+    fullname = TextLine(title=u'Full Name',
+                        description=u'The full name of the dog')
+    owner = Attribute(u'Owner')
+
+
+class IDogContainer(ISQLObjectContainer):
+
+    constraints.contains(IDog)
+
+
+class IDogContained(Interface):
+
+    constraints.containers(IDogContainer)
+
+
 class SamplePersonContainer(SQLObjectContainer):
 
     implements(IPersonContainer)
 
 
-class SamplePerson(SQLOS):
+class SampleDogContainer(SQLObjectMonoContainer):
+    """Sample container for Dog objects"""
+
+    implements(IDogContainer)
+
+
+class SamplePerson(SQLOSContainer):
 
     implements(IPerson, IPersonContained)
 
+    _allowed_joins = [(IDog, SampleDogContainer)]
+
     fullname = StringCol(length=50, notNull=1)
     username = StringCol(length=20, notNull=1)
     password = StringCol(length=20, notNull=1)
 
+    dogs = MultipleJoin('Dog', joinColumn='owner_id')
+
 
 class SampleIsolatedPerson(SQLOS):
 
@@ -112,23 +140,15 @@
     implements(IPersonContainer)
 
 
-class IDog(ISQLSchema):
-
-    fullname = TextLine(title=u'Full Name',
-                        description=u'The full name of the dog')
-    owner = TextLine(title=u'Owner username',
-                     description=u'The username of the dog\'s owner')
-
-
 class Dog(SQLOS):
 
-    implements(IDog)
+    implements(IDog, IDogContained)
 
     fullname = StringCol(length=50, notNull=1)
-    owner = StringCol(length=20, notNull=1)
+    owner = ForeignKey('SamplePerson')
 
 
-class IMultiContainer(IPersonContainer):
+class IMultiContainer(IPersonContainer, IDogContainer):
 
     constraints.contains(IDog, IPerson)
 
@@ -136,3 +156,8 @@
 class MultiContainer(SQLObjectContainer):
 
     implements(IMultiContainer)
+
+
+class SamplePersonMonoContainer(SQLObjectMonoContainer):
+
+    implements(IPersonContainer)

Modified: z3/sqlos/branch/kobold-sqlos/src/sqlos/zsqlobject.py
==============================================================================
--- z3/sqlos/branch/kobold-sqlos/src/sqlos/zsqlobject.py	(original)
+++ z3/sqlos/branch/kobold-sqlos/src/sqlos/zsqlobject.py	Sat Aug 26 11:39:20 2006
@@ -13,9 +13,11 @@
 from zope.interface import implements
 from sqlobject.main import SQLObject
 from sqlobject import StringCol
+from zope.app.container.contained import Contained
 
 from sqlos.connection import ConnectionDescriptor
 from sqlos.interfaces import ISQLObject
+from sqlos.interfaces.container import ISQLObjectJoinContainer
 from sqlos import _transaction
 
 def syncUpdateAll():
@@ -26,7 +28,7 @@
     _transaction.dirty_object_registry.syncUpdateAll()
 
 
-class SQLOS(SQLObject):
+class SQLOS(SQLObject, Contained):
     """Subclass SQLObject to enable ``lazy updates`` by default,
     as well as adding knowledge to register ``dirty`` objects
     with SQLObjectTransactionManager so they get sync'd on transaction
@@ -49,7 +51,9 @@
         >>> testdb.tearDown()
     """
     implements(ISQLObject)
+
     _connection = ConnectionDescriptor()
+    _allowed_joins = []
 
     class sqlmeta:
         lazyUpdate = True


More information about the z3-checkins mailing list