Basic ZClass Tests ================== We can create ZClasses from Python, It's a bit complicated, as ZClasses were designed mainly to be used from the web. First, we need to install the ZClass-aware class factory in our database: >>> import Zope2.App.ClassFactory >>> some_database.classFactory = Zope2.App.ClassFactory.ClassFactory To do anything, we need a working Zope object space: >>> conn = some_database.open() >>> from OFS.Application import Application >>> app = Application() >>> conn.root()['Application'] = app >>> from OFS.Folder import manage_addFolder >>> manage_addFolder(app, 'temp_folder') >>> from OFS.Application import initialize >>> initialize(app) >>> app.manage_addFolder('sandbox') >>> sandbox = app.sandbox Once we have an object space, we need to create a product to hold the ZClass: >>> app.Control_Panel.Products.manage_addProduct('test', '') >>> test = app.Control_Panel.Products['test'] Then we can create the ZClass in the product: >>> test.manage_addZClass('C', zope_object=True, CreateAFactory=True) Having created a ZClass, we can create an instance. When setting the 'CreateAFactory' flag, a factory will be created which can be called from the web to create a new instance of 'C'. It also places an option for adding 'C's in the Add-menu in the ZMI. >>> factory = test.C_factory >>> factory.__class__ The other objects created are: - a permission "C_add_permission" required to access the factory, >>> test.C_add_permission.meta_type 'Zope Permission' - an initial add-form "C_addForm" which calls >>> test.C_addForm.meta_type 'DTML Method' - the add-script "C_add" for creating a new 'C' instance >>> test.C_add.meta_type 'Script (Python)' The factory stores the name of the initial page and its permission name: >>> factory.initial, factory.permission ('C_addForm', 'Add Cs') We only need a simple add-script in this scenario: >>> factory.initial = 'C_add' >>> test.C_add.ZPythonScript_edit('', ... '##parameters=dispatcher, request\n' ... 'return container.C.createInObjectManager(' ... 'request["id"], request)\n') ... Anonymous users are usually not allowed to create new content: add a new user... >>> app.acl_users._addUser('admin', 'pass', 'pass', (), ()) >>> user = app.acl_users.getUser('admin').__of__(app.acl_users) log in as user 'admin'... >>> from AccessControl.SecurityManagement import newSecurityManager >>> newSecurityManager(None, user) Now simulate a browser request to add a 'C' instance with id 'z': >>> request = {'id': 'z'} >>> from zExceptions.unauthorized import Unauthorized >>> try: ... sandbox.manage_addProduct['test'].C_factory.index_html(request) ... except Unauthorized: ... print 'not authorized' not authorized All right, allow the admin user to 'Add Cs': >>> bool(user.has_permission('Add Cs', sandbox)) False >>> sandbox.manage_addLocalRoles('admin', ['Admin']) >>> sandbox.manage_permission('Add Cs', roles=['Admin']) >>> bool(user.has_permission('Add Cs', sandbox)) True Try again: >>> request = {'id': 'z'} >>> sandbox.manage_addProduct['test'].C_factory.index_html(request) >>> app.sandbox.z Log out: >>> from AccessControl.SecurityManagement import noSecurityManager >>> noSecurityManager() From python there is a much simpler way for creating a ZClass instance: >>> c = test.C() >>> c._setId('c') >>> app._setObject('c', c) 'c' Now, ZClass instances aren't very interesting by themselves. We can give them data by defining property sheets: >>> test.C.propertysheets.common.manage_addCommonSheet('basic', '') >>> test.C.propertysheets.common['basic'].manage_addProperty( ... 'x', 'hee ', 'string') >>> app.c.x 'hee ' >>> test.C.propertysheets.common['basic'].manage_addProperty( ... 'y', 42, 'int') >>> app.c.y 42 Of course, we can change the data: >>> app.c.x = 'hi ' >>> app.c.y = 3 >>> app.c.x, app.c.y ('hi ', 3) We can also add methods, such as Python scripts: >>> test.C.propertysheets.methods.manage_addProduct[ ... 'PythonScripts'].manage_addPythonScript('eek') '' >>> test.C.propertysheets.methods['eek'].ZPythonScript_edit('', ... 'return container.x * container.y') >>> app.c.eek() 'hi hi hi ' Let's commit our changes: >>> import transaction >>> transaction.commit() We can access the class in another connection. We'll use an explicit transaction manager so that we can use the second connection without creating a separate thread: >>> tm2 = transaction.TransactionManager() >>> conn2 = some_database.open(transaction_manager=tm2) >>> app2 = conn2.root()['Application'] >>> test2 = app2.Control_Panel.Products['test'] >>> c2 = test2.C() >>> c2._setId('c2') >>> app2._setObject('c2', c2) 'c2' >>> app2.c2.x = '*' >>> print app2.c2.x, app2.c2.y, app2.c2.eek(), '!' * 42 ****************************************** ! >>> print app.c.x, app.c.y, app.c.eek(), '!' hi 3 hi hi hi ! >>> tm2.commit() Of course, we should be able to see the new object created in the other connection: >>> conn.sync() >>> app.c2.eek() '******************************************' We can copy instances: >>> c3 = app.c2._getCopy(app) >>> c3 is app.c2.aq_base False >>> app.c3 = c3 >>> app.c3.eek() '******************************************' But the copies share a common class: >>> c3.__class__ is app.c2.__class__ True