commit-gnue
[Top][All Lists]
Advanced

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

[gnue] r7361 - in trunk: gnue-common/src/datasources gnue-common/src/dat


From: reinhard
Subject: [gnue] r7361 - in trunk: gnue-common/src/datasources gnue-common/src/datasources/drivers/Base gnue-forms/src/GFObjects
Date: Wed, 13 Apr 2005 17:46:46 -0500 (CDT)

Author: reinhard
Date: 2005-04-13 17:46:45 -0500 (Wed, 13 Apr 2005)
New Revision: 7361

Modified:
   trunk/gnue-common/src/datasources/GDataSource.py
   trunk/gnue-common/src/datasources/drivers/Base/DataObject.py
   trunk/gnue-common/src/datasources/drivers/Base/RecordSet.py
   trunk/gnue-common/src/datasources/drivers/Base/ResultSet.py
   trunk/gnue-forms/src/GFObjects/GFBlock.py
Log:
Requery the complete detail result sets after a commit, and merge the result of
the new query with the existing result set, keeping record order of existing
records and cursor position intact.
Also, fixed pseudo-posting of clean records that have modified detail records.


Modified: trunk/gnue-common/src/datasources/GDataSource.py
===================================================================
--- trunk/gnue-common/src/datasources/GDataSource.py    2005-04-13 14:00:02 UTC 
(rev 7360)
+++ trunk/gnue-common/src/datasources/GDataSource.py    2005-04-13 22:46:45 UTC 
(rev 7361)
@@ -390,9 +390,11 @@
 
   def __setResultSet(self, resultSet):
     self._currentResultSet = resultSet
-    # Notify all the listeners (i.e., blocks) that the result set changed
+    self.notifyResultSetListeners ()
+
+  def notifyResultSetListeners (self):
     for listener in self._resultSetListeners:
-      listener(resultSet)
+      listener (self._currentResultSet)
 
   def registerResultSetListener(self, listener):
     self._resultSetListeners.append(listener)

Modified: trunk/gnue-common/src/datasources/drivers/Base/DataObject.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/DataObject.py        
2005-04-13 14:00:02 UTC (rev 7360)
+++ trunk/gnue-common/src/datasources/drivers/Base/DataObject.py        
2005-04-13 22:46:45 UTC (rev 7361)
@@ -75,6 +75,14 @@
 
     cond = GConditions.combineConditions (conditions, self._staticCondition)
 
+    # Add condition from master record
+    mastercond = {}
+    for (masterfield, detailfield) in \
+        zip (self._masterfields, self._detailfields):
+      mastercond [detailfield] = masterRecordSet.getField (masterfield)
+
+    cond = GConditions.combineConditions (cond, mastercond)
+
     resultset = self._resultSetClass (dataObject      = self,
                                       defaultValues   = self._defaultValues,
                                       readonly        = readOnly,
@@ -140,8 +148,8 @@
 
   # Called when new record master in master/detail is queried
   def _masterRecordChanged(self, master):
+
     GDebug.printMesg (8, 'Master Record Changed')
-    criteria = {}
 
     # If a detail result set has already been created for a particular
     # master record set, then just return/reuse this old set (after all,
@@ -149,21 +157,18 @@
     if (not master.current._cachedDetailResultSets.has_key(self)) or \
         ( not int(gConfig('CacheDetailRecords')) and \
           not master.current._cachedDetailResultSets[self].isPending() ):
-      doQuery = None
-      for i in range(0, len(self._masterfields)):
-        GDebug.printMesg(8,"Adding criteria")
-        criteria[string.strip(self._detailfields[i])] = \
-            master.current.getField(string.strip(self._masterfields[i]))
+      # If all are set to None then this will prevent the details
+      # from being queried.  This happens are startup with blank master
+      # datasources.
+      doQuery = False
+      for masterfield in self._masterfields:
+        if master.current.getField (masterfield):
+          doQuery = True
+          break
 
-        #If all are set to None then this will prevent the details
-        #from being queried.  This happens are startup with blank master
-        #datasources.
-        doQuery = doQuery or 
master.current.getField(string.strip(self._masterfields[i]))
-
-        GDebug.printMesg(8,master.current.getField(self._masterfields[i]))
       if doQuery:
         master.current.addDetailResultSet(self.createResultSet(\
-             conditions=criteria, masterRecordSet=master.current))
+             masterRecordSet=master.current))
       else:
         master.current.addDetailResultSet(self.createEmptyResultSet())
     return master.current._cachedDetailResultSets[self]

Modified: trunk/gnue-common/src/datasources/drivers/Base/RecordSet.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/RecordSet.py 2005-04-13 
14:00:02 UTC (rev 7360)
+++ trunk/gnue-common/src/datasources/drivers/Base/RecordSet.py 2005-04-13 
22:46:45 UTC (rev 7361)
@@ -359,7 +359,7 @@
     if hasattr(self._parent._dataObject,'_dataSource'):
       if self.__status in ['empty', 'inserted']:
         self._parent._dataObject._dataSource._beforeCommitInsert(self)
-      elif self.__status in ['clean', 'modified']:
+      elif self.__status == 'modified':
         self._parent._dataObject._dataSource._beforeCommitUpdate(self)
       elif self.__status == 'deleted':
         self._parent._dataObject._dataSource._beforeCommitDelete(self)
@@ -388,7 +388,7 @@
       if self.__status == 'deleted':
         self.__connection.delete (self.__tablename, self.__wherefields (),
             recordNumber)
-      else:
+      elif self.__status in ['empty', 'inserted', 'modified']:
         modifiedFields = {}
         for field in self.__boundFields:
           if self.__modifiedFlags.has_key (field):
@@ -431,17 +431,19 @@
     get lost!
     """
 
-    # Requery detail resultsets
-    for child in (self._cachedDetailResultSets.values ()):
-      child.requery ()
-
     # Now requery ourselves
     ds = self._parent._dataObject._dataSource
     if ds and ds.requery:
       if self.__rowidField or self.__primarykeyFields:
         self.__requery (self.__boundFields)
 
+    # Requery detail resultsets
+    for (dataObject, resultSet) in self._cachedDetailResultSets.items ():
+      newResultSet = dataObject.createResultSet (masterRecordSet = self)
+      resultSet.merge (newResultSet)
+      newResultSet.close ()
 
+
   # ---------------------------------------------------------------------------
   # Post and requery the top level master record
   # ---------------------------------------------------------------------------
@@ -524,6 +526,23 @@
 
 
   # ---------------------------------------------------------------------------
+  # 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)
+
+
+  # ---------------------------------------------------------------------------
   # Set the master link
   # ---------------------------------------------------------------------------
 

Modified: trunk/gnue-common/src/datasources/drivers/Base/ResultSet.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/ResultSet.py 2005-04-13 
14:00:02 UTC (rev 7360)
+++ trunk/gnue-common/src/datasources/drivers/Base/ResultSet.py 2005-04-13 
22:46:45 UTC (rev 7361)
@@ -68,10 +68,7 @@
 
      self.current = None
 
-     if masterRecordSet:
-       masterRecordSet.addDetailResultSet(self)
 
-
   # ---------------------------------------------------------------------------
   # Execute a query
   # ---------------------------------------------------------------------------
@@ -234,6 +231,31 @@
 
 
   # ---------------------------------------------------------------------------
+  # Get data as multi dimensional dictionary
+  # ---------------------------------------------------------------------------
+
+  def getDictArray (self, keyfields, fields):
+    """
+    This function returns the values of the given fields for all records as a
+    multidimensional dictionary. The record pointer is *not* moved.
+    """
+
+    # First, load all records into the cache
+    while self.__cacheNextRecord():
+      pass
+
+    # Now build up the array
+    result = {}
+    for record in self._cachedRecords:
+      d = result
+      for field in keyfields:
+        d = d.setdefault (record [field], {})
+      for field in fields:
+        d [field] = record [field]
+    return result
+
+
+  # ---------------------------------------------------------------------------
   # Notification of record navigation
   # ---------------------------------------------------------------------------
 
@@ -273,11 +295,17 @@
   # Sync self.current with self._currentRecord and adjust detail resultsets and
   # the user interface
   def __sync (self):
+    oldCurrent = self.current
     if self._currentRecord == -1:
       self.current = None
     else:
       self.current = self._cachedRecords [self._currentRecord]
-    self.notifyDetailObjects ()
+    # If called on requery, the current record doesn't change if the old
+    # current record wasn't deleted. In that case, we don't want to notify the
+    # detail objects (and reset the current record pointer of the details to
+    # the start).
+    if self.current != oldCurrent:
+      self.notifyDetailObjects ()
     self.__notifyListeners ()
 
 
@@ -470,10 +498,7 @@
           and self._dataObject._dataSource._connection is not None:
         # Adjust the current record if a preceding record or the current record
         # is deleted
-        if recordPosition <= self._currentRecord:
-          self._currentRecord -= 1
-        self._cachedRecords.pop (recordPosition)
-        self._recordCount -= 1
+        self.__removeRecord (recordPosition)
       else:
         recordPosition += 1
 
@@ -513,6 +538,87 @@
 
 
   # ---------------------------------------------------------------------------
+  # Merge another ResultSet into this one
+  # ---------------------------------------------------------------------------
+
+  def merge (self, otherResultSet):
+    """
+    Merge another (more current) ResultSet into this one.
+
+    This function is used by the master RecordSet to update it's detail
+    ResultSets after it has been committed to the backend (and some backend
+    triggers might have changed the details).
+
+    @param otherResultSet: the other ResultSet to merge
+    """
+
+    if self.__primarykeyFields:
+      keyFields = self.__primarykeyFields
+    elif self.__rowidField:
+      keyFields = [self.__rowidField]
+    else:
+      return
+
+    # Make sure that all records are cached
+    while self.__cacheNextRecord ():
+      pass
+
+    boundFields = self._dataObject._fieldReferences.keys ()
+
+    newData = otherResultSet.getDictArray (keyFields, boundFields)
+
+    for (index, record) in enumerate (self._cachedRecords [:]):
+      if record.isEmpty ():
+        # keep empty record in old ResultSet
+        continue
+      d = newData
+      for field in keyFields:
+        if d.has_key (record [field]):
+          d = d [record [field]]
+        else:
+          # Record is not in newData - it has been deleted meanwhile
+          d = None
+          break
+      if d:
+        # Found in newData - update RecordSet
+        record.initialDataFromDict (d)
+        # And set to empty dict to indicate it has been processed
+        d.clear ()
+      else:
+        # Not found in newData - delete it
+        self.__removeRecord (index)
+
+    # Add the rest of newData - it has been inserted
+    # Convert the multi dimensional dictionary into a list
+    l = newData.values ()
+    for field in keyFields [1:]:
+      l2 = []
+      for d in l:
+        l2 += d.values ()
+      l = l2
+    # Now for all non-empty dicts, append a new record
+    for row in l:
+      if row:
+        record = self.__createRecord (initialData = row)
+        self._dataObject._dataSource._onRecordLoaded (record)
+
+    # Move to record 0 if all preceding records were deleted
+    # (or set to -1 if all records were deleted)
+    if self._currentRecord < 0:
+      if len (self._cachedRecords):
+        self._currentRecord = 0
+      else:
+        self._currentRecord = -1
+
+    # We are completely up to date now.
+    self.__recordsToRequery = []
+
+    # Finally, tell everyone who wants to know that we have become a "new
+    # ResultSet" (i.e. the complete data should be redisplayed).
+    self._dataObject._dataSource.notifyResultSetListeners ()
+
+
+  # ---------------------------------------------------------------------------
   # Close the result set
   # ---------------------------------------------------------------------------
 
@@ -529,15 +635,28 @@
     for item in self._cachedRecords:
       item._parent = None
 
-    if self._dataObject._dataSource:
+    if self._dataObject._dataSource and \
+        self._dataObject._dataSource._currentResultSet == self:
       self._dataObject._dataSource.close ()
+      self._dataObject = None
 
-    self._dataObject    = None
     self._cachedRecords = []
     self.__listeners    = []
 
 
   # ---------------------------------------------------------------------------
+  # Remove a record from the list of cached records
+  # ---------------------------------------------------------------------------
+
+  def __removeRecord (self, index):
+
+    self._cachedRecords.pop (index)
+    self._recordCount -= 1
+    if index <= self._currentRecord:
+      self._currentRecord -= 1
+
+
+  # ---------------------------------------------------------------------------
   # Virtual methods
   # ---------------------------------------------------------------------------
 

Modified: trunk/gnue-forms/src/GFObjects/GFBlock.py
===================================================================
--- trunk/gnue-forms/src/GFObjects/GFBlock.py   2005-04-13 14:00:02 UTC (rev 
7360)
+++ trunk/gnue-forms/src/GFObjects/GFBlock.py   2005-04-13 22:46:45 UTC (rev 
7361)
@@ -300,35 +300,23 @@
     Load and process a resultset
     """
     self._resultSet = resultSet
-    self._currentRecord = -1
-    self._recordCount = 0
 
+    self._currentRecord = self._resultSet.getRecordNumber ()
+    self._recordCount = self._resultSet.getRecordCount ()
+
     # This makes the resultSet call our currentRecordMoved method
     resultSet.registerListener (self)
 
     # FIXME: Is this used anywhere?
     resultSet._block = self
 
-    if self._resultSet.firstRecord():
-      # self.switchRecord(0)            # happens via currentRecordMoved
-      self._recordCount = self._resultSet.getRecordCount()
-    else:
-      # This means no results were returned from a query.
-# #       if self._minChildRows:
-# #         # If datasource.detailmin is set, create some blank
-# #         # (but non-empty) records
-# #         while self._recordCount < self._minChildRows:
-# #           self.newRecord()
-# #           # Force the record to no longer be
-# #           field = self._fieldList[0]
-# #           field.setValue(field.getValue())
-# #         self.firstRecord()
-# #       else:
-        # Otherwise, give us a single empty record to work from
-        self.newRecord()
+    # If the ResultSet already has a current record loaded, stay there.
+    # Otherwise, jump to the first record. If there is none, create an empty
+    # record.
+    if self._currentRecord == -1 and not self._resultSet.firstRecord ():
+      self.newRecord ()
 
 
-
   #
   # isEmpty()
   #





reply via email to

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