|
1 | 1 | import sqlalchemy |
2 | 2 |
|
3 | 3 | class SQL(object): |
4 | | - """TODO""" |
| 4 | + """Wrap SQLAlchemy to provide a simple SQL API.""" |
5 | 5 |
|
6 | 6 | def __init__(self, url): |
7 | | - """TODO""" |
| 7 | + """ |
| 8 | + Create instance of sqlalchemy.engine.Engine. |
| 9 | +
|
| 10 | + URL should be a string that indicates database dialect and connection arguments. |
| 11 | +
|
| 12 | + http://docs.sqlalchemy.org/en/latest/core/engines.html#sqlalchemy.create_engine |
| 13 | + """ |
8 | 14 | try: |
9 | 15 | self.engine = sqlalchemy.create_engine(url) |
10 | 16 | except Exception as e: |
11 | 17 | raise RuntimeError(e) |
12 | 18 |
|
13 | 19 | def execute(self, text, *multiparams, **params): |
14 | | - """TODO""" |
| 20 | + """ |
| 21 | + Execute a SQL statement. |
| 22 | + """ |
15 | 23 | try: |
16 | 24 |
|
| 25 | + # bind parameters before statement reaches database, so that bound parameters appear in exceptions |
17 | 26 | # http://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.text |
18 | 27 | # https://groups.google.com/forum/#!topic/sqlalchemy/FfLwKT1yQlg |
19 | 28 | # http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.Engine.execute |
20 | 29 | # http://docs.sqlalchemy.org/en/latest/faq/sqlexpressions.html#how-do-i-render-sql-expressions-as-strings-possibly-with-bound-parameters-inlined |
21 | 30 | statement = sqlalchemy.text(text).bindparams(*multiparams, **params) |
22 | 31 | result = self.engine.execute(str(statement.compile(compile_kwargs={"literal_binds": True}))) |
23 | 32 |
|
24 | | - # SELECT |
| 33 | + # if SELECT (or INSERT with RETURNING), return result set as list of dict objects |
25 | 34 | if result.returns_rows: |
26 | 35 | rows = result.fetchall() |
27 | 36 | return [dict(row) for row in rows] |
28 | 37 |
|
29 | | - # INSERT |
| 38 | + # if INSERT, return primary key value for a newly inserted row |
30 | 39 | elif result.lastrowid is not None: |
31 | 40 | return result.lastrowid |
32 | 41 |
|
33 | | - # DELETE, UPDATE |
| 42 | + # if DELETE or UPDATE (or INSERT without RETURNING), return number of rows matched |
34 | 43 | else: |
35 | 44 | return result.rowcount |
36 | 45 |
|
| 46 | + # if constraint violated, return None |
37 | 47 | except sqlalchemy.exc.IntegrityError: |
38 | 48 | return None |
39 | 49 |
|
| 50 | + # else raise error |
40 | 51 | except Exception as e: |
41 | 52 | raise RuntimeError(e) |
42 | | - |
43 | | - |
|
0 commit comments