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