sqlite-database 0.7.2__tar.gz → 0.7.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.github/workflows/python-publish.yml +1 -0
  2. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.gitignore +4 -1
  3. {sqlite_database-0.7.2/sqlite_database.egg-info → sqlite_database-0.7.4}/PKG-INFO +1 -1
  4. sqlite_database-0.7.4/docs/ModelAPI.md +261 -0
  5. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/SimpleGuide.md +6 -4
  6. sqlite_database-0.7.4/docs/_.md +251 -0
  7. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/index.rst +1 -2
  8. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/pyproject.toml +1 -1
  9. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/__init__.py +3 -3
  10. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/database.py +34 -3
  11. {sqlite_database-0.7.2/sqlite_database/model → sqlite_database-0.7.4/sqlite_database/models}/__init__.py +27 -4
  12. {sqlite_database-0.7.2/sqlite_database/model → sqlite_database-0.7.4/sqlite_database/models}/errors.py +12 -9
  13. {sqlite_database-0.7.2/sqlite_database/model → sqlite_database-0.7.4/sqlite_database/models}/helpers.py +3 -3
  14. {sqlite_database-0.7.2/sqlite_database/model → sqlite_database-0.7.4/sqlite_database/models}/query_builder.py +18 -5
  15. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/table.py +10 -1
  16. {sqlite_database-0.7.2 → sqlite_database-0.7.4/sqlite_database.egg-info}/PKG-INFO +1 -1
  17. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database.egg-info/SOURCES.txt +21 -8
  18. sqlite_database-0.7.4/tests/database/__init__.py +0 -0
  19. sqlite_database-0.7.4/tests/database/model_api/__init__.py +0 -0
  20. sqlite_database-0.7.4/tests/database/model_api/test_model_api.py +197 -0
  21. sqlite_database-0.7.4/tests/database/setup.py +159 -0
  22. sqlite_database-0.7.4/tests/database/table_api/__init__.py +0 -0
  23. sqlite_database-0.7.4/tests/database/table_api/test_csv.py +28 -0
  24. sqlite_database-0.7.4/tests/database/table_api/test_delete.py +43 -0
  25. sqlite_database-0.7.4/tests/database/table_api/test_insert.py +38 -0
  26. sqlite_database-0.7.4/tests/database/table_api/test_others.py +65 -0
  27. sqlite_database-0.7.4/tests/database/table_api/test_select.py +102 -0
  28. sqlite_database-0.7.4/tests/database/table_api/test_update.py +58 -0
  29. sqlite_database-0.7.4/tests/database/test_custom.py +33 -0
  30. sqlite_database-0.7.4/transient/README.md +1 -0
  31. sqlite_database-0.7.2/docs/usage.md +0 -315
  32. sqlite_database-0.7.2/tests/test_database.py +0 -639
  33. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.editorconfig +0 -0
  34. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  35. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  36. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.github/ISSUE_TEMPLATE/question.md +0 -0
  37. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.github/dependabot.yml +0 -0
  38. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.github/workflows/pylint.yml +0 -0
  39. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.github/workflows/pytest.yml +0 -0
  40. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.readthedocs.yaml +0 -0
  41. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/.vscode/settings.json +0 -0
  42. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/Features.md +0 -0
  43. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/History.md +0 -0
  44. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/LICENSE +0 -0
  45. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/README.md +0 -0
  46. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/SimpleGuide.md +0 -0
  47. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/TODO.md +0 -0
  48. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/bin/activate +0 -0
  49. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/bin/check.bat +0 -0
  50. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/bin/check.sh +0 -0
  51. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/bin/include/utility.bash +0 -0
  52. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/bin/install.bash +0 -0
  53. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/bin/need-installed/activate +0 -0
  54. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/bin/need-installed/pre-commit +0 -0
  55. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/bin/summarize-pylint.py +0 -0
  56. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/dev-config/black.toml +0 -0
  57. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/dev-config/pylint.toml +0 -0
  58. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/dev-config/pytest.ini +0 -0
  59. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/dev-requirements.txt +0 -0
  60. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/Makefile +0 -0
  61. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/api_reference.rst +0 -0
  62. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/conf.py +0 -0
  63. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/make.bat +0 -0
  64. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/modules.rst +0 -0
  65. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.column.rst +0 -0
  66. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.config.rst +0 -0
  67. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.csv.rst +0 -0
  68. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.database.rst +0 -0
  69. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.errors.rst +0 -0
  70. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.functions.rst +0 -0
  71. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.locals.rst +0 -0
  72. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.model.errors.rst +0 -0
  73. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.model.helpers.rst +0 -0
  74. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.model.query_builder.rst +0 -0
  75. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.model.rst +0 -0
  76. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.operators.rst +0 -0
  77. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.query_builder.rst +0 -0
  78. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.rst +0 -0
  79. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.signature.rst +0 -0
  80. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.subexp.rst +0 -0
  81. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.table.rst +0 -0
  82. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.typings.rst +0 -0
  83. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs/sqlite_database.utils.rst +0 -0
  84. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/docs-requirements.txt +0 -0
  85. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/project-init.bash +0 -0
  86. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/setup.cfg +0 -0
  87. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/setup.py +0 -0
  88. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/_debug.py +0 -0
  89. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/_utils.py +0 -0
  90. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/column.py +0 -0
  91. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/csv.py +0 -0
  92. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/errors.py +0 -0
  93. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/functions.py +0 -0
  94. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/locals.py +0 -0
  95. {sqlite_database-0.7.2/sqlite_database/model → sqlite_database-0.7.4/sqlite_database/models}/mixin.py +0 -0
  96. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/operators.py +0 -0
  97. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/query_builder.py +0 -0
  98. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/signature.py +0 -0
  99. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/subquery.py +0 -0
  100. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/typings.py +0 -0
  101. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database/utils.py +0 -0
  102. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database.egg-info/dependency_links.txt +0 -0
  103. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database.egg-info/requires.txt +0 -0
  104. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database.egg-info/top_level.txt +0 -0
  105. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/sqlite_database.egg-info/zip-safe +0 -0
  106. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/tests/__init__.py +0 -0
  107. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/tests/manual_test_performances.py +0 -0
  108. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/tests/test_internals.py +0 -0
  109. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/tests/user_benchmark.py +0 -0
  110. {sqlite_database-0.7.2 → sqlite_database-0.7.4}/tests/user_helpers.py +0 -0
@@ -11,6 +11,7 @@ name: Upload Python Package
11
11
  on:
12
12
  release:
13
13
  types: [published]
14
+ workflow_dispatch:
14
15
 
15
16
  permissions:
16
17
  contents: read
@@ -170,4 +170,7 @@ reports.txt
170
170
  records
171
171
  testdb.sqlite3
172
172
  /test*.py
173
- perf-counter.txt
173
+ perf-counter.txt
174
+
175
+ /transient/*
176
+ !/transient/README.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlite_database
3
- Version: 0.7.2
3
+ Version: 0.7.4
4
4
  Summary: A weird wrapper for SQLite Connection
5
5
  Home-page: https://github.com/RimuEirnarn/sqlite_database
6
6
  Author: RimuEirnarn
@@ -0,0 +1,261 @@
1
+ # 📘 ModelAPI – A Friendly Guide to a Lightweight SQLite ORM
2
+
3
+ > **Note**: ModelAPI uses a different structure than TableAPI! So if you're coming from that, treat this as a fresh start.
4
+
5
+ ---
6
+
7
+ ## 📚 Table of Contents
8
+
9
+ - [📘 ModelAPI – A Friendly Guide to a Lightweight SQLite ORM](#-modelapi--a-friendly-guide-to-a-lightweight-sqlite-orm)
10
+ - [📚 Table of Contents](#-table-of-contents)
11
+ - [🧠 Introduction](#-introduction)
12
+ - [🚀 Getting Started](#-getting-started)
13
+ - [1. Bootstrapping the Database](#1-bootstrapping-the-database)
14
+ - [2. Creating a Model](#2-creating-a-model)
15
+ - [💡 How it works](#-how-it-works)
16
+ - [3. Running CRUD Operations](#3-running-crud-operations)
17
+ - [CREATE](#create)
18
+ - [READ](#read)
19
+ - [Advanced `.where()` filtering](#advanced-where-filtering)
20
+ - [UPDATE](#update)
21
+ - [DELETE](#delete)
22
+ - [🎮 Example App – CRUD in Action](#-example-app--crud-in-action)
23
+ - [🙋 FAQ](#-faq)
24
+ - [💡 Tips \& Notes](#-tips--notes)
25
+
26
+ ---
27
+
28
+ ## 🧠 Introduction
29
+
30
+ **ModelAPI** gives you a Laravel-style, class-based ORM interface for SQLite in Python. It's minimal, fast, and easy to use.
31
+
32
+ Think of it like this:
33
+
34
+ - You define your data with Python classes.
35
+ - The ORM handles schema definition, inserts, reads, updates, and deletes.
36
+ - It's flexible enough for small scripts and powerful enough for CLI tools.
37
+
38
+ ---
39
+
40
+ ## 🚀 Getting Started
41
+
42
+ Let’s walk through setting things up step-by-step!
43
+
44
+ ---
45
+
46
+ ### 1. Bootstrapping the Database
47
+
48
+ Create a file like `db.py` to initialize your SQLite database.
49
+
50
+ ```py
51
+ # db.py
52
+ from sqlite_database import Database
53
+
54
+ db = Database(":memory:") # or use "your_file.db" to persist data
55
+ ```
56
+
57
+ This creates a simple in-memory SQLite database. For persistent storage, pass a filename instead of `":memory:"`.
58
+
59
+ ---
60
+
61
+ ### 2. Creating a Model
62
+
63
+ Each model is a Python class with typed fields and a schema definition.
64
+
65
+ ```py
66
+ # model/notes.py
67
+ from uuid import uuid4
68
+ from sqlite_database import model, BaseModel, Primary
69
+ from ..db import db
70
+
71
+ @model(db)
72
+ class Notes(BaseModel):
73
+ __schema__ = (Primary('id'),)
74
+ __auto_id__ = lambda: str(uuid4())
75
+
76
+ id: str
77
+ title: str
78
+ content: str
79
+ ```
80
+
81
+ #### 💡 How it works
82
+
83
+ - `@model(db)` binds the class to the database.
84
+ - `__schema__` defines your table schema. You can use `Primary()`, `Unique()`, `Foreign()`, etc.
85
+ - `__auto_id__` is optional. It helps generate IDs automatically (e.g. UUIDs).
86
+
87
+ ---
88
+
89
+ ### 3. Running CRUD Operations
90
+
91
+ #### CREATE
92
+
93
+ ```py
94
+ Notes.create(title="Hello", content="World!")
95
+ ```
96
+
97
+ You can gather user input too:
98
+
99
+ ```py
100
+ title = input('Title: ')
101
+ content = input('Content: ')
102
+ Notes.create(title=title, content=content)
103
+ ```
104
+
105
+ ---
106
+
107
+ #### READ
108
+
109
+ You can fetch all rows, one row, or use filters:
110
+
111
+ ```py
112
+ Notes.all() # Returns a list of all notes
113
+ Notes.first(id="123") # Returns the first match or None
114
+ Notes.one(id="123") # Returns exactly one result, else throws error
115
+ ```
116
+
117
+ ##### Advanced `.where()` filtering
118
+
119
+ ```py
120
+ # Find a note by title
121
+ note = Notes.where(title="Shopping List").fetch_one()
122
+
123
+ # Limit or offset
124
+ Notes.where().limit(5).fetch() # Get top 5 notes
125
+ Notes.where().offset(5).fetch() # Skip 5 and get the rest
126
+
127
+ # Count matching rows
128
+ count = Notes.where().count()
129
+ ```
130
+
131
+ ---
132
+
133
+ #### UPDATE
134
+
135
+ Updating a row is super simple. Fetch it first, then call `.update()`:
136
+
137
+ ```py
138
+ note = Notes.first(id="some-id")
139
+ note.update(title="Updated", content="Updated content here.")
140
+ ```
141
+
142
+ ---
143
+
144
+ #### DELETE
145
+
146
+ Delete just like you'd update:
147
+
148
+ ```py
149
+ note = Notes.first(id="some-id")
150
+ note.delete()
151
+ ```
152
+
153
+ ---
154
+
155
+ ## 🎮 Example App – CRUD in Action
156
+
157
+ Here’s a small CLI app to tie it all together:
158
+
159
+ ```py
160
+ from enum import IntEnum
161
+ from uuid import uuid4
162
+ from sqlite_database import Database, model, BaseModel, Primary
163
+
164
+ db = Database(":memory:")
165
+
166
+ @model(db)
167
+ class Notes(BaseModel):
168
+ __schema__ = (Primary('id'),)
169
+ __auto_id__ = lambda: str(uuid4())
170
+
171
+ id: str
172
+ title: str
173
+ content: str
174
+
175
+ def display():
176
+ print('-'*3)
177
+ for note in Notes.all():
178
+ print(f"ID : {note.id}")
179
+ print(f"Title : {note.title}")
180
+ print(f"Content : {note.content}")
181
+ print("-"*3)
182
+
183
+ def create():
184
+ title = input("Title: ")
185
+ content = input("Content: ")
186
+ Notes.create(title=title, content=content)
187
+
188
+ def update():
189
+ note_id = input('ID: ')
190
+ note = Notes.first(id=note_id)
191
+ if note:
192
+ title = input("New title: ")
193
+ content = input("New content: ")
194
+ note.update(title=title, content=content)
195
+ else:
196
+ print("Note not found.")
197
+
198
+ def delete():
199
+ note_id = input('ID: ')
200
+ note = Notes.first(id=note_id)
201
+ if note:
202
+ note.delete()
203
+ else:
204
+ print("Note not found.")
205
+
206
+ class CMD(IntEnum):
207
+ DISPLAY = 1
208
+ CREATE = 2
209
+ UPDATE = 3
210
+ DELETE = 4
211
+ EXIT = 5
212
+
213
+ def main():
214
+ while True:
215
+ print('-'*8)
216
+ print('1. Display all notes')
217
+ print('2. Create a note')
218
+ print('3. Update a note')
219
+ print('4. Delete a note')
220
+ print('5. Exit')
221
+ try:
222
+ cmd = int(input("Command: "))
223
+ if cmd == CMD.DISPLAY:
224
+ display()
225
+ elif cmd == CMD.CREATE:
226
+ create()
227
+ elif cmd == CMD.UPDATE:
228
+ update()
229
+ elif cmd == CMD.DELETE:
230
+ delete()
231
+ elif cmd == CMD.EXIT:
232
+ break
233
+ except KeyboardInterrupt:
234
+ break
235
+ except Exception as exc:
236
+ print(f"{type(exc).__name__}: {exc}")
237
+
238
+ if __name__ == "__main__":
239
+ main()
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 🙋 FAQ
245
+
246
+ **Q: Can I define relationships between models?**
247
+ A: Yes, using `Foreign()` in `__schema__`. This doc will be updated with examples soon.
248
+
249
+ **Q: Does it support migrations?**
250
+ A: Not directly. The schema is defined per-model, so migrations require manual changes.
251
+
252
+ **Q: How are models stored?**
253
+ A: The models reflect SQLite tables. Everything is automatically synced on class definition.
254
+
255
+ ---
256
+
257
+ ## 💡 Tips & Notes
258
+
259
+ - You can use any primitive Python types (e.g. `int`, `str`, `float`) in your model class.
260
+ - Keep `__auto_id__` short and efficient — UUIDs are recommended.
261
+ - Don’t forget to call `Database()` only once and reuse the instance.
@@ -1,10 +1,10 @@
1
- # SQLite Database (Beginner-Friendly Guide)
1
+ # SQLite Database (Beginner-Friendly Guide to Table API)
2
2
 
3
3
  This library is a simple wrapper for Python's built-in SQLite package. It simplifies database operations, making it easier to interact with SQLite without writing complex SQL queries.
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [SQLite Database (Beginner-Friendly Guide)](#sqlite-database-beginner-friendly-guide)
7
+ - [SQLite Database (Beginner-Friendly Guide to Table API)](#sqlite-database-beginner-friendly-guide-to-table-api)
8
8
  - [Table of Contents](#table-of-contents)
9
9
  - [Introduction](#introduction)
10
10
  - [Getting Started](#getting-started)
@@ -39,6 +39,8 @@ SQLite is a small and fast database engine that stores all data in a single file
39
39
 
40
40
  This library itself is intended to help around your life from having to write SQL statements unless in specific cases. For now, implemented API is Table API which is what is this documentation will show to you.
41
41
 
42
+ There's 2 taste what we've developed, both are named as "TableAPI" and "ModelAPI". Refer to [this for ModelAPI](./ModelAPI.md)
43
+
42
44
  ### Installation
43
45
 
44
46
  If the library is available on PyPI, install it using:
@@ -107,8 +109,8 @@ print(all_users) # Output: [Row(id=1, name='Alice'), Row(id=2, name='Bob'), Row
107
109
  To fetch only names:
108
110
 
109
111
  ```python
110
- user_names = users.select(only=("name",))
111
- print(user_names) # Output: [Row(name='Alice'), Row(name='Bob'), Row(name='Charlie')]
112
+ user_names = users.select(what=("name",))
113
+ print(user_names) # Output: ['Alice', 'Bob', 'Charlie']
112
114
  ```
113
115
 
114
116
  ### Updating Data
@@ -0,0 +1,251 @@
1
+ # 🍃 ModelAPI: Lightweight SQLite ORM
2
+
3
+ A simple, model-centric ORM for SQLite that feels familiar if you've used Laravel's Eloquent. Designed for fast prototyping and projects that value readability and minimalism.
4
+
5
+ > **Heads up:** `ModelAPI` is **not** the same as `TableAPI`. While both serve as ORMs, `ModelAPI` focuses on declarative, class-based models — ideal for those who prefer OOP-style code.
6
+
7
+ ---
8
+
9
+ ## 📘 Table of Contents
10
+
11
+ - [🍃 ModelAPI: Lightweight SQLite ORM](#-modelapi-lightweight-sqlite-orm)
12
+ - [📘 Table of Contents](#-table-of-contents)
13
+ - [🚀 Getting Started](#-getting-started)
14
+ - [🧩 Installation](#-installation)
15
+ - [🛠 Database Setup](#-database-setup)
16
+ - [🧱 Defining Models](#-defining-models)
17
+ - [🏷 `__schema__` and Field Declarations](#-__schema__-and-field-declarations)
18
+ - [🔁 Auto-Generating IDs](#-auto-generating-ids)
19
+ - [🛠 CRUD Operations](#-crud-operations)
20
+ - [✅ Create](#-create)
21
+ - [🔍 Read](#-read)
22
+ - [`all()`, `first()`, and `one()`](#all-first-and-one)
23
+ - [Flexible Queries with `where()`](#flexible-queries-with-where)
24
+ - [📝 Update](#-update)
25
+ - [🗑 Delete](#-delete)
26
+ - [💻 CLI Example](#-cli-example)
27
+ - [🧠 Best Practices](#-best-practices)
28
+ - [⚠️ Common Pitfalls](#️-common-pitfalls)
29
+
30
+ ---
31
+
32
+ ## 🚀 Getting Started
33
+
34
+ ### 🧩 Installation
35
+
36
+ You’ll need the core dependency, `sqlite-database`:
37
+
38
+ ```bash
39
+ pip install sqlite-database
40
+ ```
41
+
42
+ If you're developing locally and already have it, just ensure it's accessible in your Python path.
43
+
44
+ ---
45
+
46
+ ### 🛠 Database Setup
47
+
48
+ Start by initializing your database:
49
+
50
+ ```python
51
+ # db.py
52
+ from sqlite_database import Database
53
+
54
+ db = Database("notes.db") # Or use ":memory:" for an in-memory DB
55
+ ```
56
+
57
+ You’ll reuse `db` across all your models.
58
+
59
+ ---
60
+
61
+ ## 🧱 Defining Models
62
+
63
+ Models define how your data is structured. Think of each model as a table blueprint.
64
+
65
+ ```python
66
+ # model/notes.py
67
+ from sqlite_database import model, BaseModel, Primary
68
+ from db import db
69
+
70
+ @model(db)
71
+ class Notes(BaseModel):
72
+ __schema__ = (Primary('id'),)
73
+
74
+ id: str
75
+ title: str
76
+ content: str
77
+ ```
78
+
79
+ ### 🏷 `__schema__` and Field Declarations
80
+
81
+ Define your fields using schema helpers like:
82
+
83
+ - `Primary(field_name)`
84
+ - `Unique(field_name)`
85
+ - `Foreign(field_name, f"{ref_table}/{ref_column}")`
86
+
87
+ These help ensure integrity and enforce constraints under the hood.
88
+
89
+ ---
90
+
91
+ ### 🔁 Auto-Generating IDs
92
+
93
+ If your primary key is a UUID or something dynamic, define `__auto_id__`:
94
+
95
+ ```python
96
+ from uuid import uuid4
97
+
98
+ @model(db)
99
+ class Notes(BaseModel):
100
+ __schema__ = (Primary("id"),)
101
+ __auto_id__ = lambda: str(uuid4())
102
+
103
+ id: str
104
+ title: str
105
+ content: str
106
+ ```
107
+
108
+ Whenever you call `.create()` without an `id`, this auto-generator kicks in.
109
+
110
+ ---
111
+
112
+ ## 🛠 CRUD Operations
113
+
114
+ ### ✅ Create
115
+
116
+ Create a new record like this:
117
+
118
+ ```python
119
+ Notes.create(title="Meeting", content="Discuss roadmap")
120
+ ```
121
+
122
+ Interactive input? No problem:
123
+
124
+ ```python
125
+ title = input("Title: ")
126
+ content = input("Content: ")
127
+ Notes.create(title=title, content=content)
128
+ ```
129
+
130
+ ---
131
+
132
+ ### 🔍 Read
133
+
134
+ #### `all()`, `first()`, and `one()`
135
+
136
+ ```python
137
+ Notes.all() # Returns a list of all notes
138
+ Notes.first(id="abc") # First match or None
139
+ Notes.one(id="abc") # Exactly one match; raises error if 0 or >1
140
+ ```
141
+
142
+ #### Flexible Queries with `where()`
143
+
144
+ Chainable query builder:
145
+
146
+ ```python
147
+ Notes.where(title="Roadmap").fetch_one()
148
+ Notes.where().limit(5).fetch()
149
+ Notes.where().offset(5).fetch()
150
+ Notes.where().count()
151
+ ```
152
+
153
+ ---
154
+
155
+ ### 📝 Update
156
+
157
+ First, fetch a record — then update it:
158
+
159
+ ```python
160
+ note = Notes.first(id="abc")
161
+ if note:
162
+ note.update(title="Updated title", content="Updated body")
163
+ ```
164
+
165
+ ---
166
+
167
+ ### 🗑 Delete
168
+
169
+ ```python
170
+ note = Notes.first(id="abc")
171
+ if note:
172
+ note.delete()
173
+ ```
174
+
175
+ This permanently removes the record from the database.
176
+
177
+ ---
178
+
179
+ ## 💻 CLI Example
180
+
181
+ Here’s a complete interactive CLI app:
182
+
183
+ ```python
184
+ # cli.py
185
+ from model.notes import Notes
186
+ from enum import IntEnum
187
+
188
+ class CMD(IntEnum):
189
+ DISPLAY = 1
190
+ CREATE = 2
191
+ UPDATE = 3
192
+ DELETE = 4
193
+ EXIT = 5
194
+
195
+ def display():
196
+ print("---")
197
+ for note in Notes.all():
198
+ print(f"ID: {note.id}\nTitle: {note.title}\nContent: {note.content}\n---")
199
+
200
+ def create():
201
+ Notes.create(title=input("Title: "), content=input("Content: "))
202
+
203
+ def update():
204
+ note = Notes.first(id=input("ID: "))
205
+ if note:
206
+ note.update(title=input("Title: "), content=input("Content: "))
207
+ else:
208
+ print("Note not found.")
209
+
210
+ def delete():
211
+ note = Notes.first(id=input("ID: "))
212
+ if note:
213
+ note.delete()
214
+ else:
215
+ print("Note not found.")
216
+
217
+ def main():
218
+ while True:
219
+ print("1. Display\n2. Create\n3. Update\n4. Delete\n5. Exit")
220
+ try:
221
+ cmd = int(input("Select: "))
222
+ if cmd == CMD.DISPLAY: display()
223
+ elif cmd == CMD.CREATE: create()
224
+ elif cmd == CMD.UPDATE: update()
225
+ elif cmd == CMD.DELETE: delete()
226
+ elif cmd == CMD.EXIT: break
227
+ except Exception as e:
228
+ print(f"{type(e).__name__}: {e}")
229
+
230
+ if __name__ == '__main__':
231
+ main()
232
+ ```
233
+
234
+ ---
235
+
236
+ ## 🧠 Best Practices
237
+
238
+ ✅ **Define all constraints in `__schema__`** — Primary, Foreign, and Unique.
239
+ ✅ **Use `__auto_id__`** for consistent ID generation (especially UUIDs).
240
+ ✅ **Keep models minimal** — push business logic elsewhere (CLI, service layer, etc).
241
+ ✅ Use `.where().count()` instead of loading all records just to count them.
242
+ ✅ Only use `.one()` when you’re 100% sure the result is unique.
243
+
244
+ ---
245
+
246
+ ## ⚠️ Common Pitfalls
247
+
248
+ ❌ Missing `@model(db)` — your model won’t be registered.
249
+ ❌ Using `.one()` on multi-match queries — it will throw an exception.
250
+ ❌ Forgetting `.fetch()` or `.fetch_one()` after `.where()` — it won’t run.
251
+ ❌ Assuming `.create()` returns an object — it returns `None`.
@@ -14,8 +14,7 @@ SQLite Database
14
14
  :caption: SQLite Database
15
15
 
16
16
  SimpleGuide
17
- usage
18
- api_reference
17
+ ModelAPI
19
18
 
20
19
 
21
20
  Indices and tables
@@ -25,7 +25,7 @@ Repository = "https://github.com/RimuEirnarn/sqlite_database.git"
25
25
 
26
26
  [tool.setuptools]
27
27
  zip-safe = true
28
- packages = ["sqlite_database", "sqlite_database.model"]
28
+ packages = ["sqlite_database", "sqlite_database.models"]
29
29
 
30
30
  [project.optional-dependencies]
31
31
  dev = ["pytest", "pylint", "black"]
@@ -1,6 +1,6 @@
1
1
  """Database"""
2
2
 
3
- from .model import BaseModel, model, Foreign, Primary, Unique
3
+ from .models import BaseModel, model, Foreign, Primary, Unique
4
4
  from .database import Database
5
5
  from ._utils import null, Row, Null
6
6
  from .column import Column, text, integer, blob, real
@@ -14,7 +14,7 @@ def test_installed():
14
14
  return True
15
15
 
16
16
 
17
- __version__ = "0.7.2"
17
+ __version__ = "0.7.4"
18
18
  __all__ = [
19
19
  "Database",
20
20
  "Table",
@@ -29,7 +29,7 @@ __all__ = [
29
29
  "real",
30
30
  "blob",
31
31
  "BaseModel",
32
- "model",
32
+ "models",
33
33
  "Foreign",
34
34
  "Primary",
35
35
  "Unique"
@@ -1,7 +1,8 @@
1
1
  """SQLite Database"""
2
2
 
3
3
  from atexit import register as finalize
4
- from sqlite3 import OperationalError, connect
4
+ from sqlite3 import OperationalError, connect, Connection
5
+ from threading import local
5
6
  from typing import Iterable, Literal, Optional
6
7
 
7
8
  from sqlite_database._debug import if_debug_print
@@ -43,14 +44,17 @@ class Database: # pylint: disable=too-many-instance-attributes
43
44
  del kwargs['forgive']
44
45
  if 'strict' in kwargs:
45
46
  del kwargs['strict']
46
- self._database = connect(path, **kwargs)
47
- self._database.row_factory = dict_factory
48
47
  self._config = None
49
48
  self._closed = False
50
49
  if not self._closed or self.__dict__.get("_initiated", False) is False:
51
50
  finalize(self._finalizer)
52
51
  self._initiated = True
53
52
  self._kwargs = kwargs
53
+ self._create_connection()
54
+
55
+ def _create_connection(self):
56
+ self._database = connect(self._path, **self._kwargs)
57
+ self._database.row_factory = dict_factory
54
58
 
55
59
  def _finalizer(self):
56
60
  self.close()
@@ -224,3 +228,30 @@ class Database: # pylint: disable=too-many-instance-attributes
224
228
  def sql(self):
225
229
  """SQL Connection"""
226
230
  return self._database
231
+
232
+ class AsyncDatabase(Database):
233
+ """Async (threads, subprocess) ready"""
234
+
235
+ def __init__(self, path: str, **kwargs) -> None:
236
+ super().__init__(path, **kwargs)
237
+ self._local = local()
238
+
239
+ def _create_connection(self):
240
+ conn = getattr(self._local, "conn", None)
241
+ if conn is None:
242
+ timeout = self._kwargs.pop('timeout', 30)
243
+ conn = connect(
244
+ self._path,
245
+ timeout=timeout,
246
+ isolation_level=self._kwargs.pop("isolation_level", None),
247
+ check_same_thread=self._kwargs.pop("check_same_thread", False)
248
+ )
249
+ conn.row_factory = dict_factory
250
+ conn.execute("PRAGMA journal_mode=WAL;")
251
+ if isinstance(timeout, int):
252
+ conn.execute(f'PRAGMA busy_timeout={timeout * 1000};')
253
+ self._local.conn = conn
254
+
255
+ @property
256
+ def _database(self) -> Connection:
257
+ return self._local.conn