1 |
|
# |
2 |
|
# This file is part of GNU Enterprise. |
3 |
|
# |
4 |
|
# GNU Enterprise is free software; you can redistribute it |
5 |
|
# and/or modify it under the terms of the GNU General Public |
6 |
|
# License as published by the Free Software Foundation; either |
7 |
|
# version 2, or (at your option) any later version. |
8 |
|
# |
9 |
|
# GNU Enterprise is distributed in the hope that it will be |
10 |
|
# useful, but WITHOUT ANY WARRANTY; without even the implied |
11 |
|
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
12 |
|
# PURPOSE. See the GNU General Public License for more details. |
13 |
|
# |
14 |
|
# You should have received a copy of the GNU General Public |
15 |
|
# License along with program; see the file COPYING. If not, |
16 |
|
# write to the Free Software Foundation, Inc., 59 Temple Place |
17 |
|
# - Suite 330, Boston, MA 02111-1307, USA. |
18 |
|
# |
19 |
|
# Copyright 2000-2003 Free Software Foundation |
20 |
|
# |
21 |
|
# FILE: |
22 |
|
# DataObject.py |
23 |
|
# |
24 |
|
# DESCRIPTION: |
25 |
|
# |
26 |
|
# NOTES: |
27 |
|
# |
28 |
|
|
29 |
|
__all__ = ['DataObject'] |
30 |
|
|
31 |
|
from ResultSet import ResultSet |
32 |
|
|
33 |
|
from gnue.common.apps import GDebug |
34 |
|
from gnue.common.datasources import GConditions, Exceptions |
35 |
|
import string |
36 |
|
|
37 |
|
from ResultSet import ResultSet |
38 |
|
|
39 |
|
########################################################### |
40 |
|
# |
41 |
|
# |
42 |
|
# |
43 |
|
########################################################### |
44 |
|
class DataObject: |
45 |
|
|
46 |
|
_resultSetClass = ResultSet |
47 |
|
|
48 |
|
def __init__(self, connection): |
49 |
|
self._connection = connection |
50 |
|
|
51 |
|
self.masterlink = "" |
52 |
|
self.detaillink = "" |
53 |
|
|
54 |
|
self._masterfields = [] |
55 |
|
self._detailfields = [] |
56 |
|
self._staticCondition = None |
57 |
|
|
58 |
|
self._masterObject = None |
59 |
|
self._detailObjects = [] |
60 |
|
self._dataConnection = None |
61 |
|
self._fieldReferences = {} # Set by GDataSource; lists all fields |
62 |
|
# a client explicitly references |
63 |
|
|
64 |
|
self._unboundFieldReferences = {} # Contains names of all unbound |
65 |
|
# field references |
66 |
|
|
67 |
|
self._defaultValues = {} |
68 |
|
|
69 |
|
self._unicodeMode = 0 # if set to true, datasources will unicode |
70 |
|
# strings instead of strings in local encoding |
71 |
|
# and if non unicode strings are passed to the |
72 |
|
# db driver a warning is raised (in 0.6.0 an |
73 |
|
# exception will be raised) |
74 |
|
|
75 |
|
# TODO: This is to keep old code from breaking. |
76 |
|
# TODO: 0.5.1 was last version to support |
77 |
|
# TODO triggerExtensions (functionality is now |
78 |
|
# TODO: encapsulated in Connection objects. |
79 |
|
self.triggerExtensions = connection |
80 |
|
|
81 |
|
# Do we have a master datasource? |
82 |
|
def hasMaster(self): |
83 |
|
return self._masterObject != None |
84 |
|
|
85 |
|
# Do not over-ride by vendor code |
86 |
|
def createResultSet(self, conditions={}, readOnly=0, masterRecordSet=None, sql=""): |
87 |
|
return self._createResultSet( |
88 |
|
GConditions.combineConditions(conditions, self._staticCondition), |
89 |
|
readOnly=readOnly, masterRecordSet=masterRecordSet, sql=sql) |
90 |
|
|
91 |
|
# Designed to be replaced by vendor-specific code |
92 |
|
def _createResultSet(self, conditions={}, readOnly=0, masterRecordSet=None, \ |
93 |
|
sql=""): |
94 |
|
pass |
95 |
|
|
96 |
|
# Do not over-ride by vendor code |
97 |
|
def getQueryString(self,conditions={},forDetailSQL=None,additionalSQL=""): |
98 |
|
return self._buildQuery(conditions,forDetailSQL,additionalSQL) |
99 |
|
|
100 |
|
def createEmptyResultSet(self, readOnly=0, masterRecordSet=None): |
101 |
|
return self._createEmptyResultSet(readOnly, masterRecordSet) |
102 |
|
|
103 |
|
# Designed to be replaced by vendor-specific code |
104 |
|
def _createEmptyResultSet(self, readOnly=0, masterRecordSet=None): |
105 |
|
cond = GConditions.GCondition() |
106 |
|
ceq = GConditions.GCeq(cond) |
107 |
|
GConditions.GCConst(ceq,1,"number") |
108 |
|
GConditions.GCConst(ceq,0,"number") |
109 |
|
return self.createResultSet(conditions=cond, readOnly=readOnly, |
110 |
|
masterRecordSet=masterRecordSet) |
111 |
|
|
112 |
|
|
113 |
|
# Add a detail data object. This dataobject will create a new resultset |
114 |
|
# everytime this dataobject changes (new record, etc). The optional |
115 |
|
# handler will be called after the detail dataobject is notified. The |
116 |
|
# client application may wish to add a handler to know when the detail |
117 |
|
# has been requeried. handler is a method that takes two arguments: |
118 |
|
# the master ResultSet and the detail ResultSet |
119 |
|
def addDetailDataObject(self, dataObject, handler=None, **params): |
120 |
|
|
121 |
|
dataObject.__dict__.update(params) |
122 |
|
|
123 |
|
GDebug.printMesg (1,"Adding a master/detail relationship to DataObject") |
124 |
|
dataObject._masterObject = self |
125 |
|
dataObject._masterfields = string.split(hasattr(dataObject,'masterlink') and \ |
126 |
|
string.lower(dataObject.masterlink) or "", ',') |
127 |
|
dataObject._detailfields = string.split(hasattr(dataObject,'detaillink') and \ |
128 |
|
string.lower(dataObject.detaillink) or "", ',') |
129 |
|
|
130 |
|
if len(dataObject._masterfields) != len(dataObject._detailfields): |
131 |
|
tmsg = _("master=%s; detail=%s") % (dataObject._masterfields, dataObject._detailfields) |
132 |
|
raise Exceptions.MasterDetailFieldMismatch, tmsg |
133 |
|
|
134 |
|
# Make sure "master" fields will be in our future query |
135 |
|
for field in dataObject._masterfields: |
136 |
|
self._fieldReferences[string.strip(field)] = "" |
137 |
|
|
138 |
|
for field in dataObject._detailfields: |
139 |
|
dataObject._fieldReferences[string.strip(field)] = "" |
140 |
|
|
141 |
|
self._detailObjects.append ([dataObject, handler]) |
142 |
|
|
143 |
|
# |
144 |
|
# Connect to database from a GConnection object |
145 |
|
# |
146 |
|
def connect(self): |
147 |
|
self._dataConnection = self._connection.native |
148 |
|
|
149 |
|
def commit(self): |
150 |
|
pass |
151 |
|
|
152 |
|
def rollback(self): |
153 |
|
pass |
154 |
|
|
155 |
|
|
156 |
|
# |
157 |
|
# Schema (metadata) functions |
158 |
|
# |
159 |
|
|
160 |
|
# TODO: DEPRECATED!!!!! |
161 |
|
def getSchemaTypes(self): |
162 |
|
print "WARNING: Your app is calling GDataObject.getSchemaTypes()" |
163 |
|
return self._connection.schema.types[:] |
164 |
|
|
165 |
|
# TODO: DEPRECATED!!!!! |
166 |
|
def getSchemaList(self, type=None): |
167 |
|
print "WARNING: Your app is calling GDataObject.getSchemaList()" |
168 |
|
return self._connection.schema.find(type=type) |
169 |
|
|
170 |
|
# TODO: DEPRECATED!!!!! |
171 |
|
def getSchemaByName(self, name, type=None): |
172 |
|
print "WARNING: Your app is calling GDataObject.getSchemaByName()" |
173 |
|
return self._connection.schema.findfirst(name=name, type=type) |
174 |
|
|
175 |
|
|
176 |
|
|
177 |
|
|
178 |
|
# Called when new record master in master/detail is queried |
179 |
|
def _masterRecordChanged(self, master): |
180 |
|
GDebug.printMesg (5, 'Master Record Changed') |
181 |
|
criteria = {} |
182 |
|
|
183 |
|
# If a detail result set has already been created for a particular |
184 |
|
# master record set, then just return/reuse this old set (after all, |
185 |
|
# it may contain uncommitted changes) |
186 |
|
if (not master.current._cachedDetailResultSets.has_key(self)) or \ |
187 |
|
( not int(gConfig('CacheDetailRecords')) and \ |
188 |
|
not master.current._cachedDetailResultSets[self].isPending() ): |
189 |
|
doQuery = None |
190 |
|
for i in range(0, len(self._masterfields)): |
191 |
|
GDebug.printMesg(10,"Adding criteria") |
192 |
|
criteria[string.strip(self._detailfields[i])] = \ |
193 |
|
master.current.getField(string.strip(self._masterfields[i])) |
194 |
|
|
195 |
|
#If all are set to None then this will prevent the details |
196 |
|
#from being queried. This happens are startup with blank master |
197 |
|
#datasources. |
198 |
|
doQuery = doQuery or master.current.getField(string.strip(self._masterfields[i])) |
199 |
|
|
200 |
|
GDebug.printMesg(10,master.current.getField(self._masterfields[i])) |
201 |
|
if doQuery: |
202 |
|
master.current.addDetailResultSet(self.createResultSet(\ |
203 |
|
conditions=criteria, masterRecordSet=master.current)) |
204 |
|
else: |
205 |
|
master.current.addDetailResultSet(self.createEmptyResultSet()) |
206 |
|
## master.current._cachedDetailResultSets[self] = \ |
207 |
|
## self.createResultSet(conditions=criteria, masterRecordSet=master.current) |
208 |
|
return master.current._cachedDetailResultSets[self] |
209 |
|
|