[Top][All Lists]
[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()
#
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r7361 - in trunk: gnue-common/src/datasources gnue-common/src/datasources/drivers/Base gnue-forms/src/GFObjects,
reinhard <=