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"
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, row):
130 def sync(self, commit = True):
132 Flush changes back to the database.
135 for row in self.values():
138 def selectall(self, sql, params = None):
140 Given a list of rows from the database, fill ourselves with
141 Row objects keyed on the primary key defined by the Row class
142 we were initialized with.
145 for row in self.api.db.selectall(sql, params):
146 self[row[self.row.primary_key]] = self.row(self.api, row)