sqliter-py 0.3.0__py3-none-any.whl → 0.4.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.
Potentially problematic release.
This version of sqliter-py might be problematic. Click here for more details.
- sqliter/__init__.py +5 -1
- sqliter/constants.py +18 -1
- sqliter/exceptions.py +39 -12
- sqliter/helpers.py +35 -0
- sqliter/model/__init__.py +3 -2
- sqliter/model/model.py +50 -10
- sqliter/query/__init__.py +5 -1
- sqliter/query/query.py +278 -36
- sqliter/sqliter.py +317 -38
- sqliter_py-0.4.0.dist-info/METADATA +196 -0
- sqliter_py-0.4.0.dist-info/RECORD +13 -0
- sqliter_py-0.3.0.dist-info/METADATA +0 -601
- sqliter_py-0.3.0.dist-info/RECORD +0 -12
- {sqliter_py-0.3.0.dist-info → sqliter_py-0.4.0.dist-info}/WHEEL +0 -0
- {sqliter_py-0.3.0.dist-info → sqliter_py-0.4.0.dist-info}/licenses/LICENSE.txt +0 -0
sqliter/query/query.py
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Implements the query building and execution logic for SQLiter.
|
|
2
|
+
|
|
3
|
+
This module defines the QueryBuilder class, which provides a fluent
|
|
4
|
+
interface for constructing SQL queries. It supports operations such
|
|
5
|
+
as filtering, ordering, limiting, and various data retrieval methods,
|
|
6
|
+
allowing for flexible and expressive database queries without writing
|
|
7
|
+
raw SQL.
|
|
8
|
+
"""
|
|
2
9
|
|
|
3
10
|
from __future__ import annotations
|
|
4
11
|
|
|
@@ -37,7 +44,21 @@ FilterValue = Union[
|
|
|
37
44
|
|
|
38
45
|
|
|
39
46
|
class QueryBuilder:
|
|
40
|
-
"""
|
|
47
|
+
"""Builds and executes database queries for a specific model.
|
|
48
|
+
|
|
49
|
+
This class provides methods to construct SQL queries, apply filters,
|
|
50
|
+
set ordering, and execute the queries against the database.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
db (SqliterDB): The database connection object.
|
|
54
|
+
model_class (type[BaseDBModel]): The Pydantic model class.
|
|
55
|
+
table_name (str): The name of the database table.
|
|
56
|
+
filters (list): List of applied filter conditions.
|
|
57
|
+
_limit (Optional[int]): The LIMIT clause value, if any.
|
|
58
|
+
_offset (Optional[int]): The OFFSET clause value, if any.
|
|
59
|
+
_order_by (Optional[str]): The ORDER BY clause, if any.
|
|
60
|
+
_fields (Optional[list[str]]): List of fields to select, if specified.
|
|
61
|
+
"""
|
|
41
62
|
|
|
42
63
|
def __init__(
|
|
43
64
|
self,
|
|
@@ -45,13 +66,11 @@ class QueryBuilder:
|
|
|
45
66
|
model_class: type[BaseDBModel],
|
|
46
67
|
fields: Optional[list[str]] = None,
|
|
47
68
|
) -> None:
|
|
48
|
-
"""Initialize
|
|
49
|
-
|
|
50
|
-
Pass the database, model class, and optional fields.
|
|
69
|
+
"""Initialize a new QueryBuilder instance.
|
|
51
70
|
|
|
52
71
|
Args:
|
|
53
|
-
db: The
|
|
54
|
-
model_class: The model class
|
|
72
|
+
db: The database connection object.
|
|
73
|
+
model_class: The Pydantic model class for the table.
|
|
55
74
|
fields: Optional list of field names to select. If None, all fields
|
|
56
75
|
are selected.
|
|
57
76
|
"""
|
|
@@ -68,7 +87,11 @@ class QueryBuilder:
|
|
|
68
87
|
self._validate_fields()
|
|
69
88
|
|
|
70
89
|
def _validate_fields(self) -> None:
|
|
71
|
-
"""Validate that the specified fields exist in the model.
|
|
90
|
+
"""Validate that the specified fields exist in the model.
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
ValueError: If any specified field is not in the model.
|
|
94
|
+
"""
|
|
72
95
|
if self._fields is None:
|
|
73
96
|
return
|
|
74
97
|
valid_fields = set(self.model_class.model_fields.keys())
|
|
@@ -80,7 +103,26 @@ class QueryBuilder:
|
|
|
80
103
|
raise ValueError(err_message)
|
|
81
104
|
|
|
82
105
|
def filter(self, **conditions: str | float | None) -> QueryBuilder:
|
|
83
|
-
"""
|
|
106
|
+
"""Apply filter conditions to the query.
|
|
107
|
+
|
|
108
|
+
This method allows adding one or more filter conditions to the query.
|
|
109
|
+
Each condition is specified as a keyword argument, where the key is
|
|
110
|
+
the field name and the value is the condition to apply.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
**conditions: Arbitrary keyword arguments representing filter
|
|
114
|
+
conditions. The key is the field name, and the value is the
|
|
115
|
+
condition to apply. Supported operators include equality,
|
|
116
|
+
comparison, and special operators like __in, __isnull, etc.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
QueryBuilder: The current QueryBuilder instance for method
|
|
120
|
+
chaining.
|
|
121
|
+
|
|
122
|
+
Examples:
|
|
123
|
+
>>> query.filter(name="John", age__gt=30)
|
|
124
|
+
>>> query.filter(status__in=["active", "pending"])
|
|
125
|
+
"""
|
|
84
126
|
valid_fields = self.model_class.model_fields
|
|
85
127
|
|
|
86
128
|
for field, value in conditions.items():
|
|
@@ -93,14 +135,34 @@ class QueryBuilder:
|
|
|
93
135
|
return self
|
|
94
136
|
|
|
95
137
|
def fields(self, fields: Optional[list[str]] = None) -> QueryBuilder:
|
|
96
|
-
"""
|
|
138
|
+
"""Specify which fields to select in the query.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
fields: List of field names to select. If None, all fields are
|
|
142
|
+
selected.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
The QueryBuilder instance for method chaining.
|
|
146
|
+
"""
|
|
97
147
|
if fields:
|
|
98
148
|
self._fields = fields
|
|
99
149
|
self._validate_fields()
|
|
100
150
|
return self
|
|
101
151
|
|
|
102
152
|
def exclude(self, fields: Optional[list[str]] = None) -> QueryBuilder:
|
|
103
|
-
"""
|
|
153
|
+
"""Specify which fields to exclude from the query results.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
fields: List of field names to exclude. If None, no fields are
|
|
157
|
+
excluded.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
The QueryBuilder instance for method chaining.
|
|
161
|
+
|
|
162
|
+
Raises:
|
|
163
|
+
ValueError: If exclusion results in no fields being selected or if
|
|
164
|
+
invalid fields are specified.
|
|
165
|
+
"""
|
|
104
166
|
if fields:
|
|
105
167
|
all_fields = set(self.model_class.model_fields.keys())
|
|
106
168
|
|
|
@@ -127,7 +189,17 @@ class QueryBuilder:
|
|
|
127
189
|
return self
|
|
128
190
|
|
|
129
191
|
def only(self, field: str) -> QueryBuilder:
|
|
130
|
-
"""
|
|
192
|
+
"""Specify a single field to select in the query.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
field: The name of the field to select.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
The QueryBuilder instance for method chaining.
|
|
199
|
+
|
|
200
|
+
Raises:
|
|
201
|
+
ValueError: If the specified field is invalid.
|
|
202
|
+
"""
|
|
131
203
|
all_fields = set(self.model_class.model_fields.keys())
|
|
132
204
|
|
|
133
205
|
# Validate that the field exists
|
|
@@ -142,6 +214,14 @@ class QueryBuilder:
|
|
|
142
214
|
def _get_operator_handler(
|
|
143
215
|
self, operator: str
|
|
144
216
|
) -> Callable[[str, Any, str], None]:
|
|
217
|
+
"""Get the appropriate handler function for the given operator.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
operator: The filter operator string.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
A callable that handles the specific operator type.
|
|
224
|
+
"""
|
|
145
225
|
handlers = {
|
|
146
226
|
"__isnull": self._handle_null,
|
|
147
227
|
"__notnull": self._handle_null,
|
|
@@ -164,12 +244,31 @@ class QueryBuilder:
|
|
|
164
244
|
def _validate_field(
|
|
165
245
|
self, field_name: str, valid_fields: dict[str, FieldInfo]
|
|
166
246
|
) -> None:
|
|
247
|
+
"""Validate that a field exists in the model.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
field_name: The name of the field to validate.
|
|
251
|
+
valid_fields: Dictionary of valid fields from the model.
|
|
252
|
+
|
|
253
|
+
Raises:
|
|
254
|
+
InvalidFilterError: If the field is not in the model.
|
|
255
|
+
"""
|
|
167
256
|
if field_name not in valid_fields:
|
|
168
257
|
raise InvalidFilterError(field_name)
|
|
169
258
|
|
|
170
259
|
def _handle_equality(
|
|
171
260
|
self, field_name: str, value: FilterValue, operator: str
|
|
172
261
|
) -> None:
|
|
262
|
+
"""Handle equality filter conditions.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
field_name: The name of the field to filter on.
|
|
266
|
+
value: The value to compare against.
|
|
267
|
+
operator: The operator string (usually '__eq').
|
|
268
|
+
|
|
269
|
+
This method adds an equality condition to the filters list, handling
|
|
270
|
+
NULL values separately.
|
|
271
|
+
"""
|
|
173
272
|
if value is None:
|
|
174
273
|
self.filters.append((f"{field_name} IS NULL", None, "__isnull"))
|
|
175
274
|
else:
|
|
@@ -178,6 +277,16 @@ class QueryBuilder:
|
|
|
178
277
|
def _handle_null(
|
|
179
278
|
self, field_name: str, _: FilterValue, operator: str
|
|
180
279
|
) -> None:
|
|
280
|
+
"""Handle IS NULL and IS NOT NULL filter conditions.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
field_name: The name of the field to filter on. _: Placeholder for
|
|
284
|
+
unused value parameter.
|
|
285
|
+
operator: The operator string ('__isnull' or '__notnull').
|
|
286
|
+
|
|
287
|
+
This method adds an IS NULL or IS NOT NULL condition to the filters
|
|
288
|
+
list.
|
|
289
|
+
"""
|
|
181
290
|
condition = (
|
|
182
291
|
f"{field_name} IS NOT NULL"
|
|
183
292
|
if operator == "__notnull"
|
|
@@ -188,6 +297,18 @@ class QueryBuilder:
|
|
|
188
297
|
def _handle_in(
|
|
189
298
|
self, field_name: str, value: FilterValue, operator: str
|
|
190
299
|
) -> None:
|
|
300
|
+
"""Handle IN and NOT IN filter conditions.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
field_name: The name of the field to filter on.
|
|
304
|
+
value: A list of values to check against.
|
|
305
|
+
operator: The operator string ('__in' or '__not_in').
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
TypeError: If the value is not a list.
|
|
309
|
+
|
|
310
|
+
This method adds an IN or NOT IN condition to the filters list.
|
|
311
|
+
"""
|
|
191
312
|
if not isinstance(value, list):
|
|
192
313
|
err = f"{field_name} requires a list for '{operator}'"
|
|
193
314
|
raise TypeError(err)
|
|
@@ -204,6 +325,19 @@ class QueryBuilder:
|
|
|
204
325
|
def _handle_like(
|
|
205
326
|
self, field_name: str, value: FilterValue, operator: str
|
|
206
327
|
) -> None:
|
|
328
|
+
"""Handle LIKE and GLOB filter conditions.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
field_name: The name of the field to filter on.
|
|
332
|
+
value: The pattern to match against.
|
|
333
|
+
operator: The operator string (e.g., '__startswith', '__contains').
|
|
334
|
+
|
|
335
|
+
Raises:
|
|
336
|
+
TypeError: If the value is not a string.
|
|
337
|
+
|
|
338
|
+
This method adds a LIKE or GLOB condition to the filters list, depending
|
|
339
|
+
on whether the operation is case-sensitive or not.
|
|
340
|
+
"""
|
|
207
341
|
if not isinstance(value, str):
|
|
208
342
|
err = f"{field_name} requires a string value for '{operator}'"
|
|
209
343
|
raise TypeError(err)
|
|
@@ -228,11 +362,29 @@ class QueryBuilder:
|
|
|
228
362
|
def _handle_comparison(
|
|
229
363
|
self, field_name: str, value: FilterValue, operator: str
|
|
230
364
|
) -> None:
|
|
365
|
+
"""Handle comparison filter conditions.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
field_name: The name of the field to filter on.
|
|
369
|
+
value: The value to compare against.
|
|
370
|
+
operator: The comparison operator string (e.g., '__lt', '__gte').
|
|
371
|
+
|
|
372
|
+
This method adds a comparison condition to the filters list.
|
|
373
|
+
"""
|
|
231
374
|
sql_operator = OPERATOR_MAPPING[operator]
|
|
232
375
|
self.filters.append((f"{field_name} {sql_operator} ?", value, operator))
|
|
233
376
|
|
|
234
377
|
# Helper method for parsing field and operator
|
|
235
378
|
def _parse_field_operator(self, field: str) -> tuple[str, str]:
|
|
379
|
+
"""Parse a field string to separate the field name and operator.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
field: The field string, potentially including an operator.
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
A tuple containing the field name and the operator (or '__eq' if
|
|
386
|
+
no operator was specified).
|
|
387
|
+
"""
|
|
236
388
|
for operator in OPERATOR_MAPPING:
|
|
237
389
|
if field.endswith(operator):
|
|
238
390
|
return field[: -len(operator)], operator
|
|
@@ -240,7 +392,15 @@ class QueryBuilder:
|
|
|
240
392
|
|
|
241
393
|
# Helper method for formatting string operators (like startswith)
|
|
242
394
|
def _format_string_for_operator(self, operator: str, value: str) -> str:
|
|
243
|
-
|
|
395
|
+
"""Format a string value based on the specified operator.
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
operator: The operator string (e.g., '__startswith', '__contains').
|
|
399
|
+
value: The original string value.
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
The formatted string value suitable for the given operator.
|
|
403
|
+
"""
|
|
244
404
|
format_map = {
|
|
245
405
|
"__startswith": f"{value}*",
|
|
246
406
|
"__endswith": f"*{value}",
|
|
@@ -254,12 +414,29 @@ class QueryBuilder:
|
|
|
254
414
|
return format_map.get(operator, value)
|
|
255
415
|
|
|
256
416
|
def limit(self, limit_value: int) -> Self:
|
|
257
|
-
"""Limit the number of results returned by the query.
|
|
417
|
+
"""Limit the number of results returned by the query.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
limit_value: The maximum number of records to return.
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
The QueryBuilder instance for method chaining.
|
|
424
|
+
"""
|
|
258
425
|
self._limit = limit_value
|
|
259
426
|
return self
|
|
260
427
|
|
|
261
428
|
def offset(self, offset_value: int) -> Self:
|
|
262
|
-
"""Set an offset value for the query.
|
|
429
|
+
"""Set an offset value for the query.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
offset_value: The number of records to skip.
|
|
433
|
+
|
|
434
|
+
Returns:
|
|
435
|
+
The QueryBuilder instance for method chaining.
|
|
436
|
+
|
|
437
|
+
Raises:
|
|
438
|
+
InvalidOffsetError: If the offset value is negative.
|
|
439
|
+
"""
|
|
263
440
|
if offset_value < 0:
|
|
264
441
|
raise InvalidOffsetError(offset_value)
|
|
265
442
|
self._offset = offset_value
|
|
@@ -270,7 +447,7 @@ class QueryBuilder:
|
|
|
270
447
|
|
|
271
448
|
def order(
|
|
272
449
|
self,
|
|
273
|
-
order_by_field: str,
|
|
450
|
+
order_by_field: Optional[str] = None,
|
|
274
451
|
direction: Optional[str] = None,
|
|
275
452
|
*,
|
|
276
453
|
reverse: bool = False,
|
|
@@ -278,19 +455,19 @@ class QueryBuilder:
|
|
|
278
455
|
"""Order the query results by the specified field.
|
|
279
456
|
|
|
280
457
|
Args:
|
|
281
|
-
order_by_field
|
|
282
|
-
direction
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
458
|
+
order_by_field: The field to order by [optional].
|
|
459
|
+
direction: Deprecated. Use 'reverse' instead.
|
|
460
|
+
reverse: If True, sort in descending order.
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
The QueryBuilder instance for method chaining.
|
|
286
464
|
|
|
287
465
|
Raises:
|
|
288
|
-
InvalidOrderError: If the field doesn't exist
|
|
289
|
-
|
|
466
|
+
InvalidOrderError: If the field doesn't exist or if both 'direction'
|
|
467
|
+
and 'reverse' are specified.
|
|
290
468
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
ordering.
|
|
469
|
+
Warns:
|
|
470
|
+
DeprecationWarning: If 'direction' is used instead of 'reverse'.
|
|
294
471
|
"""
|
|
295
472
|
if direction:
|
|
296
473
|
warnings.warn(
|
|
@@ -300,6 +477,9 @@ class QueryBuilder:
|
|
|
300
477
|
stacklevel=2,
|
|
301
478
|
)
|
|
302
479
|
|
|
480
|
+
if order_by_field is None:
|
|
481
|
+
order_by_field = self.model_class.get_primary_key()
|
|
482
|
+
|
|
303
483
|
if order_by_field not in self.model_class.model_fields:
|
|
304
484
|
err = f"'{order_by_field}' does not exist in the model fields."
|
|
305
485
|
raise InvalidOrderError(err)
|
|
@@ -331,7 +511,19 @@ class QueryBuilder:
|
|
|
331
511
|
fetch_one: bool = False,
|
|
332
512
|
count_only: bool = False,
|
|
333
513
|
) -> list[tuple[Any, ...]] | Optional[tuple[Any, ...]]:
|
|
334
|
-
"""
|
|
514
|
+
"""Execute the constructed SQL query.
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
fetch_one: If True, fetch only one result.
|
|
518
|
+
count_only: If True, return only the count of results.
|
|
519
|
+
|
|
520
|
+
Returns:
|
|
521
|
+
A list of tuples (all results), a single tuple (one result),
|
|
522
|
+
or None if no results are found.
|
|
523
|
+
|
|
524
|
+
Raises:
|
|
525
|
+
RecordFetchError: If there's an error executing the query.
|
|
526
|
+
"""
|
|
335
527
|
if count_only:
|
|
336
528
|
fields = "COUNT(*)"
|
|
337
529
|
elif self._fields:
|
|
@@ -360,6 +552,11 @@ class QueryBuilder:
|
|
|
360
552
|
sql += " OFFSET ?"
|
|
361
553
|
values.append(self._offset)
|
|
362
554
|
|
|
555
|
+
# Print the raw SQL and values if debug is enabled
|
|
556
|
+
# Log the SQL if debug is enabled
|
|
557
|
+
if self.db.debug:
|
|
558
|
+
self.db._log_sql(sql, values) # noqa: SLF001
|
|
559
|
+
|
|
363
560
|
try:
|
|
364
561
|
with self.db.connect() as conn:
|
|
365
562
|
cursor = conn.cursor()
|
|
@@ -369,7 +566,13 @@ class QueryBuilder:
|
|
|
369
566
|
raise RecordFetchError(self.table_name) from exc
|
|
370
567
|
|
|
371
568
|
def _parse_filter(self) -> tuple[list[Any], LiteralString]:
|
|
372
|
-
"""
|
|
569
|
+
"""Parse the filter conditions into SQL clauses and values.
|
|
570
|
+
|
|
571
|
+
Returns:
|
|
572
|
+
A tuple containing:
|
|
573
|
+
- A list of values to be used in the SQL query.
|
|
574
|
+
- A string representing the WHERE clause of the SQL query.
|
|
575
|
+
"""
|
|
373
576
|
where_clauses = []
|
|
374
577
|
values = []
|
|
375
578
|
for field, value, operator in self.filters:
|
|
@@ -388,7 +591,14 @@ class QueryBuilder:
|
|
|
388
591
|
return values, where_clause
|
|
389
592
|
|
|
390
593
|
def _convert_row_to_model(self, row: tuple[Any, ...]) -> BaseDBModel:
|
|
391
|
-
"""Convert a
|
|
594
|
+
"""Convert a database row to a model instance.
|
|
595
|
+
|
|
596
|
+
Args:
|
|
597
|
+
row: A tuple representing a database row.
|
|
598
|
+
|
|
599
|
+
Returns:
|
|
600
|
+
An instance of the model class populated with the row data.
|
|
601
|
+
"""
|
|
392
602
|
if self._fields:
|
|
393
603
|
return self.model_class.model_validate_partial(
|
|
394
604
|
{field: row[idx] for idx, field in enumerate(self._fields)}
|
|
@@ -413,7 +623,15 @@ class QueryBuilder:
|
|
|
413
623
|
def _fetch_result(
|
|
414
624
|
self, *, fetch_one: bool = False
|
|
415
625
|
) -> Union[list[BaseDBModel], Optional[BaseDBModel]]:
|
|
416
|
-
"""Fetch
|
|
626
|
+
"""Fetch and convert query results to model instances.
|
|
627
|
+
|
|
628
|
+
Args:
|
|
629
|
+
fetch_one: If True, fetch only one result.
|
|
630
|
+
|
|
631
|
+
Returns:
|
|
632
|
+
A list of model instances, a single model instance, or None if no
|
|
633
|
+
results are found.
|
|
634
|
+
"""
|
|
417
635
|
result = self._execute_query(fetch_one=fetch_one)
|
|
418
636
|
|
|
419
637
|
if not result:
|
|
@@ -432,30 +650,54 @@ class QueryBuilder:
|
|
|
432
650
|
return [self._convert_row_to_model(row) for row in result]
|
|
433
651
|
|
|
434
652
|
def fetch_all(self) -> list[BaseDBModel]:
|
|
435
|
-
"""Fetch all results
|
|
653
|
+
"""Fetch all results of the query.
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
A list of model instances representing all query results.
|
|
657
|
+
"""
|
|
436
658
|
return self._fetch_result(fetch_one=False)
|
|
437
659
|
|
|
438
660
|
def fetch_one(self) -> Optional[BaseDBModel]:
|
|
439
|
-
"""Fetch
|
|
661
|
+
"""Fetch a single result of the query.
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
A single model instance or None if no result is found.
|
|
665
|
+
"""
|
|
440
666
|
return self._fetch_result(fetch_one=True)
|
|
441
667
|
|
|
442
668
|
def fetch_first(self) -> Optional[BaseDBModel]:
|
|
443
|
-
"""Fetch the first result of the query.
|
|
669
|
+
"""Fetch the first result of the query.
|
|
670
|
+
|
|
671
|
+
Returns:
|
|
672
|
+
The first model instance or None if no result is found.
|
|
673
|
+
"""
|
|
444
674
|
self._limit = 1
|
|
445
675
|
return self._fetch_result(fetch_one=True)
|
|
446
676
|
|
|
447
677
|
def fetch_last(self) -> Optional[BaseDBModel]:
|
|
448
|
-
"""Fetch the last result of the query
|
|
678
|
+
"""Fetch the last result of the query.
|
|
679
|
+
|
|
680
|
+
Returns:
|
|
681
|
+
The last model instance or None if no result is found.
|
|
682
|
+
"""
|
|
449
683
|
self._limit = 1
|
|
450
684
|
self._order_by = "rowid DESC"
|
|
451
685
|
return self._fetch_result(fetch_one=True)
|
|
452
686
|
|
|
453
687
|
def count(self) -> int:
|
|
454
|
-
"""
|
|
688
|
+
"""Count the number of results for the current query.
|
|
689
|
+
|
|
690
|
+
Returns:
|
|
691
|
+
The number of results that match the current query conditions.
|
|
692
|
+
"""
|
|
455
693
|
result = self._execute_query(count_only=True)
|
|
456
694
|
|
|
457
695
|
return int(result[0][0]) if result else 0
|
|
458
696
|
|
|
459
697
|
def exists(self) -> bool:
|
|
460
|
-
"""
|
|
698
|
+
"""Check if any results exist for the current query.
|
|
699
|
+
|
|
700
|
+
Returns:
|
|
701
|
+
True if at least one result exists, False otherwise.
|
|
702
|
+
"""
|
|
461
703
|
return self.count() > 0
|