Skip to content

Commit 51a6e01

Browse files
deploy: 15557e6
0 parents  commit 51a6e01

23 files changed

+6126
-0
lines changed

.nojekyll

Whitespace-only changes.

index.html

Lines changed: 860 additions & 0 deletions
Large diffs are not rendered by default.

index.html.md

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
# apswutils
2+
3+
4+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
5+
6+
<div>
7+
8+
> **Where to find the complete documentation for this library**
9+
>
10+
> If you want to learn about everything this project can do, we
11+
> recommend reading the Python library section of the sqlite-utils
12+
> project
13+
> [here](https://sqlite-utils.datasette.io/en/stable/python-api.html).
14+
>
15+
> This project wouldn’t exist without Simon Willison and his excellent
16+
> [sqlite-utils](https://github.com/simonw/sqlite-utils) project. Most
17+
> of this project is his code, with some minor changes made to it.
18+
19+
</div>
20+
21+
## Install
22+
23+
pip install apswutils
24+
25+
## Use
26+
27+
First, import the apswutils library. Through the use of the **all**
28+
attribute in our Python modules by using `import *` we only bring in the
29+
`Database`, `Queryable`, `Table`, `View` classes. There’s no risk of
30+
namespace pollution.
31+
32+
``` python
33+
from apswutils.db import *
34+
```
35+
36+
Then we create a SQLite database. For the sake of convienance we’re
37+
doing it in-memory with the `:memory:` special string. If you wanted
38+
something more persistent, name it something not surrounded by colons,
39+
`data.db` is a common file name.
40+
41+
``` python
42+
db = Database(":memory:")
43+
```
44+
45+
Let’s drop (aka ‘delete’) any tables that might exist. These docs also
46+
serve as a test harness, and we want to make certain we are starting
47+
with a clean slate. This also serves as a handy sneak preview of some of
48+
the features of this library.
49+
50+
``` python
51+
for t in db.tables: t.drop()
52+
```
53+
54+
User tables are a handy way to create a useful example with some
55+
real-world meaning. To do this, we first instantiate the `users` table
56+
object:
57+
58+
``` python
59+
users = Table(db, 'Users')
60+
users
61+
```
62+
63+
<Table Users (does not exist yet)>
64+
65+
The table doesn’t exist yet, so let’s add some columns via the
66+
`Table.create` method:
67+
68+
``` python
69+
users.create(columns=dict(id=int, name=str, age=int))
70+
users
71+
```
72+
73+
<Table Users (id, name, age)>
74+
75+
What if we need to change the table structure?
76+
77+
For example User tables often include things like password field. Let’s
78+
add that now by calling `create` again, but this time with
79+
`transform=True`. We should now see that the `users` table now has the
80+
`pwd:str` field added.
81+
82+
``` python
83+
users.create(columns=dict(id=int, name=str, age=int, pwd=str), transform=True, pk='id')
84+
users
85+
```
86+
87+
<Table Users (id, name, age, pwd)>
88+
89+
``` python
90+
print(db.schema)
91+
```
92+
93+
CREATE TABLE "Users" (
94+
[id] INTEGER PRIMARY KEY,
95+
[name] TEXT,
96+
[age] INTEGER,
97+
[pwd] TEXT
98+
);
99+
100+
## Queries
101+
102+
Let’s add some users to query:
103+
104+
``` python
105+
users.insert(dict(name='Raven', age=8, pwd='s3cret'))
106+
users.insert(dict(name='Magpie', age=5, pwd='supersecret'))
107+
users.insert(dict(name='Crow', age=12, pwd='verysecret'))
108+
users.insert(dict(name='Pigeon', age=3, pwd='keptsecret'))
109+
users.insert(dict(name='Eagle', age=7, pwd='s3cr3t'))
110+
```
111+
112+
<Table Users (id, name, age, pwd)>
113+
114+
A simple unfiltered select can be executed using `rows` property on the
115+
table object.
116+
117+
``` python
118+
users.rows
119+
```
120+
121+
<generator object Queryable.rows_where>
122+
123+
Let’s iterate over that generator to see the results:
124+
125+
``` python
126+
[o for o in users.rows]
127+
```
128+
129+
[{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'},
130+
{'id': 2, 'name': 'Magpie', 'age': 5, 'pwd': 'supersecret'},
131+
{'id': 3, 'name': 'Crow', 'age': 12, 'pwd': 'verysecret'},
132+
{'id': 4, 'name': 'Pigeon', 'age': 3, 'pwd': 'keptsecret'},
133+
{'id': 5, 'name': 'Eagle', 'age': 7, 'pwd': 's3cr3t'}]
134+
135+
Filtering can be done via the `rows_where` function:
136+
137+
``` python
138+
[o for o in users.rows_where('age > 3')]
139+
```
140+
141+
[{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'},
142+
{'id': 2, 'name': 'Magpie', 'age': 5, 'pwd': 'supersecret'},
143+
{'id': 3, 'name': 'Crow', 'age': 12, 'pwd': 'verysecret'},
144+
{'id': 5, 'name': 'Eagle', 'age': 7, 'pwd': 's3cr3t'}]
145+
146+
We can also `limit` the results:
147+
148+
``` python
149+
[o for o in users.rows_where('age > 3', limit=2)]
150+
```
151+
152+
[{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'},
153+
{'id': 2, 'name': 'Magpie', 'age': 5, 'pwd': 'supersecret'}]
154+
155+
The `offset` keyword can be combined with the `limit` keyword.
156+
157+
``` python
158+
[o for o in users.rows_where('age > 3', limit=2, offset=1)]
159+
```
160+
161+
[{'id': 2, 'name': 'Magpie', 'age': 5, 'pwd': 'supersecret'},
162+
{'id': 3, 'name': 'Crow', 'age': 12, 'pwd': 'verysecret'}]
163+
164+
The `offset` must be used with `limit` or raise a `ValueError`:
165+
166+
``` python
167+
try:
168+
[o for o in users.rows_where(offset=1)]
169+
except ValueError as e:
170+
print(e)
171+
```
172+
173+
Cannot use offset without limit
174+
175+
## Transactions
176+
177+
If you have any SQL calls outside an explicit transaction, they are
178+
committed instantly.
179+
180+
To group 2 or more queries together into 1 transaction, wrap them in a
181+
BEGIN and COMMIT, executing ROLLBACK if an exception is caught:
182+
183+
``` python
184+
users.get(1)
185+
```
186+
187+
{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'}
188+
189+
``` python
190+
db.begin()
191+
try:
192+
users.delete([1])
193+
db.execute('FNOOORD')
194+
db.commit()
195+
except Exception as e:
196+
print(e)
197+
db.rollback()
198+
```
199+
200+
near "FNOOORD": syntax error
201+
202+
Because the transaction was rolled back, the user was not deleted:
203+
204+
``` python
205+
users.get(1)
206+
```
207+
208+
{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'}
209+
210+
Let’s do it again, but without the DB error, to check the transaction is
211+
successful:
212+
213+
``` python
214+
db.begin()
215+
try:
216+
users.delete([1])
217+
db.commit()
218+
except Exception as e: db.rollback()
219+
```
220+
221+
``` python
222+
try:
223+
users.get(1)
224+
print("Delete failed!")
225+
except: print("Delete succeeded!")
226+
```
227+
228+
Delete succeeded!
229+
230+
## Differences from sqlite-utils and sqlite-minutils
231+
232+
- WAL is the default
233+
- Setting `Database(recursive_triggers=False)` works as expected
234+
- Primary keys must be set on a table for it to be a target of a foreign
235+
key
236+
- Errors have been changed minimally, future PRs will change them
237+
incrementally
238+
239+
## Differences in error handling
240+
241+
<table>
242+
<colgroup>
243+
<col style="width: 33%" />
244+
<col style="width: 33%" />
245+
<col style="width: 33%" />
246+
</colgroup>
247+
<thead>
248+
<tr>
249+
<th>Old/sqlite3/dbapi</th>
250+
<th>New/APSW</th>
251+
<th>Reason</th>
252+
</tr>
253+
</thead>
254+
<tbody>
255+
<tr>
256+
<td>IntegrityError</td>
257+
<td>apsw.ConstraintError</td>
258+
<td>Caused due to SQL transformation blocked on database
259+
constraints</td>
260+
</tr>
261+
<tr>
262+
<td>sqlite3.dbapi2.OperationalError</td>
263+
<td>apsw.Error</td>
264+
<td>General error, OperationalError is now proxied to apsw.Error</td>
265+
</tr>
266+
<tr>
267+
<td>sqlite3.dbapi2.OperationalError</td>
268+
<td>apsw.SQLError</td>
269+
<td>When an error is due to flawed SQL statements</td>
270+
</tr>
271+
<tr>
272+
<td>sqlite3.ProgrammingError</td>
273+
<td>apsw.ConnectionClosedError</td>
274+
<td>Caused by an improperly closed database file</td>
275+
</tr>
276+
</tbody>
277+
</table>

robots.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sitemap: https://AnswerDotAI.github.io/apswutils/sitemap.xml

search.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
[
2+
{
3+
"objectID": "index.html",
4+
"href": "index.html",
5+
"title": "apswutils",
6+
"section": "",
7+
"text": "Where to find the complete documentation for this library\n\n\n\nIf you want to learn about everything this project can do, we recommend reading the Python library section of the sqlite-utils project here.\nThis project wouldn’t exist without Simon Willison and his excellent sqlite-utils project. Most of this project is his code, with some minor changes made to it.",
8+
"crumbs": [
9+
"apswutils"
10+
]
11+
},
12+
{
13+
"objectID": "index.html#install",
14+
"href": "index.html#install",
15+
"title": "apswutils",
16+
"section": "Install",
17+
"text": "Install\npip install apswutils",
18+
"crumbs": [
19+
"apswutils"
20+
]
21+
},
22+
{
23+
"objectID": "index.html#use",
24+
"href": "index.html#use",
25+
"title": "apswutils",
26+
"section": "Use",
27+
"text": "Use\nFirst, import the apswutils library. Through the use of the all attribute in our Python modules by using import * we only bring in the Database, Queryable, Table, View classes. There’s no risk of namespace pollution.\n\nfrom apswutils.db import *\n\nThen we create a SQLite database. For the sake of convienance we’re doing it in-memory with the :memory: special string. If you wanted something more persistent, name it something not surrounded by colons, data.db is a common file name.\n\ndb = Database(\":memory:\")\n\nLet’s drop (aka ‘delete’) any tables that might exist. These docs also serve as a test harness, and we want to make certain we are starting with a clean slate. This also serves as a handy sneak preview of some of the features of this library.\n\nfor t in db.tables: t.drop()\n\nUser tables are a handy way to create a useful example with some real-world meaning. To do this, we first instantiate the users table object:\n\nusers = Table(db, 'Users')\nusers\n\n&lt;Table Users (does not exist yet)&gt;\n\n\nThe table doesn’t exist yet, so let’s add some columns via the Table.create method:\n\nusers.create(columns=dict(id=int, name=str, age=int))\nusers\n\n&lt;Table Users (id, name, age)&gt;\n\n\nWhat if we need to change the table structure?\nFor example User tables often include things like password field. Let’s add that now by calling create again, but this time with transform=True. We should now see that the users table now has the pwd:str field added.\n\nusers.create(columns=dict(id=int, name=str, age=int, pwd=str), transform=True, pk='id')\nusers\n\n&lt;Table Users (id, name, age, pwd)&gt;\n\n\n\nprint(db.schema)\n\nCREATE TABLE \"Users\" (\n [id] INTEGER PRIMARY KEY,\n [name] TEXT,\n [age] INTEGER,\n [pwd] TEXT\n);",
28+
"crumbs": [
29+
"apswutils"
30+
]
31+
},
32+
{
33+
"objectID": "index.html#queries",
34+
"href": "index.html#queries",
35+
"title": "apswutils",
36+
"section": "Queries",
37+
"text": "Queries\nLet’s add some users to query:\n\nusers.insert(dict(name='Raven', age=8, pwd='s3cret'))\nusers.insert(dict(name='Magpie', age=5, pwd='supersecret'))\nusers.insert(dict(name='Crow', age=12, pwd='verysecret'))\nusers.insert(dict(name='Pigeon', age=3, pwd='keptsecret'))\nusers.insert(dict(name='Eagle', age=7, pwd='s3cr3t'))\n\n&lt;Table Users (id, name, age, pwd)&gt;\n\n\nA simple unfiltered select can be executed using rows property on the table object.\n\nusers.rows\n\n&lt;generator object Queryable.rows_where&gt;\n\n\nLet’s iterate over that generator to see the results:\n\n[o for o in users.rows]\n\n[{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'},\n {'id': 2, 'name': 'Magpie', 'age': 5, 'pwd': 'supersecret'},\n {'id': 3, 'name': 'Crow', 'age': 12, 'pwd': 'verysecret'},\n {'id': 4, 'name': 'Pigeon', 'age': 3, 'pwd': 'keptsecret'},\n {'id': 5, 'name': 'Eagle', 'age': 7, 'pwd': 's3cr3t'}]\n\n\nFiltering can be done via the rows_where function:\n\n[o for o in users.rows_where('age &gt; 3')]\n\n[{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'},\n {'id': 2, 'name': 'Magpie', 'age': 5, 'pwd': 'supersecret'},\n {'id': 3, 'name': 'Crow', 'age': 12, 'pwd': 'verysecret'},\n {'id': 5, 'name': 'Eagle', 'age': 7, 'pwd': 's3cr3t'}]\n\n\nWe can also limit the results:\n\n[o for o in users.rows_where('age &gt; 3', limit=2)]\n\n[{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'},\n {'id': 2, 'name': 'Magpie', 'age': 5, 'pwd': 'supersecret'}]\n\n\nThe offset keyword can be combined with the limit keyword.\n\n[o for o in users.rows_where('age &gt; 3', limit=2, offset=1)]\n\n[{'id': 2, 'name': 'Magpie', 'age': 5, 'pwd': 'supersecret'},\n {'id': 3, 'name': 'Crow', 'age': 12, 'pwd': 'verysecret'}]\n\n\nThe offset must be used with limit or raise a ValueError:\n\ntry:\n [o for o in users.rows_where(offset=1)]\nexcept ValueError as e:\n print(e)\n\nCannot use offset without limit",
38+
"crumbs": [
39+
"apswutils"
40+
]
41+
},
42+
{
43+
"objectID": "index.html#transactions",
44+
"href": "index.html#transactions",
45+
"title": "apswutils",
46+
"section": "Transactions",
47+
"text": "Transactions\nIf you have any SQL calls outside an explicit transaction, they are committed instantly.\nTo group 2 or more queries together into 1 transaction, wrap them in a BEGIN and COMMIT, executing ROLLBACK if an exception is caught:\n\nusers.get(1)\n\n{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'}\n\n\n\ndb.begin()\ntry:\n users.delete([1])\n db.execute('FNOOORD')\n db.commit()\nexcept Exception as e:\n print(e)\n db.rollback()\n\nnear \"FNOOORD\": syntax error\n\n\nBecause the transaction was rolled back, the user was not deleted:\n\nusers.get(1)\n\n{'id': 1, 'name': 'Raven', 'age': 8, 'pwd': 's3cret'}\n\n\nLet’s do it again, but without the DB error, to check the transaction is successful:\n\ndb.begin()\ntry:\n users.delete([1])\n db.commit()\nexcept Exception as e: db.rollback()\n\n\ntry:\n users.get(1)\n print(\"Delete failed!\")\nexcept: print(\"Delete succeeded!\")\n\nDelete succeeded!",
48+
"crumbs": [
49+
"apswutils"
50+
]
51+
},
52+
{
53+
"objectID": "index.html#differences-from-sqlite-utils-and-sqlite-minutils",
54+
"href": "index.html#differences-from-sqlite-utils-and-sqlite-minutils",
55+
"title": "apswutils",
56+
"section": "Differences from sqlite-utils and sqlite-minutils",
57+
"text": "Differences from sqlite-utils and sqlite-minutils\n\nWAL is the default\nSetting Database(recursive_triggers=False) works as expected\nPrimary keys must be set on a table for it to be a target of a foreign key\nErrors have been changed minimally, future PRs will change them incrementally",
58+
"crumbs": [
59+
"apswutils"
60+
]
61+
},
62+
{
63+
"objectID": "index.html#differences-in-error-handling",
64+
"href": "index.html#differences-in-error-handling",
65+
"title": "apswutils",
66+
"section": "Differences in error handling",
67+
"text": "Differences in error handling\n\n\n\n\n\n\n\n\nOld/sqlite3/dbapi\nNew/APSW\nReason\n\n\n\n\nIntegrityError\napsw.ConstraintError\nCaused due to SQL transformation blocked on database constraints\n\n\nsqlite3.dbapi2.OperationalError\napsw.Error\nGeneral error, OperationalError is now proxied to apsw.Error\n\n\nsqlite3.dbapi2.OperationalError\napsw.SQLError\nWhen an error is due to flawed SQL statements\n\n\nsqlite3.ProgrammingError\napsw.ConnectionClosedError\nCaused by an improperly closed database file",
68+
"crumbs": [
69+
"apswutils"
70+
]
71+
}
72+
]

site_libs/bootstrap/bootstrap-ebc57992554dae5230fa85c1bd5a55fa.min.css

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)