dabo Commit
Revision 4986
Date: 2009-01-28 19:35:04 -0800 (Wed, 28 Jan 2009)
Author: Ed
Trac: http://trac.dabodev.com/changeset/4986
Changed:
U trunk/dabo/biz/dBizobj.py
U trunk/dabo/db/dCursorMixin.py
U trunk/dabo/db/dDataSet.py
U trunk/dabo/ui/uiwx/dForm.py
Log:
Added the ability to filter data in the current dataset without requerying the
database. In order to ensure that the UI is properly updated, you should call
dForm.filter().
There are still issues to resolve when using filter(). One situation is if you
change a record, and then apply a filter that 'removes' that record from the
current data set. Most of our scan routines are based on RowNumber, and this is
completely broken by filtering. So for now I'm adding this with the warning
that some things affected by this are not rock-solid by any means.
Diff:
Modified: trunk/dabo/biz/dBizobj.py
===================================================================
--- trunk/dabo/biz/dBizobj.py 2009-01-28 22:33:33 UTC (rev 4985)
+++ trunk/dabo/biz/dBizobj.py 2009-01-29 03:35:04 UTC (rev 4986)
@@ -757,6 +757,8 @@
else:
cursors = {old_currentCursorKey: currCursor}
+ dabo.trace()
+
for key, cursor in cursors.iteritems():
self._CurrentCursor = key
changedRows = self.getChangedRows(includeNewUnchanged)
@@ -966,6 +968,47 @@
self.__params = params
+ def filter(self, fld, expr, op="="):
+ """
+ This takes a field name, an expression, and an optional
operator, and applies that
+ to the current dataset. The original dataset is preserved;
calling removeFilter() will
+ remove the last filter applied to the bizobj. If the current
record is in the filtered
+ dataset, that record will still be current; if it is filtered
out, the current row will
+ be row 0.
+ If the operator is specified, it will be used literally in the
evaluation instead of the
+ equals sign, unless it is one of the following strings, which
will be interpreted
+ as indicated:
+ eq, equals: =
+ ne, nequals: !=
+ gt: >
+ gte: >=
+ lt: <
+ lte: <=
+ startswith, beginswith: fld.startswith(expr)
+ endswith: fld.endswith(expr)
+ contains: expr in fld
+ """
+ currPK = self.getPK()
+ self._CurrentCursor.filter(fld=fld, expr=expr, op=op)
+ newPK = self.getPK()
+ if newPK != currPK:
+ try:
+ self.moveToPK(currPK)
+ except dabo.dException.RowNotFoundException:
+ # The old row was filtered out of the dataset
+ self.first()
+
+
+ def removeFilter(self):
+ """Remove the most recently applied filter."""
+ self._CurrentCursor.removeFilter()
+
+
+ def removeFilters(self):
+ """Remove all applied filters, going back to the original data
set."""
+ self._CurrentCursor.removeFilters()
+
+
def _validate(self):
""" Internal method. User code should override validateRecord().
Modified: trunk/dabo/db/dCursorMixin.py
===================================================================
--- trunk/dabo/db/dCursorMixin.py 2009-01-28 22:33:33 UTC (rev 4985)
+++ trunk/dabo/db/dCursorMixin.py 2009-01-29 03:35:04 UTC (rev 4986)
@@ -165,7 +165,7 @@
"""Returns the PK expression for the passed record."""
if rec is None:
try:
- rec = self._records[self.RowNumber]
+ rec =
self._records.UnfilteredDataSet[self.RowNumber]
except IndexError:
rec = {}
if isinstance(self.KeyField, tuple):
@@ -372,11 +372,8 @@
field_name=fld,
_newQuery=_newQuery)
self._records = dDataSet(_records)
- if self.RowCount > 0:
- self.RowNumber = max(0, self.RowNumber)
- maxrow = max(0, (self.RowCount-1) )
- self.RowNumber = min(self.RowNumber, maxrow)
-
+ # This will handle bounds issues
+ self.RowNumber = self.RowNumber
return res
@@ -816,7 +813,6 @@
raise dException.NoRecordsException(_("No records in
the data set."))
if row is None:
row = self.RowNumber
-
try:
rec = self._records[row]
except IndexError:
@@ -1140,7 +1136,7 @@
"""Creates a copy of the current record and adds it to the
dataset."""
if not self.RowCount:
raise dException.NoRecordsException(_("No records in
the data set."))
- rec = self._records[self.RowNumber]
+ rec = self._records[self.RowNumber].copy()
if self.AutoPopulatePK:
kf = self.KeyField
blank = self._getBlankRecord()
@@ -1180,6 +1176,21 @@
pass
+ def filter(self, fld, expr, op="="):
+ """Apply a filter to the current records."""
+ self._records = self._records.filter(fld=fld, expr=expr, op=op)
+
+
+ def removeFilter(self):
+ """Remove the most recently applied filter."""
+ self._records = self._records.removeFilter()
+
+
+ def removeFilters(self):
+ """Remove all applied filters, going back to the original data
set."""
+ self._records = self._records.removeFilters()
+
+
def replace(self, field, valOrExpr, scope=None):
"""Replaces the value of the specified field with the given
value
or expression. All records matching the scope are affected; if
@@ -1189,11 +1200,10 @@
with an equals sign. All expressions will therefore be a string
beginning with '='. Literals can be of any type.
"""
- if isinstance(self._records, dDataSet):
- # Make sure that the data set object has any necessary
references
- self._records.Cursor = self
- self._records.Bizobj = self._bizobj
- self._records.replace(field, valOrExpr, scope=scope)
+ # Make sure that the data set object has any necessary
references
+ self._records.Cursor = self
+ self._records.Bizobj = self._bizobj
+ self._records.replace(field, valOrExpr, scope=scope)
def first(self):
@@ -1287,6 +1297,10 @@
rec = self._records[row]
recKey = self.pkExpression(rec)
newrec = kons.CURSOR_TMPKEY_FIELD in rec
+
+ print "newrec = ", newrec
+ print "recKey = ", recKey
+
newPKVal = None
if newrec and self.AutoPopulatePK:
# Some backends do not provide a means to retrieve
@@ -1505,7 +1519,7 @@
# Faster to deal with 2 specific cases: all rows or just
current row
if allRows:
- recs = self._records
+ recs = self._records.UnfilteredDataSet
if self._newRecords:
recs = list(recs)
@@ -2490,14 +2504,16 @@
def _getRowNumber(self):
try:
- ret = self.__rownumber
+ ret = min(self.__rownumber, self._getRowCount()-1)
except AttributeError:
ret = -1
return ret
def _setRowNumber(self, num):
- self.__rownumber = num
+ rn = max(0, num)
+ maxrow = max(0, (self.RowCount-1) )
+ self.__rownumber = min(rn, maxrow)
def _getTable(self):
Modified: trunk/dabo/db/dDataSet.py
===================================================================
--- trunk/dabo/db/dDataSet.py 2009-01-28 22:33:33 UTC (rev 4985)
+++ trunk/dabo/db/dDataSet.py 2009-01-29 03:35:04 UTC (rev 4986)
@@ -192,6 +192,9 @@
stmnt = "select * from dataset where %s %s" % (fld,
clause)
ret = self.execute(stmnt, (param, ))
ret._sourceDataSet = self
+ ret._filtered_fld = fld
+ ret._filtered_expr = expr
+ ret._filtered_op = op
return ret
@@ -420,6 +423,13 @@
pass
+ def _getUnfilteredDataSet(self):
+ ret = self
+ while ret._sourceDataSet:
+ ret = ret._sourceDataSet
+ return ret
+
+
Bizobj = property(_getBizobj, _setBizobj, None,
_("Reference to the bizobj that 'owns' this data set.
Default=None (bizobj)"))
@@ -429,9 +439,12 @@
Encoding = property(_getEncoding, _setEncoding, None,
_(" The encoding used for data in the dataset. (str)"))
+ UnfilteredDataSet = property(_getUnfilteredDataSet, None, None,
+ _("""If filters have been applied, returns the
unfiltered dataset that would be returned if removeFilters() had been called.
If no filters have been applied, returns self (dDataSet)"""))
+
# class DataSetOld(tuple):
# """ This class assumes that its contents are not ordinary tuples, but
# rather tuples consisting of dicts, where the dict keys are field names.
Modified: trunk/dabo/ui/uiwx/dForm.py
===================================================================
--- trunk/dabo/ui/uiwx/dForm.py 2009-01-28 22:33:33 UTC (rev 4985)
+++ trunk/dabo/ui/uiwx/dForm.py 2009-01-29 03:35:04 UTC (rev 4986)
@@ -275,6 +275,7 @@
self._moveRecordPointer(bizobj.first, dataSource)
self.afterFirst()
+
def last(self, dataSource=None):
""" Ask the bizobj to move to the last record."""
bizobj = self.getBizobj(dataSource)
@@ -288,6 +289,7 @@
self._moveRecordPointer(bizobj.last, dataSource)
self.afterLast()
+
def prior(self, dataSource=None):
""" Ask the bizobj to move to the previous record."""
bizobj = self.getBizobj(dataSource)
@@ -300,7 +302,8 @@
return
self._moveRecordPointer(bizobj.prior, dataSource)
self.afterPrior()
-
+
+
def next(self, dataSource=None):
""" Ask the bizobj to move to the next record."""
bizobj = self.getBizobj(dataSource)
@@ -315,6 +318,41 @@
self.afterNext()
+ def filter(self, dataSource=None, fld=None, expr=None, op="="):
+ """Apply a filter to the bizobj's data."""
+ bizobj = self.getBizobj(dataSource)
+ if bizobj is None:
+ # Running in preview or some other non-live mode
+ return
+ # Make sure that they passed 'fld' and 'expr'
+ if fld is None or expr is None:
+ raise ValueError(_("Both 'fld' and 'expr' need to be
supplied to the call to filter()."))
+ err = self.beforeFilter()
+ if err:
+ self.notifyUser(err)
+ return
+ self._moveRecordPointer(bizobj.filter, dataSource, fld=fld,
expr=expr, op=op)
+ self.afterFilter()
+
+
+ def removeFilter(self, dataSource=None):
+ """Remove the most recently applied filter from the bizobj's
data."""
+ bizobj = self.getBizobj(dataSource)
+ if bizobj is None:
+ # Running in preview or some other non-live mode
+ return
+ self._moveRecordPointer(bizobj.removeFilter, dataSource)
+
+
+ def removeFilters(self, dataSource=None):
+ """Remove all filters from the bizobj's data."""
+ bizobj = self.getBizobj(dataSource)
+ if bizobj is None:
+ # Running in preview or some other non-live mode
+ return
+ self._moveRecordPointer(bizobj.removeFilters, dataSource)
+
+
def save(self, dataSource=None):
""" Ask the bizobj to commit its changes to the backend."""
bizobj = self.getBizobj(dataSource)
@@ -666,7 +704,8 @@
def beforeLast(self): pass
def beforePrior(self): pass
def beforeNext(self): pass
- def beforeSave(self): pass
+ def beforeNext(self): pass
+ def beforeFilter(self): pass
def beforeCancel(self): pass
def beforeRequery(self): pass
def beforeDelete(self): pass
@@ -677,6 +716,7 @@
def afterLast(self): pass
def afterPrior(self): pass
def afterNext(self): pass
+ def afterFilter(self): pass
def afterSave(self): pass
def afterCancel(self): pass
def afterRequery(self): pass
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev
Searchable Archives: http://leafe.com/archives/search/dabo-dev
This message:
http://leafe.com/archives/byMID/[email protected]