1 # -*- coding: utf-8 -*-
3 from pprint
import pformat
8 """Represents a row in a table, plus all data it is a 'parent' of.
10 Each Record may be linked to multiple Records in child tables.
14 def __init__(self
, data
=None, children
=None):
15 self
.data
= data
if data
is not None else {}
16 self
.children
= children
if children
is not None else {}
19 """Returns a dictionary representing the values in the record."""
21 for key
in self
.data
.keys():
22 data_d
[key
] = self
.data
[key
]
24 for table
, records
in self
.children
.iteritems():
25 children_d
[table
] = [record
.as_dict() for record
in records
]
28 result_d
[u
"data"] = data_d
29 if len(children_d
) > 0:
30 result_d
[u
"children"] = children_d
33 def find_children(self
, *args
):
34 """Returns child records for the given string arguments.
36 Each argument is used as a key for selecting a group out of
37 the next generation of child objects. For example, the args
38 ["sense", "gloss"] will return a list of all gloss records.
40 Returns a list of records, or an empty list if none are found.
45 record_lists
= [record
.children
.get(key
, []) for record
in records
]
46 # Merge the lists into one.
47 records
= reduce(lambda x
, y
: x
+ y
, record_lists
)
50 def __unicode__(self
):
51 return pformat(self
.as_dict())
53 __repr__
= __unicode__
58 """Base class for tables.
60 Given SQL template queries, provides table create/insert logic and
61 attaches indices if desired.
63 create_query and insert_query must be specified in child classes.
64 index_queries is not required.
66 create_query and insert_query take a single template argument: the
67 table name. Do not put the table name in directly; replace it
70 index_queries are a little more complicated. They should be
73 CREATE INDEX %s_XXX ON %s (YYY)
75 where %s is a placeholder for the table name (note that it's used
76 twice), and XXX/YYY are replaced as desired.
84 def __init__(self
, cursor
, name
):
89 """Creates table, plus indices if supplied in class definition."""
90 query
= self
._get
_create
_query
()
92 self
.cursor
.execute(query
)
93 index_queries
= self
._get
_index
_queries
()
94 for query
in index_queries
:
96 self
.cursor
.execute(query
)
98 def insert(self
, *args
):
99 """Runs an insert with the specified arguments.
101 Returns the row id of the insert. (cursor.lastrowid)
104 query
= self
._get
_insert
_query
()
107 self
.cursor
.execute(query
, args
)
109 print("Exception occurred on insert: query=%s, args=%s" %
110 (repr(query
), repr(args
)))
112 return self
.cursor
.lastrowid
114 def insertmany(self
, *args
):
115 """Runs a multi-row insert with the specified arguments.
117 Arguments are treated similarly to the DB-API 2.0 executemany
118 statement: this function takes a list of argument lists.
120 There is no return value.
123 query
= self
._get
_insert
_query
()
125 self
.cursor
.executemany(query
, *args
)
127 print("Exception occurred on insertmany: query=%s, args=%s" %
128 (repr(query
), repr(args
)))
131 def _get_create_query(self
):
132 if self
.name
is None:
134 "name must be specified in class definition")
135 return self
.create_query
% self
.name
137 def _get_insert_query(self
):
138 if self
.name
is None:
140 "name must be specified in class definition")
141 return self
.insert_query
% self
.name
143 def _get_index_queries(self
):
144 if self
.name
is None:
146 "name must be specified in class definition")
147 if (not (isinstance(self
.index_queries
, list))
148 or (len(self
.index_queries
) == 0)):
151 # Each query needs to have the table name merged in two
153 queries
= [q
% (self
.name
, self
.name
)
154 for q
in self
.index_queries
]
157 def lookup_by_id(self
, id):
158 """Retrieves the row matching the id.
160 Returns the matching row, or None if no row was found.
163 query
= "SELECT * FROM %s WHERE id = ?" % self
.name
164 self
.cursor
.execute(query
, (id,))
165 row
= self
.cursor
.fetchone()
169 class ChildTable(Table
):
171 """Table using a foreign key column for parent-child relationships."""
173 def lookup_by_fk(self
, fk
):
174 """Retrieves all rows with the matching foreign key.
176 Returns a list of rows. If no rows are found, an empty list
180 query
= "SELECT * FROM %s WHERE fk = ?" % self
.name
181 self
.cursor
.execute(query
, (fk
,))
182 rows
= self
.cursor
.fetchall()
186 class KeyValueTable(ChildTable
):
187 """General key/value table for one-many relations."""
188 create_query
= ("CREATE TABLE %s "
189 "(id INTEGER PRIMARY KEY, fk INTEGER, value TEXT)")
190 insert_query
= "INSERT INTO %s VALUES (NULL, ?, ?)"
192 "CREATE INDEX %s_fk ON %s (fk)",