sqliter-py 0.12.0__py3-none-any.whl → 0.16.0__py3-none-any.whl
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.
- sqliter/constants.py +4 -3
- sqliter/exceptions.py +13 -0
- sqliter/model/model.py +42 -3
- sqliter/orm/__init__.py +16 -0
- sqliter/orm/fields.py +412 -0
- sqliter/orm/foreign_key.py +8 -0
- sqliter/orm/model.py +243 -0
- sqliter/orm/query.py +221 -0
- sqliter/orm/registry.py +169 -0
- sqliter/query/query.py +573 -51
- sqliter/sqliter.py +141 -47
- sqliter/tui/__init__.py +62 -0
- sqliter/tui/__main__.py +6 -0
- sqliter/tui/app.py +179 -0
- sqliter/tui/demos/__init__.py +96 -0
- sqliter/tui/demos/base.py +114 -0
- sqliter/tui/demos/caching.py +283 -0
- sqliter/tui/demos/connection.py +150 -0
- sqliter/tui/demos/constraints.py +211 -0
- sqliter/tui/demos/crud.py +154 -0
- sqliter/tui/demos/errors.py +231 -0
- sqliter/tui/demos/field_selection.py +150 -0
- sqliter/tui/demos/filters.py +389 -0
- sqliter/tui/demos/models.py +248 -0
- sqliter/tui/demos/ordering.py +156 -0
- sqliter/tui/demos/orm.py +460 -0
- sqliter/tui/demos/results.py +241 -0
- sqliter/tui/demos/string_filters.py +210 -0
- sqliter/tui/demos/timestamps.py +126 -0
- sqliter/tui/demos/transactions.py +177 -0
- sqliter/tui/runner.py +116 -0
- sqliter/tui/styles/app.tcss +130 -0
- sqliter/tui/widgets/__init__.py +7 -0
- sqliter/tui/widgets/code_display.py +81 -0
- sqliter/tui/widgets/demo_list.py +65 -0
- sqliter/tui/widgets/output_display.py +92 -0
- {sqliter_py-0.12.0.dist-info → sqliter_py-0.16.0.dist-info}/METADATA +23 -7
- sqliter_py-0.16.0.dist-info/RECORD +47 -0
- {sqliter_py-0.12.0.dist-info → sqliter_py-0.16.0.dist-info}/WHEEL +2 -2
- sqliter_py-0.16.0.dist-info/entry_points.txt +3 -0
- sqliter_py-0.12.0.dist-info/RECORD +0 -15
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Models & Tables demos."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from typing import Optional, Union
|
|
8
|
+
|
|
9
|
+
from sqliter import SqliterDB
|
|
10
|
+
from sqliter.model import BaseDBModel
|
|
11
|
+
from sqliter.tui.demos.base import Demo, DemoCategory, extract_demo_code
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _run_basic_model() -> str:
|
|
15
|
+
"""Define a simple model with automatic primary key generation.
|
|
16
|
+
|
|
17
|
+
BaseDBModel provides an auto-incrementing 'pk' field and handles
|
|
18
|
+
all the database table creation.
|
|
19
|
+
"""
|
|
20
|
+
output = io.StringIO()
|
|
21
|
+
|
|
22
|
+
class User(BaseDBModel):
|
|
23
|
+
name: str
|
|
24
|
+
age: int
|
|
25
|
+
email: str
|
|
26
|
+
|
|
27
|
+
db = SqliterDB(memory=True)
|
|
28
|
+
db.create_table(User)
|
|
29
|
+
|
|
30
|
+
user = db.insert(User(name="Alice", age=30, email="alice@example.com"))
|
|
31
|
+
output.write(f"Created user: {user.name}\n")
|
|
32
|
+
output.write(f"Primary key: {user.pk}\n")
|
|
33
|
+
output.write(f"Age: {user.age}\n")
|
|
34
|
+
output.write(f"Email: {user.email}\n")
|
|
35
|
+
|
|
36
|
+
db.close()
|
|
37
|
+
return output.getvalue()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _run_custom_table_name() -> str:
|
|
41
|
+
"""Override the auto-generated table name.
|
|
42
|
+
|
|
43
|
+
Set __tablename__ to use a custom table name instead of the
|
|
44
|
+
auto-pluralized model class name.
|
|
45
|
+
"""
|
|
46
|
+
output = io.StringIO()
|
|
47
|
+
|
|
48
|
+
class Person(BaseDBModel):
|
|
49
|
+
"""Person model with custom table name."""
|
|
50
|
+
|
|
51
|
+
__tablename__ = "people"
|
|
52
|
+
name: str
|
|
53
|
+
|
|
54
|
+
db = SqliterDB(memory=True)
|
|
55
|
+
db.create_table(Person)
|
|
56
|
+
|
|
57
|
+
output.write(f"Table created: {Person.__tablename__}\n")
|
|
58
|
+
output.write("The model uses 'people' instead of 'persons'\n")
|
|
59
|
+
|
|
60
|
+
person = db.insert(Person(name="Bob"))
|
|
61
|
+
output.write(f"Inserted: {person.name} (pk={person.pk})\n")
|
|
62
|
+
|
|
63
|
+
db.close()
|
|
64
|
+
return output.getvalue()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _run_field_types() -> str:
|
|
68
|
+
"""Use various field types in your models.
|
|
69
|
+
|
|
70
|
+
BaseDBModel supports str, int, float, bool, and more with automatic
|
|
71
|
+
type conversion and validation.
|
|
72
|
+
"""
|
|
73
|
+
output = io.StringIO()
|
|
74
|
+
|
|
75
|
+
class Product(BaseDBModel):
|
|
76
|
+
name: str
|
|
77
|
+
price: float
|
|
78
|
+
in_stock: bool
|
|
79
|
+
quantity: int
|
|
80
|
+
created_at: int
|
|
81
|
+
|
|
82
|
+
db = SqliterDB(memory=True)
|
|
83
|
+
db.create_table(Product)
|
|
84
|
+
|
|
85
|
+
product = db.insert(
|
|
86
|
+
Product(
|
|
87
|
+
name="Widget",
|
|
88
|
+
price=19.99,
|
|
89
|
+
in_stock=True,
|
|
90
|
+
quantity=100,
|
|
91
|
+
created_at=int(datetime.now(timezone.utc).timestamp()),
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
output.write(f"Product: {product.name}\n")
|
|
95
|
+
output.write(f"Price: ${product.price}\n")
|
|
96
|
+
output.write(f"In stock: {product.in_stock}\n")
|
|
97
|
+
output.write(f"Quantity: {product.quantity}\n")
|
|
98
|
+
output.write(f"Created: {product.created_at}\n")
|
|
99
|
+
|
|
100
|
+
db.close()
|
|
101
|
+
return output.getvalue()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _run_optional_fields() -> str:
|
|
105
|
+
"""Define fields that can be NULL (optional) in the database.
|
|
106
|
+
|
|
107
|
+
Use Optional[T] or Union[T, None] for nullable fields, with
|
|
108
|
+
optional default values.
|
|
109
|
+
"""
|
|
110
|
+
output = io.StringIO()
|
|
111
|
+
|
|
112
|
+
class Article(BaseDBModel):
|
|
113
|
+
title: str
|
|
114
|
+
content: Optional[str]
|
|
115
|
+
author: Optional[str] = "Anonymous"
|
|
116
|
+
|
|
117
|
+
db = SqliterDB(memory=True)
|
|
118
|
+
db.create_table(Article)
|
|
119
|
+
|
|
120
|
+
article1 = db.insert(Article(title="First Post", content=None))
|
|
121
|
+
output.write(f"Article 1: {article1.title}\n")
|
|
122
|
+
output.write(f"Content: {article1.content}\n")
|
|
123
|
+
output.write(f"Author: {article1.author}\n")
|
|
124
|
+
|
|
125
|
+
article2 = db.insert(
|
|
126
|
+
Article(title="Second Post", content="Hello world!", author="Bob"),
|
|
127
|
+
)
|
|
128
|
+
output.write(f"\nArticle 2: {article2.title}\n")
|
|
129
|
+
output.write(f"Content: {article2.content}\n")
|
|
130
|
+
output.write(f"Author: {article2.author}\n")
|
|
131
|
+
|
|
132
|
+
db.close()
|
|
133
|
+
return output.getvalue()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _run_default_values() -> str:
|
|
137
|
+
"""Set default values for fields.
|
|
138
|
+
|
|
139
|
+
Assign default values in the model definition to use when
|
|
140
|
+
inserting records without specifying those fields.
|
|
141
|
+
"""
|
|
142
|
+
output = io.StringIO()
|
|
143
|
+
|
|
144
|
+
class Task(BaseDBModel):
|
|
145
|
+
title: str
|
|
146
|
+
completed: bool = False
|
|
147
|
+
priority: int = 1
|
|
148
|
+
|
|
149
|
+
db = SqliterDB(memory=True)
|
|
150
|
+
db.create_table(Task)
|
|
151
|
+
|
|
152
|
+
task = db.insert(Task(title="New task"))
|
|
153
|
+
output.write(f"Task: {task.title}\n")
|
|
154
|
+
output.write(f"Completed: {task.completed} (default)\n")
|
|
155
|
+
output.write(f"Priority: {task.priority} (default)\n")
|
|
156
|
+
|
|
157
|
+
db.close()
|
|
158
|
+
return output.getvalue()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _run_complex_types() -> str:
|
|
162
|
+
"""Store complex data types like lists and dicts.
|
|
163
|
+
|
|
164
|
+
BaseDBModel automatically serializes lists, dicts, sets, and tuples
|
|
165
|
+
to BLOBs for SQLite storage, and deserializes them back.
|
|
166
|
+
"""
|
|
167
|
+
output = io.StringIO()
|
|
168
|
+
|
|
169
|
+
class Document(BaseDBModel):
|
|
170
|
+
title: str
|
|
171
|
+
tags: list[str]
|
|
172
|
+
metadata: dict[str, Union[str, int]]
|
|
173
|
+
|
|
174
|
+
db = SqliterDB(memory=True)
|
|
175
|
+
db.create_table(Document)
|
|
176
|
+
|
|
177
|
+
doc = db.insert(
|
|
178
|
+
Document(
|
|
179
|
+
title="Guide",
|
|
180
|
+
tags=["python", "database", "tutorial"],
|
|
181
|
+
metadata={"views": 1000, "rating": 4},
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
output.write(f"Document: {doc.title}\n")
|
|
185
|
+
output.write(f"Tags: {doc.tags}\n")
|
|
186
|
+
output.write(f"Metadata: {doc.metadata}\n")
|
|
187
|
+
|
|
188
|
+
db.close()
|
|
189
|
+
return output.getvalue()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def get_category() -> DemoCategory:
|
|
193
|
+
"""Get the Models & Tables demo category."""
|
|
194
|
+
return DemoCategory(
|
|
195
|
+
id="models",
|
|
196
|
+
title="Models & Tables",
|
|
197
|
+
icon="",
|
|
198
|
+
demos=[
|
|
199
|
+
Demo(
|
|
200
|
+
id="model_basic",
|
|
201
|
+
title="Basic Model",
|
|
202
|
+
description="Define a simple model with fields",
|
|
203
|
+
category="models",
|
|
204
|
+
code=extract_demo_code(_run_basic_model),
|
|
205
|
+
execute=_run_basic_model,
|
|
206
|
+
),
|
|
207
|
+
Demo(
|
|
208
|
+
id="model_custom_table",
|
|
209
|
+
title="Custom Table Name",
|
|
210
|
+
description="Specify a custom table name",
|
|
211
|
+
category="models",
|
|
212
|
+
code=extract_demo_code(_run_custom_table_name),
|
|
213
|
+
execute=_run_custom_table_name,
|
|
214
|
+
),
|
|
215
|
+
Demo(
|
|
216
|
+
id="model_field_types",
|
|
217
|
+
title="Field Types",
|
|
218
|
+
description="Various field type examples",
|
|
219
|
+
category="models",
|
|
220
|
+
code=extract_demo_code(_run_field_types),
|
|
221
|
+
execute=_run_field_types,
|
|
222
|
+
),
|
|
223
|
+
Demo(
|
|
224
|
+
id="model_optional",
|
|
225
|
+
title="Optional Fields",
|
|
226
|
+
description="Fields with None values and defaults",
|
|
227
|
+
category="models",
|
|
228
|
+
code=extract_demo_code(_run_optional_fields),
|
|
229
|
+
execute=_run_optional_fields,
|
|
230
|
+
),
|
|
231
|
+
Demo(
|
|
232
|
+
id="model_defaults",
|
|
233
|
+
title="Default Values",
|
|
234
|
+
description="Fields with default values",
|
|
235
|
+
category="models",
|
|
236
|
+
code=extract_demo_code(_run_default_values),
|
|
237
|
+
execute=_run_default_values,
|
|
238
|
+
),
|
|
239
|
+
Demo(
|
|
240
|
+
id="model_complex",
|
|
241
|
+
title="Complex Types",
|
|
242
|
+
description="Lists and dicts (stored as BLOBs)",
|
|
243
|
+
category="models",
|
|
244
|
+
code=extract_demo_code(_run_complex_types),
|
|
245
|
+
execute=_run_complex_types,
|
|
246
|
+
),
|
|
247
|
+
],
|
|
248
|
+
)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Ordering & Pagination demos."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
|
|
7
|
+
from sqliter import SqliterDB
|
|
8
|
+
from sqliter.model import BaseDBModel
|
|
9
|
+
from sqliter.tui.demos.base import Demo, DemoCategory, extract_demo_code
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _run_order_asc() -> str:
|
|
13
|
+
"""Sort query results in ascending order.
|
|
14
|
+
|
|
15
|
+
Use order(field_name) to sort results from lowest to highest.
|
|
16
|
+
"""
|
|
17
|
+
output = io.StringIO()
|
|
18
|
+
|
|
19
|
+
class User(BaseDBModel):
|
|
20
|
+
name: str
|
|
21
|
+
age: int
|
|
22
|
+
|
|
23
|
+
db = SqliterDB(memory=True)
|
|
24
|
+
db.create_table(User)
|
|
25
|
+
|
|
26
|
+
db.insert(User(name="Charlie", age=35))
|
|
27
|
+
db.insert(User(name="Alice", age=25))
|
|
28
|
+
db.insert(User(name="Bob", age=30))
|
|
29
|
+
|
|
30
|
+
results = db.select(User).order("age").fetch_all()
|
|
31
|
+
output.write("Users ordered by age (ascending):\n")
|
|
32
|
+
for user in results:
|
|
33
|
+
output.write(f" - {user.name}: {user.age}\n")
|
|
34
|
+
|
|
35
|
+
db.close()
|
|
36
|
+
return output.getvalue()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _run_order_desc() -> str:
|
|
40
|
+
"""Sort query results in descending order.
|
|
41
|
+
|
|
42
|
+
Use order(field_name, reverse=True) to sort from highest to lowest.
|
|
43
|
+
"""
|
|
44
|
+
output = io.StringIO()
|
|
45
|
+
|
|
46
|
+
class Product(BaseDBModel):
|
|
47
|
+
name: str
|
|
48
|
+
price: float
|
|
49
|
+
|
|
50
|
+
db = SqliterDB(memory=True)
|
|
51
|
+
db.create_table(Product)
|
|
52
|
+
|
|
53
|
+
db.insert(Product(name="Item A", price=10.0))
|
|
54
|
+
db.insert(Product(name="Item B", price=30.0))
|
|
55
|
+
db.insert(Product(name="Item C", price=20.0))
|
|
56
|
+
|
|
57
|
+
results = db.select(Product).order("price", reverse=True).fetch_all()
|
|
58
|
+
output.write("Products ordered by price (descending):\n")
|
|
59
|
+
for product in results:
|
|
60
|
+
output.write(f" - {product.name}: ${product.price}\n")
|
|
61
|
+
|
|
62
|
+
db.close()
|
|
63
|
+
return output.getvalue()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _run_limit() -> str:
|
|
67
|
+
"""Limit the number of results returned.
|
|
68
|
+
|
|
69
|
+
Use limit(count) to fetch only the first N records.
|
|
70
|
+
"""
|
|
71
|
+
output = io.StringIO()
|
|
72
|
+
|
|
73
|
+
class Article(BaseDBModel):
|
|
74
|
+
title: str
|
|
75
|
+
|
|
76
|
+
db = SqliterDB(memory=True)
|
|
77
|
+
db.create_table(Article)
|
|
78
|
+
|
|
79
|
+
for i in range(1, 11):
|
|
80
|
+
db.insert(Article(title=f"Article {i}"))
|
|
81
|
+
|
|
82
|
+
results = db.select(Article).limit(3).fetch_all()
|
|
83
|
+
output.write("Top 3 articles:\n")
|
|
84
|
+
for article in results:
|
|
85
|
+
output.write(f" - {article.title}\n")
|
|
86
|
+
|
|
87
|
+
db.close()
|
|
88
|
+
return output.getvalue()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _run_offset() -> str:
|
|
92
|
+
"""Skip a specified number of results.
|
|
93
|
+
|
|
94
|
+
Use offset(count) with limit() for pagination, skipping first N records.
|
|
95
|
+
"""
|
|
96
|
+
output = io.StringIO()
|
|
97
|
+
|
|
98
|
+
class Item(BaseDBModel):
|
|
99
|
+
name: str
|
|
100
|
+
|
|
101
|
+
db = SqliterDB(memory=True)
|
|
102
|
+
db.create_table(Item)
|
|
103
|
+
|
|
104
|
+
for i in range(1, 11):
|
|
105
|
+
db.insert(Item(name=f"Item {i}"))
|
|
106
|
+
|
|
107
|
+
results = db.select(Item).limit(5).offset(5).fetch_all()
|
|
108
|
+
output.write("Items 6-10:\n")
|
|
109
|
+
for item in results:
|
|
110
|
+
output.write(f" - {item.name}\n")
|
|
111
|
+
|
|
112
|
+
db.close()
|
|
113
|
+
return output.getvalue()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_category() -> DemoCategory:
|
|
117
|
+
"""Get the Ordering & Pagination demo category."""
|
|
118
|
+
return DemoCategory(
|
|
119
|
+
id="ordering",
|
|
120
|
+
title="Ordering & Pagination",
|
|
121
|
+
icon="",
|
|
122
|
+
demos=[
|
|
123
|
+
Demo(
|
|
124
|
+
id="order_asc",
|
|
125
|
+
title="Order Ascending",
|
|
126
|
+
description="Sort results in ascending order",
|
|
127
|
+
category="ordering",
|
|
128
|
+
code=extract_demo_code(_run_order_asc),
|
|
129
|
+
execute=_run_order_asc,
|
|
130
|
+
),
|
|
131
|
+
Demo(
|
|
132
|
+
id="order_desc",
|
|
133
|
+
title="Order Descending",
|
|
134
|
+
description="Sort results in descending order",
|
|
135
|
+
category="ordering",
|
|
136
|
+
code=extract_demo_code(_run_order_desc),
|
|
137
|
+
execute=_run_order_desc,
|
|
138
|
+
),
|
|
139
|
+
Demo(
|
|
140
|
+
id="paginate_limit",
|
|
141
|
+
title="Limit Results",
|
|
142
|
+
description="Limit number of results",
|
|
143
|
+
category="ordering",
|
|
144
|
+
code=extract_demo_code(_run_limit),
|
|
145
|
+
execute=_run_limit,
|
|
146
|
+
),
|
|
147
|
+
Demo(
|
|
148
|
+
id="paginate_offset",
|
|
149
|
+
title="Offset Results",
|
|
150
|
+
description="Skip records for pagination",
|
|
151
|
+
category="ordering",
|
|
152
|
+
code=extract_demo_code(_run_offset),
|
|
153
|
+
execute=_run_offset,
|
|
154
|
+
),
|
|
155
|
+
],
|
|
156
|
+
)
|