commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnue] r7567 - trunk/gnue-common/src/datasources/drivers/Base


From: reinhard
Subject: [gnue] r7567 - trunk/gnue-common/src/datasources/drivers/Base
Date: Wed, 1 Jun 2005 16:18:13 -0500 (CDT)

Author: reinhard
Date: 2005-06-01 16:18:12 -0500 (Wed, 01 Jun 2005)
New Revision: 7567

Modified:
   trunk/gnue-common/src/datasources/drivers/Base/RecordSet.py
   trunk/gnue-common/src/datasources/drivers/Base/ResultSet.py
Log:
Last (last?? bwahahahaha) tweaks in RecordSet.


Modified: trunk/gnue-common/src/datasources/drivers/Base/RecordSet.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/RecordSet.py 2005-06-01 
19:55:00 UTC (rev 7566)
+++ trunk/gnue-common/src/datasources/drivers/Base/RecordSet.py 2005-06-01 
21:18:12 UTC (rev 7567)
@@ -39,17 +39,33 @@
 
   A RecordSet instance encapsulates a database record. Field values can be read
   and written, and the RecordSet keeps track of which fields have been changed.
-  When the L{post} method is called, the RecordSet uses the assigned
-  L{Connection} object to store the changes in the backend. After L{post} has
-  been called, L{requery} should be called subsequently to make sure that the
+  Read and write access to fields happens as if the RecordSet object was a
+  dictionary::
+    print myRecordSet [fieldname]
+    myRecordSet [fieldname] = 'foo'
+    field_dict = myRecordSet.copy ()
+    myRecordSet.update (updateDict)
+
+  The RecordSet object allows deletion of the underlying record from the
+  database with L{delete}, and - if used in a 3 tier environment - calling of a
+  server side procedure with L{call}.
+
+  A RecordSet keeps track of its own status. The L{isEmpty}, L{isInserted},
+  L{isModified}, L{isDeleted}, and L{isPending} functions are available to
+  check the current status of the record.
+
+  When the L{_post} method is called, the RecordSet uses the assigned
+  L{Connection} object to store the changes in the backend. After L{_post} has
+  been called, L{_requery} should be called subsequently to make sure that the
   RecordSet can requery all its field values from the database in case a server
-  side trigger has changed any of the fields. The L{post} and L{requery}
+  side trigger has changed any of the fields. The L{_post} and L{_requery}
   methods are separated so a caller can call the Connection's commit method in
-  between.
+  between. Both functions are called by the L{ResultSet} this RecordSet belongs
+  to.
 
   If the RecordSet is the master in a master/detail relationship, it is aware
-  of all its details. It notifies all its detail L{DataSource} objects whenever
-  it becomes the current record, and it posts/requeries all detail
+  of all its details. It notifies all its detail L{GDataSource} objects
+  whenever it becomes the current record, and it posts/requeries all detail
   L{ResultSet} objects after posting/requerying its own data.
 
   If the RecordSet is the detail in a master/detail relationship, it is not
@@ -120,8 +136,8 @@
     self.__dataSource       = dataSource
 
     # Record status
-    # New records:      'empty' -(setField)-> 'inserted' -(delete)-> 'void'
-    # Existing records: 'clean' -(setField)-> 'modified' -(delete)-> 'deleted'
+    # New recs:      'empty' -(__setitem__)-> 'inserted' -(delete)-> 'void'
+    # Existing recs: 'clean' -(__setitem__)-> 'modified' -(delete)-> 'deleted'
     self.__status = 'clean'
 
     # The field values
@@ -157,7 +173,7 @@
       # 1. mark as new
       self.__status = 'empty'
 
-      # 2. Get the default values from the driver
+      # 2. Get the default values from the backend
       if self.__connection:
         defaults = self.__connection.initialize (self.__tablename,
             self.__boundFields)
@@ -171,55 +187,55 @@
       for fieldname in self.__primarykeyFields:
         self.__modifiedFlags [fieldname] = True
 
-      # 4. Get default values from DataSource.
+      # 4. Get default values from DataSource (includes link to master).
       for (fieldname, value) in defaultData.items ():
         self.__setField (fieldname, value)
 
 
   # ---------------------------------------------------------------------------
-  # Field access
+  # String representation
   # ---------------------------------------------------------------------------
 
-  def getField (self, fieldname):
+  def __repr__ (self):
     """
-    Return the current value of a field.
+    Shows a string representation of the RecordSet.
+    """
 
+    if self.__tablename:
+      return "<RecordSet for %s at %d>" % (self.__tablename, id (self))
+    else:
+      return "<Unbound/Static RecordSet at %d>" % id (self)
+
+
+  # ---------------------------------------------------------------------------
+  # Dictionary emulation
+  # ---------------------------------------------------------------------------
+
+  def __getitem__ (self, fieldname):
+    """
+    Return the current value of a field, so the RecordSet can be used like a
+    dictionary.
+
     @param fieldname: Field name.
     @return: Field value.
     """
     if self.__fields.has_key (fieldname):
       return self.__fields [fieldname]
     else:
-      # FIXME: If a field value has yet to be set (either from a query or via a
-      # setField), then __fields will not contain a key for the requested field
-      # even though the field name may still be valid.
-      # This should be changed - the __fields dictionary should be initialized
-      # with all valid field names.
       return None
 
   # ---------------------------------------------------------------------------
 
-  def getFieldsAsDict (self):
+  def __setitem__ (self, fieldname, value):
     """
-    Return all fieldnames and values as a dictionary.
+    Set a new value for a field, so the RecordSet can be used like a
+    dictionary.
 
-    @return: A python dictionary of field name/value pairs.
-    """
-    return self.__fields.copy ()
-
-  # ---------------------------------------------------------------------------
-
-  def setField (self, fieldname, value):
-    """
-    Set a new value for a field.
-
     @param fieldname: Field name.
     @param value: Value to set.
     """
-
     if fieldname in self.__boundFields and self.__readonly:
       raise Exceptions.ReadOnlyModifyError
-
     self.__setField (fieldname, value)
     if fieldname in self.__boundFields:
       if self.__status in ['empty', 'clean']:
@@ -232,22 +248,82 @@
 
   # ---------------------------------------------------------------------------
 
-  def setFields (self, updateDict):
+  def items (self):
     """
-    Set new values for several fields at once.
+    Return a list of tuples of fieldname/value pairs, as if the RecordSet was a
+    dictionary.
+    """
+    return self.__fields.items ()
 
+  # ---------------------------------------------------------------------------
+
+  def keys (self):
+    """
+    Return the list of fieldnames, as if the RecordSet was a dictionary.
+    """
+    return self.__fields.keys ()
+
+  # ---------------------------------------------------------------------------
+
+  def values (self):
+    """
+    Return the list of field values, as if the RecordSet was a dictionary.
+    """
+    return self.__fields.values ()
+
+  # ---------------------------------------------------------------------------
+
+  def copy (self):
+    """
+    Return all fieldnames and values as a dictionary, as if the RecordSet was a
+    dictionary itself.
+    """
+    return self.__fields.copy ()
+
+  # ---------------------------------------------------------------------------
+
+  def update (self, updateDict):
+    """
+    Set new values for several fields at once, as if the RecordSet was a
+    dictionary.
+
     @param updateDict: dictionary with the keys being the field names and the
-        values being the new values for the fields
+      values being the new values for the fields.
     """
     for (fieldname, value) in updateDict.items ():
-      self.setField (fieldname, value)
+      self [fieldname] = value
 
+
   # ---------------------------------------------------------------------------
+  # Set a field and mark it as dirty, leaving the record state unchanged
+  # ---------------------------------------------------------------------------
 
+  def __setField (self, fieldname, value):
+
+    # Calling this function directly (instead of __setitem__) causes the field
+    # to be marked as dirty, but the complete record remains unchanged. This is
+    # useful to initialize a new record: the record will still be regarded as
+    # "empty", but the field will be included in any following insert
+    # statement.
+    self.__fields [fieldname] = value
+    self.__modifiedFlags [fieldname] = True
+
+
+  # ---------------------------------------------------------------------------
+  # Find out if a field is modified
+  # ---------------------------------------------------------------------------
+
   def isFieldModified (self, fieldname):
     """
     Determine whether a field of this record has local modifications.
 
+    This function seems to be used nowhere and might be removed at some point
+    because it can return unexpected results in a 3-tier environment: If a
+    server side procedure is called, all modification flags are reset (because
+    the record is posted to the backend), but the changes are not committed.
+
+    Please do not use this function.
+
     @param fieldname: Field name.
     @return: True if the field has local modifications, False otherwise.
     """
@@ -262,7 +338,8 @@
     """
     Mark the record as deleted.
 
-    The actual deletion occurs on the next call to the L{post} method.
+    The actual deletion occurs on the next call to the L{_post} method (to be
+    called via L{ResultSet.post}).
     """
 
     if self.__readonly:
@@ -275,6 +352,35 @@
 
 
   # ---------------------------------------------------------------------------
+  # Call backend code
+  # ---------------------------------------------------------------------------
+
+  def call (self, methodname, parameters):
+    """
+    Call a function of the backend.
+
+    It is highly recommended to post this record, it's chain of master records
+    and all of it's details before calling this function, so the backend is up
+    to date when executing the called backend method. L{GDataSource.postAll}
+    provides a convenient way to do this.
+
+    It is also recommended to requery all these records afterwards, so the
+    changes done to the data by the called function become visible.
+    L{GDataSource.requeryAll} provides a convenient way to do this.
+
+    @param methodname: Name of the function to call.
+    @param parameters: Dictionary with parametername/value pairs.
+    @return: Return value of the function that was called.
+    """
+
+    if self.isEmpty ():
+      raise errors.ApplicationError, u_("Function call on empty record")
+
+    return self.__connection.call (self.__tablename, self.__wherefields (),
+        methodname, parameters)
+
+
+  # ---------------------------------------------------------------------------
   # Status of this record
   # ---------------------------------------------------------------------------
 
@@ -346,13 +452,35 @@
 
 
   # ---------------------------------------------------------------------------
+  # Set clean data from a dictionary
+  # ---------------------------------------------------------------------------
+
+  def _initialDataFromDict (self, data):
+    """
+    Set the clean data of the record from a dictionary.
+
+    This is used when this record is in a detail ResultSet that has been
+    requeried completely.
+
+    @param data: Fieldname/value dictionary with the new clean data.
+    """
+
+    self.__initialData.update (data)
+    self.__fields.update (data)
+
+
+  # ---------------------------------------------------------------------------
   # Make this RecordSet the current one (notify all details)
   # ---------------------------------------------------------------------------
 
   def _activate (self):
     """
     Make this the current record, notifying all detail datasources.
+
+    This is called by the ResultSet whenever the record pointer is moved to
+    this record.
     """
+
     for dataSource in self.__details.keys ():
 
       # If we already have it in our cache, activate it
@@ -376,52 +504,16 @@
 
 
   # ---------------------------------------------------------------------------
-  # Fields to be used in WHERE clauses for UPDATE and DELETE.
-  # ---------------------------------------------------------------------------
-
-  def __wherefields (self):
-
-    result = {}
-
-    # First priority: row id
-    if self.__rowidField:
-      result [self.__rowidField] = self.__initialData [self.__rowidField]
-
-    # Second priority: primary key
-    elif self.__primarykeyFields:
-      for field in self.__primarykeyFields:
-        result [field] = self.__initialData [field]
-
-    # If all else fails, use all fields in the where clause
-    else:
-      for field in self.__boundFields:
-        if self.__initialData.has_key (field):
-          result [field] = self.__initialData [field]
-
-    return result
-
-
-  # ---------------------------------------------------------------------------
-  # Requery this record
-  # ---------------------------------------------------------------------------
-
-  def __do_requery (self, fields):
-
-    newfields = self.__connection.requery (self.__tablename,
-        self.__wherefields (), fields)
-    self.__initialData.update (newfields)
-    self.__fields.update (newfields)
-
-
-  # ---------------------------------------------------------------------------
   # Post changes to database
   # ---------------------------------------------------------------------------
 
-  def post (self, recordNumber = None):
+  def _post (self, recordNumber = None):
     """
     Write all local changes for this record to the backend, as
     well as for all detail records where this record is the master.
 
+    This is called by L{ResultSet.post} for each record with pending changes.
+
     @param recordNumber: Record number to be used in error messages.
     """
 
@@ -446,11 +538,11 @@
     # Check for empty primary key and set with the sequence value if so
     if self.__status in ['empty', 'inserted']:
       if len (self.__primarykeyFields) == 1 and \
-          self.getField (self.__primarykeyFields [0]) is None and \
+          self [self.__primarykeyFields [0]] is None and \
           self.__primarykeySeq is not None and \
           hasattr (self.__connection, 'getSequence'):
         pk = self.__connection.getSequence (self.__primarykeySeq)
-        self.setField (self.__primarykeyFields [0], pk)
+        self [self.__primarykeyFields [0]] = pk
 
     # If we have a connection (i.e. we aren't static or unbound), do the post
     if self.__connection is not None:
@@ -487,7 +579,7 @@
     for (dataSource, resultSet) in (self.__cachedDetailResultSets.items ()):
       fkData = {}
       for (pkField, fkField) in zip (*self.__details [dataSource]):
-        fkData [fkField] = self.getField (pkField)
+        fkData [fkField] = self [pkField]
       resultSet.post (fkData = fkData)
 
 
@@ -495,12 +587,12 @@
   # Requery the record data from the backend
   # ---------------------------------------------------------------------------
 
-  def requery (self):
+  def _requery (self):
     """
     Requery this record to reflect changes done by the backend.
 
-    This method may not be called if the record has unsaved changes; they would
-    get lost!
+    This is called by L{ResultSet.requery} for each record that has been posted
+    in the last L{ResultSet.post} call.
     """
 
     # First, requery ourselves
@@ -514,93 +606,73 @@
 
 
   # ---------------------------------------------------------------------------
-  # Call backend code
+  # Requery this record
   # ---------------------------------------------------------------------------
 
-  def call (self, methodname, parameters):
-    """
-    Call a function of the backend.
+  def __do_requery (self, fields):
 
-    It is highly recommended to post this record, it's chain of master records
-    and all of it's details before calling this function, so the backend is up
-    to date when executing the called backend method.
+    newfields = self.__connection.requery (self.__tablename,
+        self.__wherefields (), fields)
+    self.__initialData.update (newfields)
+    self.__fields.update (newfields)
 
-    @param methodname: Name of the function to call.
-    @param parameters: Dictionary with parametername/value pairs.
-    @return: Return value of the function that was called.
-    """
 
-    if self.isEmpty ():
-      raise errors.ApplicationError, u_("Function call on empty record")
-
-    return self.__connection.call (self.__tablename, self.__wherefields (),
-        methodname, parameters)
-
-
   # ---------------------------------------------------------------------------
-  # Set clean data from a dictionary
+  # Fields to be used in WHERE clauses for UPDATE and DELETE.
   # ---------------------------------------------------------------------------
 
-  def initialDataFromDict (self, data):
-    """
-    Set the clean data of the record from a dictionary.
+  def __wherefields (self):
 
-    This is used when this record is in a detail ResultSet that has been
-    requeried completely.
+    result = {}
 
-    @param data: Fieldname/value dictionary with the new clean data.
-    """
-    self.__initialData.update (data)
-    self.__fields.update (data)
+    # First priority: row id
+    if self.__rowidField:
+      result [self.__rowidField] = self.__initialData [self.__rowidField]
 
+    # Second priority: primary key
+    elif self.__primarykeyFields:
+      for field in self.__primarykeyFields:
+        result [field] = self.__initialData [field]
 
-  # ---------------------------------------------------------------------------
-  # Set a field and mark it as dirty, leaving the record state unchanged
-  # ---------------------------------------------------------------------------
+    # If all else fails, use all fields in the where clause
+    else:
+      for field in self.__boundFields:
+        if self.__initialData.has_key (field):
+          result [field] = self.__initialData [field]
 
-  def __setField (self, fieldname, value):
+    return result
 
-    # Calling this function directly (instead of setField) causes the field to
-    # be marked as dirty, but the complete record remains unchanged. This is
-    # useful to initialize a new record: the record will still be regarded as
-    # "empty", but the field will be included in any following insert
-    # statement.
-    self.__fields [fieldname] = value
-    self.__modifiedFlags [fieldname] = True
 
   # ---------------------------------------------------------------------------
-  # Dictionary emulation
+  # Depreciated methods (replaced by dictionary functions)
   # ---------------------------------------------------------------------------
 
-  def __setitem__(self, attr, val):
-    self.setField (attr, val)
+  def getField (self, fieldname):
+    """
+    Depreciated: use [fieldname] instead!
+    """
+    return self [fieldname]
 
   # ---------------------------------------------------------------------------
 
-  def __getitem__ (self, attr):
-    return self.getField (attr)
+  def setField (self, fieldname, value):
+    """
+    Depreciated: use [fieldname] instead!
+    """
+    self [fieldname] = value
 
   # ---------------------------------------------------------------------------
 
-  def keys (self):
-    return self.__fields.keys ()
+  def getFieldsAsDict (self):
+    """
+    Depreciated: use L{copy} instead!
+    """
+    return self.copy ()
 
   # ---------------------------------------------------------------------------
 
-  def values (self):
-    return self.__fields.values ()
-
-  # ---------------------------------------------------------------------------
-
-  def items (self):
-    return self.__fields.items ()
-
-  # ---------------------------------------------------------------------------
-  # Nice string representation
-  # ---------------------------------------------------------------------------
-
-  def __repr__ (self):
-    if self.__tablename:
-      return "<RecordSet for %s>" % self.__tablename
-    else:
-      return "<Unbound/Static RecordSet>"
+  def setFields (self, updateDict):
+    """
+    Depreciated: use L{update} instead!
+    """
+    self.update (updateDict)

Modified: trunk/gnue-common/src/datasources/drivers/Base/ResultSet.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/ResultSet.py 2005-06-01 
19:55:00 UTC (rev 7566)
+++ trunk/gnue-common/src/datasources/drivers/Base/ResultSet.py 2005-06-01 
21:18:12 UTC (rev 7567)
@@ -534,10 +534,10 @@
       # its primary key in a commit trigger
       if self._postingRecord.isInserted ():
         for (fieldname, value) in fkData.items ():
-          self._postingRecord.setField (fieldname, value)
+          self._postingRecord [fieldname] = value
 
       if self._postingRecord.isPending ():
-        self._postingRecord.post (recordPosition)
+        self._postingRecord._post (recordPosition)
 
     # Move to record 0 if all preceding records were deleted
     # (or set to -1 if all records were deleted)
@@ -563,7 +563,7 @@
     """
 
     for record in self.__recordsToRequery:
-      record.requery ()
+      record._requery ()
     self.__recordsToRequery = []
     self.__sync ()
 
@@ -610,7 +610,7 @@
           break
       if d:
         # Found in newData - update RecordSet
-        record.initialDataFromDict (d)
+        record._initialDataFromDict (d)
         # And set to empty dict to indicate it has been processed
         d.clear ()
       else:





reply via email to

[Prev in Thread] Current Thread [Next in Thread]