Ross Boylan wrote:
Here's the traceback for Error: release unlocked lock
Traceback (innermost last): File V:\src\ZopeRoot\lib\python\ZPublisher\Publish.py, line 214, in publish_module File V:\src\ZopeRoot\lib\python\ZPublisher\Publish.py, line 179, in publish File V:\src\ZopeRoot\lib\python\Zope\__init__.py, line 202, in zpublisher_exception_hook (Object: RoleManager) File V:\src\ZopeRoot\lib\python\ZPublisher\Publish.py, line 169, in publish File V:\src\ZopeRoot\lib\python\ZODB\Transaction.py, line 275, in commit File V:\src\ZopeRoot\lib\python\ZODB\Connection.py, line 467, in tpc_finish File V:\src\ZopeRoot\lib\python\ZODB\BaseStorage.py, line 202, in tpc_finish (Object: V:\src\ZopeRoot/var/Data.fs)
Hm. This is odd..... You've uncovered a bug. ZODB doesn't correctly handle the case when modifications are made through two connections to the same database from the same transaction. I've submitted this to the bug collector to be fixed.
The traceback appears in the browser, while the error message below appears on the console. After this, the application is unresponsive.
Is it really unresponsive? Or does it simply not allow you to make changes?
The only reason I got a new connection was that I didn't know how to get the old one
someObject._p_jar where someObject must be an object that is already in the database.
(or if it was safe to reuse it).
Yes. See above.
Is there a canonical way to get Zope's connection? I tried again using Zope.app()._p_jar. (I had thought Zope.app() returned an application wrapper, and gave up when it didn't). This produces the connection, but leads to the same error.
Zope.app is an application wrapper. Zope.app() *opens a new connection* and fetches the application object from it. You get the same error because, once again, you have a new database connection. Just use the _p_jar attribute from some object that is already stored in the database. (snip)
Here's the code I'm running, with some junk stripped out. The product creates OrgUserFolder. Some dtml then sends that object addOrgUser. This creates a regular zope user and then an OrgUserDirectory and an OrgUser, and puts the latter in the former.
OK, see my comments below. I may not be following your logic completely, but hopefully, my suggestions are close to the mark.
---------------------------------- __init__.py ----------------------------------------------------------------- import OrgUser
def initialize(context): try: context.registerClass( OrgUser.OrgUserFolder, constructors=(OrgUser.manage_addUserFolder, ) ) [except clause omited]
--------------------------------- OrgUser.py------------------------------------------------------------------- import Globals from Globals import HTMLFile, MessageDialog import Persistence from AccessControl.User import UserFolder import Zope # for app for database
manage_addUserForm = HTMLFile('addUser', globals())
class OrgUser(Persistence.Persistent): """Add additional information on the user""" meta_type ='Org User' id = 'Organizational_User' title = 'Organizational User'
You need to pass either an existing database object or a connection to getOrgUserDirectory. We'll do the former, which requires us to pass it to the constructor. xxx def __init__(self, user, email): def __init__(self, user, email, container):
self.user = user self.email = email
xxx getOrgUserDirectory().addUser(self) getOrgUserDirectory(container).addUser(self)
def getUserName(self): return self.user.getUserName()
def addOrgUser(name, password, confirm, email, namespace, REQUEST=None ) : "To be called from dtml. Use _ for namespace" [code omitted--I couldn't figure out how to call it, and it seems not to be active] == # class method for OrgUserDirectory _my_Dir = None
You need to pass either an existing database object or a connection to getOrgUserDirectory. We'll do the former.
def getOrgUserDirectory(): def getOrgUserDirectory(container): "Return the root directory for OrgUsers" global _my_Dir if not _my_Dir : import pdb pdb.set_trace()
Get the connection from the passed in container: xxx conn = Zope.app()._p_jar conn = container._p_jar
root = conn.root() _my_Dir = root.get("OrgUser", None) if _my_Dir : print "Found directory" else: print "Creating directory" root["OrgUser"] = OrgUserDirectory() _my_Dir = root["OrgUser"] return _my_Dir
class OrgUserDirectory(Persistence.PersistentMapping):
def __init__(self) : print "Creating OrgUserDirectory" Persistence.PersistentMapping.__init__(self)
def addUser(self, orgUser): "Add indicated user to database" name = orgUser.getUserName() if self.has_key(name) : raise "You can not add an existing user" self[name] = orgUser
class OrgUserFolder(UserFolder): """Base class for UserFolder-like objects""" meta_type='Org User Folder' title ='Organizational User Folder' manage_userInfo=HTMLFile('editUser',globals()) def addOrgUser(acl, name, password, confirm, email, REQUEST=None ) : "To be called from dtml. " print "passing bound addOrgUser" roles = [] # to be decided domains = [] result = acl._addUser(name, password, confirm, roles, domains) #I think that if we don't pass request, it returns None if all well if result : return result # probably not right since management user = acl.getUser(name)
Pass ourselves as container: xxx orgUser = OrgUser(user, email) orgUser = OrgUser(user, email, self)
return "I think we're done"
def manage_addUserFolder(self,dtself=None,REQUEST=None,**ignored): """ """ f=OrgUserFolder() self=self.this() try: self._setObject('acl_users', f) except: return MessageDialog( title ='Item Exists', message='This object already contains a User Folder', action ='%s/manage_main' % REQUEST['URL1']) self.__allow_groups__=f
if REQUEST: return self.manage_main(self,REQUEST,update_menu=1)
(snip) Hope this helps. Jim -- Jim Fulton mailto:jim@digicool.com Python Powered! Technical Director (888) 344-4332 http://www.python.org Digital Creations http://www.digicool.com http://www.zope.org Under US Code Title 47, Sec.227(b)(1)(C), Sec.227(a)(2)(B) This email address may not be added to any commercial mail list with out my permission. Violation of my privacy with advertising or SPAM will result in a suit for a MINIMUM of $500 damages/incident, $1500 for repeats.