1 from PLC.Faults import *
2 from PLC.Parameter import Parameter
6 Representation of a row in a database table. To use, optionally
7 instantiate with a dict of values. Update as you would a
8 dict. Commit to the database with sync().
11 # Set this to the name of the table that stores the row.
14 # Set this to the name of the primary key of the table. It is
15 # assumed that the this key is a sequence if it is not set when
19 # Set this to the names of tables that reference this table's
23 # Set this to a dict of the valid fields of this object and their
24 # types. Not all fields (e.g., joined fields) may be updated via
28 def __init__(self, api, fields = {}):
29 dict.__init__(self, fields)
34 Validates values. Will validate a value with a custom function
35 if a function named 'validate_[key]' exists.
38 # Warn about mandatory fields
39 mandatory_fields = self.api.db.fields(self.table_name, notnull = True, hasdef = False)
40 for field in mandatory_fields:
41 if not self.has_key(field) or self[field] is None:
42 raise PLCInvalidArgument, field + " must be specified and cannot be unset in class %s"%self.__class__.__name__
44 # Validate values before committing
45 for key, value in self.iteritems():
46 if value is not None and hasattr(self, 'validate_' + key):
47 validate = getattr(self, 'validate_' + key)
48 self[key] = validate(value)
50 def sync(self, commit = True, insert = None):
52 Flush changes back to the database.
55 # Validate all specified fields
58 # Filter out fields that cannot be set or updated directly
59 all_fields = self.api.db.fields(self.table_name)
60 fields = dict(filter(lambda (key, value): \
61 key in all_fields and \
62 (key not in self.fields or \
63 not isinstance(self.fields[key], Parameter) or \
64 not self.fields[key].ro),
67 # Parameterize for safety
69 values = [self.api.db.param(key, value) for (key, value) in fields.items()]
71 # If the primary key (usually an auto-incrementing serial
72 # identifier) has not been specified, or the primary key is the
73 # only field in the table, or insert has been forced.
74 if not self.has_key(self.primary_key) or \
75 all_fields == [self.primary_key] or \
78 sql = "INSERT INTO %s (%s) VALUES (%s)" % \
79 (self.table_name, ", ".join(keys), ", ".join(values))
82 columns = ["%s = %s" % (key, value) for (key, value) in zip(keys, values)]
83 sql = "UPDATE %s SET " % self.table_name + \
84 ", ".join(columns) + \
87 self.api.db.param(self.primary_key, self[self.primary_key]))
89 self.api.db.do(sql, fields)
91 if not self.has_key(self.primary_key):
92 self[self.primary_key] = self.api.db.last_insert_id(self.table_name, self.primary_key)
97 def delete(self, commit = True):
99 Delete row from its primary table, and from any tables that
103 assert self.primary_key in self
105 for table in self.join_tables + [self.table_name]:
106 if isinstance(table, tuple):
110 key = self.primary_key
112 sql = "DELETE FROM %s WHERE %s = %s" % \
114 self.api.db.param(self.primary_key, self[self.primary_key]))
116 self.api.db.do(sql, self)
123 Representation of row(s) in a database table.
126 def __init__(self, api, classobj, columns = None):
128 self.classobj = classobj
132 columns = classobj.fields
134 columns = filter(lambda x: x in classobj.fields, columns)
136 raise PLCInvalidArgument, "No valid return fields specified"
138 self.columns = columns
140 def sync(self, commit = True):
142 Flush changes back to the database.
148 def selectall(self, sql, params = None):
150 Given a list of rows from the database, fill ourselves with
154 for row in self.api.db.selectall(sql, params):
155 obj = self.classobj(self.api, row)
158 def dict(self, key_field = None):
160 Return ourself as a dict keyed on key_field.
163 if key_field is None:
164 key_field = self.classobj.primary_key
166 return dict([(obj[key_field], obj) for obj in self])