[Zope-dev] ZPatterns AttributeProvider question

Christian Scholz cs@comlounge.net
Mon, 23 Apr 2001 19:05:09 +0200


Hi!

So here's more :)

> >    def _objectAdding(self,client,
> >        _tmap={ None:AddedStatus,
> >                DeletedStatus:ChangedStatus,
> >                ChangedStatus:AddedStatus
> >            }
> >        ):
> >        t = client._getTokenFor(self)
> >        s = t.status
> >        t.status = _tmap.get(s,s)
> >
> >        # we need to do a commit here in order to store the record into
> the database!
> >	# and we need to have it stored as an AttributeFor() of the primary key will
> >	# return None otherwise (as this also just makes a query).
> >        client.commitSubtransaction()
> 
> This is broken.  Don't do this!  commitSubtransaction() is an
> application-level operation.  Only.  If you have to use it inside of
> provider-level code or code called from SkinScript, chances are that you
> are doing something horribly wrong.  You run the risk of causing a
> recursive commit operation in the Zope transaction machinery.  Don't do this!

well, it always looked no-good to me, I just didn't know how to solve my problem.

So my problem I was trying to fix was actually the following:

- When creating a new object via newItem(), the Rack checks if self.loadAttrib is defined
  before creating a new one.
  if loadAttrib is returned by the attribute provider then the Rack assumes it already
  exists and raises an exception.

- My Attribute Provider now does not know if an object exists or not before looking into
  the database (I tried different ways to check it but somehow they haven't worked).
  So it does that. If it finds a record with the given id it returns it, if not it returns
  the default value (which means "does not exist" to the rack).

- When adding a new object in my provider it does an insert via an ZSQL method
  to store it inside the database. This is done in _objectAdded().
  Unfortunately this is too late as before this is called, we already need to have
  it in the database as the request to my primary key will return the default still.
  So I moved this insert to _objectAdding().
  Unfortunately this new record seems not to appear in the database before commit()
  (dunno why, maybe some new transaction code in ZMySQLDA? haven't checked this).
  That's why I did a commit() there which then called _objectAdded().

So my problem is basically that I need to know in _AttributeFor() whether this
object is already created or not.. I will check now again with asking the status
of the client but I think I also tried this before.
The best would be to collect all data before _objectAdded() and store it at once
and not as it's done now by first creating an empty record and doing the rest
via _objectChanged().

But I am open for any other idea :) 

> >    def _SetAttributeFor(self,client,name,value):
> >        """Set the attribute and return true if successful"""
> >	# attribs stores the attributes we care about
> >        if name in self._attribs.keys():
> >            client._getCache()[name]=value
> >            client.__dict__[name]=value; 
> >	    client._p_changed = 1
> >            return 1
> >        return None
> 
> This also looks broken to me.  Why are you changing both the cache and the
> persistent state?  There's no reason to do both.

ok, this is old code where I wanted to test some other solution to fix my problem.
I will remove the additional line.. (__dict__ might be all I need, it's already a cache
if I remember right?!?)


> I think you're seriously over-engineering this.  I don't see anything that
> you're doing here that couldn't be done more easily with SkinScript
> statements.  If all you want to do is store the data in an SQL database, a
> few SkinScript triggers would suffice.

Well, as mailed yesterday I know this. I've also done this a lot but was then
tired of always typing those paramater lists (for each ZSQL method, for each SkinScript
call twice, ...).
This is of course always the tradeoff between easy to use plugins which are somehow
restricted and the more flexible ones which do need lots of configuration.
I would like to have the first one for the day-to-day-stuff and the latter
for more advanced operations, like linking specialists together etc.

> Now, if you're trying to get a row to be inserted in an SQL database at the
> actual time of the newItem() call, there is a trick you can use to do that
> from SkinScript also.  Do this:
> 
> INITIALIZE OBJECT WITH someAttr1=default1,
> someAttr2=default2,...,dummyAttributeName=myInsertQuery(primaryKey=self.id)
> 
> Where myInsertQuery() is an SQL insert query that takes primaryKey as a
> parameter, and dummyAttributeName is some attribute name you don't care
> about.  You should also set any default values for fields that will be
> inserted.  Your insert query will be called, initializing the row in the
> database, and then use something like this:
> 
> WHEN OBJECT ADDED,CHANGED STORE foo,bar,baz USING
> UpdateMethod(widget_id=self.id, foo_field=self.foo, wbar=self.bar)
> 
> 
> To update the data at transaction commit time.  Voila.  No need to make any
> custom attribute providers just to do this.

Actually when using SkinScript I never had the need for directly putting
the new row into the db, as then I haven't had the loadAttrib problem ;-)

> Just to clarify the event sequence that should take place:
> 
> 1. newItem() is called, which fires an _objectCreating() event
> 
> 1.1. The SkinScript INITIALIZE OBJECT statement is triggered by the
> _objectCreating() event, and calls myInsertQuery() to insert a row in the
> database, with default values for everything but the primary key.

Hm, _objectCreating() is a method of which object? Can I define it in some
agent or is it a method of the DataSkin or a Rack?

I will read more closely about the rest when I get back to programming,
so I might mail then again.. (if you don't mind).

> Whew.  The above is a vastly simplified picture of the whole sequence,
> leaving out a lot of things such as how a statement can actually be
> responsible for many dataskins at once, the state machine that agents use
> to decide which event is considered to have occurred, etc.

There aren't any UML diagrams of all this btw? ;-)

ok, thanks for the enlightenment! :)

cheers,
  christian

-- 
COM.lounge                                          http://comlounge.net/
communication & design                                 info@comlounge.net