ok, persistent fool that i am, i'm still trying to figure out how to use ZODB in an External Method to do some simple (ha!) database stuff. so i built a very simple app, to implement a counter. each time you invoke the "Count" method, you get a number back, and it increments. running the script (see below) by hand, it works fine. referencing it from Zope, using this dtml fragment: <dtml-var "Count( 'chickens' )"> or just pointing my browser at: http://kestrel.sage.att.com:8080/Garry/ZODB/Count?name=chickens to reference an External Method called Count, which refers to the Count function in this script, i get: Error Type: AttributeError Error Value: Counter if i then click the reload button, i get: Could not lock the database file. There must be another process that has opened the file. and i'm locked out until i restart zope. can someone please help? this is getting really discouraging. a simple explanation of where i'm going wrong would be greatly appreciated. a small working example of a working app would be terrific. thanks -------------------------------------------------------------- script follows: import ZODB, ZODB.FileStorage import Persistence class Counter(Persistence.Persistent): """A Persistent Counter""" def __init__( self, name ): self.name = name self.value = 0 def Count( name ): "Get counter value, then increment, creating if necessary" db = ZODB.DB( ZODB.FileStorage.FileStorage( '/tmp/CountDB.fs' ) ) connection = db.open() root = connection.root() if not root.has_key( name ): counter = Counter( name ) root[ name ] = counter else: counter = root[ name ] counter.value = counter.value + 1 get_transaction().commit() connection.close() return counter.value if __name__ == '__main__': print Count( 'chickens' ) -- Garry Hodgson Every night garry@sage.att.com a child is born Software Innovation Services is a Holy Night. AT&T Labs - Sophia Lyon Fahs
Garry Hodgson wrote:
ok, persistent fool that i am, i'm still trying to figure out how to use ZODB in an External Method to do some simple (ha!) database stuff.
so i built a very simple app, to implement a counter. each time you invoke the "Count" method, you get a number back, and it increments.
running the script (see below) by hand, it works fine. referencing it from Zope, using this dtml fragment:
<dtml-var "Count( 'chickens' )">
or just pointing my browser at: http://kestrel.sage.att.com:8080/Garry/ZODB/Count?name=chickens
to reference an External Method called Count, which refers to the Count function in this script, i get:
Error Type: AttributeError Error Value: Counter
if i then click the reload button, i get:
Could not lock the database file. There must be another process that has opened the file.
and i'm locked out until i restart zope. can someone please help? this is getting really discouraging. a simple explanation of where i'm going wrong would be greatly appreciated.
You cannot open the object database twice. In this case, Zope opens it first, and then you open it second (why you are getting the AttributeError I don't know, a traceback there would help). Basically what you are doing is sort-of a no-no unless you know what you are doing. First, a database cannot be opened twice, even from the same process. Second, you do not need to open the database again, because it is already open and accessible to you from your external method: def anExtMeth(self): connection = self._p_jar All persistent objects (in this case, 'self' being the container of the external method, probably a Folder) have an _p_jar attribute which is their connection to the database. Always use that instead of opening your own. Also, you need to be very careful with what you are doing. You are inserting objects right into the root level of the object database, this is not really a good idea. For example, let's say you fix your code to not open the database again and you get it working. Then (don't!) try this URL: http://kestrel.sage.att.com:8080/Garry/ZODB/Count?name=Application Doh! Guess what? You just trampled your entire Zope, hosed the entire enchilada, because Zope also lives in that top level namespace, and its name is 'Application'. Pick your own name (like GarryHodgsonApplication) and assign your counter objects as sub-attributes of that. -Michel
I would implement this whole thing as a Python product and avoid mucking around directly with the persistence machinery altogether. This would allow you to add a Counter object anywhere in the Zope tree. import OFS, Persistence class Counter(OFS.SimpleItem.Item, Persistence.Persistent): def __init__(self, id, title): self.id = id self.title = title self.counters = {} def Count(self, name): if not self.counters.has_key(name): self.counters[name] = 0 v = self.counters[name] + 1 self.counters[name] = v return v manage_addCounterForm=HTMLFile('methodAdd', globals()) def manage_addCounter(self, id, title, REQUEST=None): ob = Counter(id, title) self._setObject(id, ob) return self.manage_main(self, REQUEST) Add a Counter call 'counter' to a folder, then call: <dtml-var "counter.Count('chickens')"> -jfarr ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Hi! I'm a signature virus. Copy me into your .sig to join the fun! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Hi, Wasn't it generally agreed that storing a counter in a ZODB was a bad idea 'cos of the high number of refreshes, and hence the number of transactions stored in DB? Basically, because of the undo/rollback capability of ZODB, the data.fs grows rapidly if you use it to do counter-type things. Or an I wrong? Chris Jonothan Farr wrote:
I would implement this whole thing as a Python product and avoid mucking around directly with the persistence machinery altogether. This would allow you to add a Counter object anywhere in the Zope tree.
import OFS, Persistence
class Counter(OFS.SimpleItem.Item, Persistence.Persistent):
def __init__(self, id, title): self.id = id self.title = title self.counters = {}
def Count(self, name): if not self.counters.has_key(name): self.counters[name] = 0 v = self.counters[name] + 1 self.counters[name] = v return v
manage_addCounterForm=HTMLFile('methodAdd', globals())
def manage_addCounter(self, id, title, REQUEST=None): ob = Counter(id, title) self._setObject(id, ob) return self.manage_main(self, REQUEST)
Add a Counter call 'counter' to a folder, then call:
<dtml-var "counter.Count('chickens')">
-jfarr
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Hi! I'm a signature virus. Copy me into your .sig to join the fun! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
_______________________________________________ Zope maillist - Zope@zope.org http://lists.zope.org/mailman/listinfo/zope ** No cross posts or HTML encoding! ** (Related lists - http://lists.zope.org/mailman/listinfo/zope-announce http://lists.zope.org/mailman/listinfo/zope-dev )
Jonothan Farr wrote:
I would implement this whole thing as a Python product and avoid mucking around directly with the persistence machinery altogether. This would allow you to add a Counter object anywhere in the Zope tree.
thanks, i'll give that a try. i don't really need a counter, at all. it was just a minimal example to try and figure out ZODB. -- Garry Hodgson Every night garry@sage.att.com a child is born Software Innovation Services is a Holy Night. AT&T Labs - Sophia Lyon Fahs
Michel Pelletier wrote:
Garry Hodgson wrote:
ok, persistent fool that i am, i'm still trying to figure out how to use ZODB in an External Method to do some simple (ha!) database stuff.
You cannot open the object database twice.
that makes sense.
In this case, Zope opens it first, and then you open it second
ok, this i don't get. i'd have thought that since i opened what i thought was my own instance of a Zope DB, using my own FileStorage pointing to a file in /tmp: db = ZODB.DB( ZODB.FileStorage.FileStorage( '/tmp/foo.fs' ) ) connection = db.open() root = connection.root() that this would have nothing to do with the DB that Zope was using for its internals. all of the code examples i've seen for ZODB are from python programs, and all include code like this. if this *is* using Zope's internal DB instance, then that would imply that you can't use ZODB outside of Zope when Zope is running. what am i missing?
(why you are getting the AttributeError I don't know, a traceback there would help).
it appears that was unrelated. i actually got my example to work since posting. the "unable to access" thing only happens when i get an exception due to bugs in my code.
Basically what you are doing is sort-of a no-no unless you know what you are doing. First, a database cannot be opened twice, even from the same process. Second, you do not need to open the database again, because it is already open and accessible to you from your external method:
def anExtMeth(self): connection = self._p_jar
cool. that's good to know.
Also, you need to be very careful with what you are doing. You are inserting objects right into the root level of the object database, this is not really a good idea.
indeed. that's part of why i wanted to use a completely separate database. the other part is so i can use the same routines outside of Zope, perhaps at the same time.
Pick your own name (like GarryHodgsonApplication) and assign your counter objects as sub-attributes of that.
i'll try that. thanks for your help. -- Garry Hodgson Every night garry@sage.att.com a child is born Software Innovation Services is a Holy Night. AT&T Labs - Sophia Lyon Fahs
participants (4)
-
Chris Withers -
Garry Hodgson -
Jonothan Farr -
Michel Pelletier