[Zope] skinscript and ZSQL

Steve Spicklemire steve@spvi.com
Sun, 17 Dec 2000 05:33:21 -0500 (EST)


Hi Aaron,

   Since we communicated privately and I now have a much better
understanding of your problem, I realized that my last message to the
list was unclear so I thought I'd take a moment to try to clarify the
'gotchas' that got you, as I see them, so that other folks might not
so easily fall into the same problems. 

   Since you started with "DumbZPatternsExample":http://www.zope.org/Members/sspickle/DumbZPatternsExample
others might be able to follow along better if they refer to that 
example.

Situation: 

You're creating a class that is used as a 'stand-in' for
objects stored in an SQL database. You're using ZPatterns to implement
this stand-in-ability.

These objects have the following attributes (these also correspond to
column names for the table that lives in the SQL database, in your
case, gadfly.)

client_name, email, rate, last_payment, primary, and services.

client_name is used as the 'id' of the object when it is
instantiated in Zope.

In order to accomplish this you:

1) Create a ZClass (ClientObject) to act as the stand-in. 
 1a) set up methods of the ZClass 

  editInstance: self.propertysheets.Basic.manage_changeProperties(REQUEST)
  editInstanceForm: (form to edit the properties of an instance)
  editInstanceInterface: (UI method that calls editInstance to get modification done)
  index_html: (display this instance)

 1b) set up propertysheet for ZClass
  add properties: email, rate, last_payment, primary and services.
   do *not* set up a property for client_name since it is really
   going to be the 'id', and also the property that the rack uses to see
   if the object was found in the database. 

   [ If 'client_name' were propertysheet-property then the Rack could
   create a virtual instance, and then attemt to access the property
   'client_name'. This would then fire the trigger that queries the
   database, which would fail (leaving the virtual instance
   unchanged).  Since the virtual instance is an instance of your
   ZClass even though the query failed the Rack would *still* get a
   valid value for the property from the ZClass property sheet!
   This is why you were getting the 'already exists' error in
   the first place. Taking the 'client_name' property out of the
   ZClass fixes this problem. ]

2) Create a Specialist with a Rack that manages the ZClass instances
   that represent the objects from your database.

  2a) In the Rack set the Storage tab so that it uses ClientObject
   as the class of objects it manages, and choose "loaded by accessing attribute"
   rather than "stored persistently". Set the attribute to 'client_name'.

  2b) In the "Data PlugIns" tab clear out the persistent PlugIns and
   replace them with a single SkinScript method with the following
   lines:
   
   ------------------------------------------------------------

    WHEN OBJECT ADDED CALL sqlInsertProduct(client_name=self.id, 
    email=self.email, rate=self.rate, last_payment=self.last_payment, 
    primary=self.primary, services=self.services)
    
    WHEN OBJECT CHANGED STORE email, rate, last_payment, primary, services 
    USING sqlUpdateProduct(client_name=self.id, email=self.email, rate=self.rate, 
    last_payment=self.last_payment, primary=self.primary, services=self.services)
    
    WHEN OBJECT DELETED CALL sqlDeleteProduct(client_name=self.id)
    
    WITH QUERY lookup_product(client_name=self.id) COMPUTE 
    client_name, email,rate,last_payment,primary,services

    ------------------------------------------------------------

    (The 'STORE ..' part on the WHEN OBJECT CHANGED line is
    important to ensure that the Rack knows that these attributes
    are being stored with this command.)
    
  2c) Add ZSQL Methods, Python Methods/Scripts and DTML methods to the
    Speciliast so that it can carry out it's program.  Everything
    *but* the SQL methods are covered in the previously mentioned
    example. Here I'll only describe the ZSQL Methods used by the
    SkinScript:

    ZSQL Methods:
    
    sqlInsertProduct, args: 
    
    client_name email rate last_payment primary services
    
    code:
    
    INSERT INTO clienttracker_table
    (client_name, email, rate, last_payment, primary, services)
    VALUES
    (
    <dtml-sqlvar client_name type=string>,
    <dtml-sqlvar email type=string>,
    <dtml-sqlvar rate type=string>,
    <dtml-sqlvar last_payment type=string>, 
    <dtml-sqlvar primary type=string>, 
    <dtml-sqlvar services type=string> 
    )
    
    
    sqlUpdateProduct, args:
    
    client_name email rate last_payment primary services
    
    code:
    
    UPDATE clienttracker_table SET
    email=<dtml-sqlvar email type=string>, 
    rate=<dtml-sqlvar rate type=string>, 
    last_payment=<dtml-sqlvar last_payment type=string>, 
    primary=<dtml-sqlvar primary type=string>, 
    services=<dtml-sqlvar services type=string>
    where client_name=<dtml-sqlvar client_name type=string>
    
    sqlDeleteProduct, args:
    
    client_name
    
    code:
    
    DELETE FROM clienttracker_table WHERE 
    client_name = <dtml-sqlvar client_name type=string>
    
    lookup_product, args:
    
    client_name 
    
    code:
    
    select * from clienttracker_table where
    <dtml-sqltest client_name type=string>

  3) Since the items are no longer stored persistently, you need
    to change index_html to use a ZSQL Method to get the object
    ids, rather than defaultRack.getPersistentItemIDs(). In a 'real'
    situation this would probably be tied to some sort of search
    interface.

Good Luck!
-steve

    
>>>>> "AP" == Aaron Payne <aaron@aaronpayne.com> writes:

    AP> Hi all,

    AP> I'm trying to use Gadfly as storage for a Zpatterns rack.
    AP> With the following skinscript trigger I can view records in
    AP> the database.  But I can't insert a new one.

    AP> WHEN OBJECT ADDED CALL sqlInsertProduct()

    AP> WITH QUERY lookup_product(client_name=self.id) COMPUTE
    AP> client_name,email,rate,last_payment,primary,services

    AP> Where:

    AP> sqlInsertProduct is a zsql method.  args: client_name email
    AP> rate last_payment primary services Query template: INSERT INTO
    AP> clienttracker_table (client_name, email, rate, last_payment,
    AP> primary, services) VALUES ( '<dtml-var client_name>',
    AP> '<dtml-var email>', '<dtml-var rate>', '<dtml-var
    AP> last_payment>', '<dtml-var primary>', '<dtml-var services>' )

    AP> lookup_product is a zsql method.  args: client_name Query
    AP> template: select * from clienttracker_table where
    AP> <dtml-sqltest client_name type=string>


    AP> client_name,email,rate,last_payment,primary,services are
    AP> fields of the table clienttracker_table and properties of the
    AP> dataskin.

    AP> If I remove "=self.id" from the trigger I am able to add a
    AP> record to the db through the trigger.  Without "=self.id" in
    AP> the trigger I am unable to view the records in the db.


    AP> I noticed in a post today that said

    AP> ... you should probably choose some attribute other than 'id'
    AP> as the attribute to check for your DataSkin. The 'id'
    AP> attribute is always there...

    AP> Objects are loaded by accessing attribute client_name and I
    AP> still get the "object already exists" error.

    AP> Questions: -Should I just create the record directly with the
    AP> zsql method?  That would defeat the purpose of using
    AP> ZPatterns.  -How do I eliminate the "already exists" error

    AP> Zope version: Zope 2.2.1 Python version: 1.5.2 System
    AP> Platform: freebsd4

    AP> -Aaron


    AP> _______________________________________________ Zope maillist
    AP> - Zope@zope.org http://lists.zope.org/mailman/listinfo/zope **
    AP> No cross posts or HTML encoding!  ** (Related lists -
    AP> http://lists.zope.org/mailman/listinfo/zope-announce
    AP> http://lists.zope.org/mailman/listinfo/zope-dev )