dapper-sqls 0.9.7__py3-none-any.whl → 1.2.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.
- dapper_sqls/__init__.py +4 -2
- dapper_sqls/_types.py +25 -2
- dapper_sqls/async_dapper/async_dapper.py +1 -1
- dapper_sqls/async_dapper/async_executors.py +128 -53
- dapper_sqls/builders/model/model.py +421 -36
- dapper_sqls/builders/model/utils.py +337 -45
- dapper_sqls/builders/query.py +165 -44
- dapper_sqls/builders/stored.py +16 -10
- dapper_sqls/builders/stp.py +6 -2
- dapper_sqls/config.py +41 -32
- dapper_sqls/dapper/dapper.py +1 -1
- dapper_sqls/dapper/executors.py +131 -56
- dapper_sqls/decorators.py +5 -3
- dapper_sqls/http/__init__.py +4 -0
- dapper_sqls/http/aiohttp.py +155 -0
- dapper_sqls/http/decorators.py +123 -0
- dapper_sqls/http/models.py +58 -0
- dapper_sqls/http/request.py +140 -0
- dapper_sqls/models/__init__.py +3 -5
- dapper_sqls/models/base.py +246 -20
- dapper_sqls/models/connection.py +2 -2
- dapper_sqls/models/query_field.py +214 -0
- dapper_sqls/models/result.py +315 -45
- dapper_sqls/sqlite/__init__.py +5 -1
- dapper_sqls/sqlite/async_local_database.py +168 -0
- dapper_sqls/sqlite/decorators.py +69 -0
- dapper_sqls/sqlite/installer.py +97 -0
- dapper_sqls/sqlite/local_database.py +67 -185
- dapper_sqls/sqlite/models.py +51 -1
- dapper_sqls/sqlite/utils.py +9 -0
- dapper_sqls/utils.py +18 -6
- dapper_sqls-1.2.0.dist-info/METADATA +41 -0
- dapper_sqls-1.2.0.dist-info/RECORD +40 -0
- {dapper_sqls-0.9.7.dist-info → dapper_sqls-1.2.0.dist-info}/WHEEL +1 -1
- dapper_sqls-0.9.7.dist-info/METADATA +0 -19
- dapper_sqls-0.9.7.dist-info/RECORD +0 -30
- {dapper_sqls-0.9.7.dist-info → dapper_sqls-1.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,214 @@
|
|
1
|
+
from typing import Union, List, Literal, Any, Optional
|
2
|
+
from pydantic import BaseModel, Field, create_model
|
3
|
+
from datetime import datetime, date
|
4
|
+
from abc import ABC, abstractmethod
|
5
|
+
|
6
|
+
class QueryFieldBase(BaseModel, ABC):
|
7
|
+
|
8
|
+
class Config:
|
9
|
+
extra = "forbid"
|
10
|
+
|
11
|
+
prefix: Optional[str] = Field(
|
12
|
+
default=...,
|
13
|
+
description="Optional prefix to be prepended to the SQL condition (e.g., for parentheses or NOT)"
|
14
|
+
)
|
15
|
+
suffix: Optional[str] = Field(
|
16
|
+
default=...,
|
17
|
+
description="Optional suffix to be appended to the SQL condition (e.g., for closing parentheses)"
|
18
|
+
)
|
19
|
+
|
20
|
+
def quote(self, val):
|
21
|
+
if isinstance(val, str):
|
22
|
+
val = val.replace("'", "''")
|
23
|
+
return f"'{val}'"
|
24
|
+
elif isinstance(val, bool):
|
25
|
+
return '1' if val else '0'
|
26
|
+
elif isinstance(val, datetime):
|
27
|
+
return f"'{val.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}'"
|
28
|
+
elif isinstance(val, date):
|
29
|
+
return f"'{val.strftime('%Y-%m-%d')}'"
|
30
|
+
return str(val)
|
31
|
+
|
32
|
+
def format_sql(self, field_name: str, value_expr: str, operator: str) -> str:
|
33
|
+
prefix = self.prefix if isinstance(self.prefix, str) else ""
|
34
|
+
suffix = self.suffix if isinstance(self.suffix, str) else ""
|
35
|
+
return f"{prefix}{field_name} {operator} {value_expr}{suffix}"
|
36
|
+
|
37
|
+
@abstractmethod
|
38
|
+
def to_sql(self, field_name: str):
|
39
|
+
...
|
40
|
+
|
41
|
+
|
42
|
+
class StringQueryField(QueryFieldBase):
|
43
|
+
value: Union[str, List[str]] = Field(
|
44
|
+
default=...,
|
45
|
+
description="The value or list of values to compare against the string column"
|
46
|
+
)
|
47
|
+
operator: Literal['=', '!=', 'LIKE', 'IN', 'NOT IN'] = Field(
|
48
|
+
default=...,
|
49
|
+
description="SQL operator used for comparison"
|
50
|
+
)
|
51
|
+
|
52
|
+
case_insensitive: bool = Field(
|
53
|
+
default=...,
|
54
|
+
description="Whether to apply case-insensitive matching (uses UPPER() on field and value)"
|
55
|
+
)
|
56
|
+
|
57
|
+
def apply_like_pattern(self, v: str) -> str:
|
58
|
+
if self.operator == 'LIKE':
|
59
|
+
return f"%{v}%"
|
60
|
+
return v
|
61
|
+
|
62
|
+
def to_sql(self, field_name: str) -> str:
|
63
|
+
field_expr = f"UPPER({field_name})" if self.case_insensitive else field_name
|
64
|
+
|
65
|
+
if isinstance(self.value, list):
|
66
|
+
values = [self.apply_like_pattern(v) for v in self.value]
|
67
|
+
values = [v.upper() if self.case_insensitive else v for v in values]
|
68
|
+
value_expr = "(" + ", ".join(self.quote(v) for v in values) + ")"
|
69
|
+
else:
|
70
|
+
val = self.apply_like_pattern(self.value)
|
71
|
+
val = val.upper() if self.case_insensitive else val
|
72
|
+
value_expr = self.quote(val)
|
73
|
+
|
74
|
+
return self.format_sql(field_expr, value_expr, self.operator)
|
75
|
+
|
76
|
+
class NumericQueryField(QueryFieldBase):
|
77
|
+
value: Union[int, float, List[Union[int, float]]] = Field(
|
78
|
+
default=...,
|
79
|
+
description="The numeric value or list of values to compare against the column"
|
80
|
+
)
|
81
|
+
operator: Literal['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN'] = Field(
|
82
|
+
default=...,
|
83
|
+
description="SQL operator used for numeric comparison"
|
84
|
+
)
|
85
|
+
|
86
|
+
def to_sql(self, field_name: str) -> str:
|
87
|
+
if isinstance(self.value, list):
|
88
|
+
value_expr = "(" + ", ".join(str(v) for v in self.value) + ")"
|
89
|
+
else:
|
90
|
+
value_expr = str(self.value)
|
91
|
+
|
92
|
+
return self.format_sql(field_name, value_expr, self.operator)
|
93
|
+
|
94
|
+
class BoolQueryField(QueryFieldBase):
|
95
|
+
value: bool = Field(
|
96
|
+
default=...,
|
97
|
+
description="Boolean value to compare against the column"
|
98
|
+
)
|
99
|
+
operator: Literal['=', '!='] = Field(
|
100
|
+
default=...,
|
101
|
+
description="SQL operator used for boolean comparison"
|
102
|
+
)
|
103
|
+
|
104
|
+
def to_sql(self, field_name: str) -> str:
|
105
|
+
value_expr = '1' if self.value else '0'
|
106
|
+
return self.format_sql(field_name, value_expr, self.operator)
|
107
|
+
|
108
|
+
|
109
|
+
class DateQueryField(QueryFieldBase):
|
110
|
+
value:Union[str, datetime, date] = Field(
|
111
|
+
default=...,
|
112
|
+
description="Date or datetime value to compare (can also be a string in ISO format)"
|
113
|
+
)
|
114
|
+
operator: Literal['=', '!=', '>', '<', '>=', '<='] = Field(
|
115
|
+
default=...,
|
116
|
+
description="SQL operator used for date/time comparison"
|
117
|
+
)
|
118
|
+
|
119
|
+
def to_sql(self, field_name: str) -> str:
|
120
|
+
if isinstance(self.value, str):
|
121
|
+
value_expr = f"'{self.value}'"
|
122
|
+
else:
|
123
|
+
value_expr = self.quote(self.value)
|
124
|
+
return self.format_sql(field_name, value_expr, self.operator)
|
125
|
+
|
126
|
+
|
127
|
+
class BytesQueryField(QueryFieldBase):
|
128
|
+
value: Union[bytes, List[bytes]] = Field(
|
129
|
+
default=...,
|
130
|
+
description="The bytes value or list of byte values to compare against the column"
|
131
|
+
)
|
132
|
+
operator: Literal['=', '!=', 'IN', 'NOT IN'] = Field(
|
133
|
+
default=...,
|
134
|
+
description="SQL operator used for byte comparison"
|
135
|
+
)
|
136
|
+
|
137
|
+
def to_sql(self, field_name: str) -> str:
|
138
|
+
def format_byte(b: bytes) -> str:
|
139
|
+
return "0x" + b.hex() # SQL Server format
|
140
|
+
|
141
|
+
if isinstance(self.value, list):
|
142
|
+
value_expr = "(" + ", ".join(format_byte(v) for v in self.value) + ")"
|
143
|
+
else:
|
144
|
+
value_expr = format_byte(self.value)
|
145
|
+
|
146
|
+
return self.format_sql(field_name, value_expr, self.operator)
|
147
|
+
|
148
|
+
class BaseJoinConditionField(BaseModel):
|
149
|
+
class Config:
|
150
|
+
extra = "forbid"
|
151
|
+
|
152
|
+
join_table_column: str = Field(
|
153
|
+
...,
|
154
|
+
description="Join table column"
|
155
|
+
)
|
156
|
+
operator: Literal['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE'] = Field(
|
157
|
+
default=...,
|
158
|
+
description="SQL operator used for join condition"
|
159
|
+
)
|
160
|
+
|
161
|
+
def to_sql(self, alias_table : str, field_name: str) -> str:
|
162
|
+
right = f"{alias_table}.{self.join_table_column}"
|
163
|
+
return f"{field_name} {self.operator} {right}"
|
164
|
+
|
165
|
+
@classmethod
|
166
|
+
def with_join_table_column_type(cls, join_table_column_type: Any):
|
167
|
+
new_model = create_model(
|
168
|
+
cls.__name__,
|
169
|
+
__base__=cls,
|
170
|
+
join_table_column=(
|
171
|
+
join_table_column_type,
|
172
|
+
Field(
|
173
|
+
...,
|
174
|
+
description=cls.model_fields['join_table_column'].description
|
175
|
+
)
|
176
|
+
),
|
177
|
+
)
|
178
|
+
return new_model
|
179
|
+
|
180
|
+
class JoinNumericCondition(BaseJoinConditionField):
|
181
|
+
operator: Literal['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN'] = Field(
|
182
|
+
default=...,
|
183
|
+
description="SQL operator used for numeric comparison"
|
184
|
+
)
|
185
|
+
|
186
|
+
class JoinNumericCondition(BaseJoinConditionField):
|
187
|
+
operator: Literal['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN'] = Field(
|
188
|
+
default=...,
|
189
|
+
description="SQL operator used for numeric comparison in a join condition"
|
190
|
+
)
|
191
|
+
|
192
|
+
class JoinStringCondition(BaseJoinConditionField):
|
193
|
+
operator: Literal['=', '!=', 'LIKE', 'IN', 'NOT IN'] = Field(
|
194
|
+
default=...,
|
195
|
+
description="SQL operator used for string comparison in a join condition"
|
196
|
+
)
|
197
|
+
|
198
|
+
class JoinBooleanCondition(BaseJoinConditionField):
|
199
|
+
operator: Literal['=', '!='] = Field(
|
200
|
+
default=...,
|
201
|
+
description="SQL operator used for boolean comparison in a join condition"
|
202
|
+
)
|
203
|
+
|
204
|
+
class JoinDateCondition(BaseJoinConditionField):
|
205
|
+
operator: Literal['=', '!=', '>', '<', '>=', '<='] = Field(
|
206
|
+
default=...,
|
207
|
+
description="SQL operator used for date/time comparison in a join condition"
|
208
|
+
)
|
209
|
+
|
210
|
+
class JoinBytesCondition(BaseJoinConditionField):
|
211
|
+
operator: Literal['=', '!=', 'IN', 'NOT IN'] = Field(
|
212
|
+
default=...,
|
213
|
+
description="SQL operator used for byte comparison in a join condition"
|
214
|
+
)
|
dapper_sqls/models/result.py
CHANGED
@@ -1,27 +1,168 @@
|
|
1
|
-
#
|
1
|
+
# coding: utf-8
|
2
|
+
from typing import Generic, Any
|
3
|
+
from .._types import SqlErrorType, SQL_ERROR_HTTP_CODES, T
|
4
|
+
from .base import SensitiveFields
|
5
|
+
import json
|
6
|
+
from collections import defaultdict
|
2
7
|
|
3
8
|
def result_dict(cursor, result):
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
return dict(
|
10
|
+
zip(
|
11
|
+
[column[0] for column in cursor.description],
|
12
|
+
result
|
13
|
+
)
|
14
|
+
)
|
10
15
|
|
11
|
-
|
16
|
+
def classify_error(message: str) -> SqlErrorType:
|
17
|
+
msg = message.lower()
|
18
|
+
|
19
|
+
if "unique key constraint" in msg or "duplicate key" in msg:
|
20
|
+
return SqlErrorType.UNIQUE_VIOLATION
|
21
|
+
if "foreign key constraint" in msg:
|
22
|
+
return SqlErrorType.FOREIGN_KEY_VIOLATION
|
23
|
+
if "check constraint" in msg:
|
24
|
+
return SqlErrorType.CHECK_CONSTRAINT_VIOLATION
|
25
|
+
if "permission denied" in msg or "permission violation" in msg:
|
26
|
+
return SqlErrorType.PERMISSION_DENIED
|
27
|
+
if "syntax error" in msg:
|
28
|
+
return SqlErrorType.SYNTAX_ERROR
|
29
|
+
if "timeout" in msg:
|
30
|
+
return SqlErrorType.TIMEOUT
|
31
|
+
if any(kw in msg for kw in [
|
32
|
+
"could not connect",
|
33
|
+
"connection failed",
|
34
|
+
"server not found",
|
35
|
+
"network-related",
|
36
|
+
"login failed",
|
37
|
+
"connection timeout",
|
38
|
+
"transport-level error",
|
39
|
+
"communication link failure"
|
40
|
+
]):
|
41
|
+
return SqlErrorType.CONNECTION_ERROR
|
12
42
|
|
13
|
-
|
14
|
-
|
43
|
+
return SqlErrorType.UNKNOWN
|
44
|
+
|
45
|
+
class Error(object):
|
46
|
+
def __init__(self, exception: Exception = None):
|
47
|
+
self.message = str(exception) if isinstance(exception, Exception) else ""
|
48
|
+
self.type = classify_error(self.message)
|
49
|
+
|
50
|
+
class BaseResult(object):
|
51
|
+
def __init__(self, query : str | tuple):
|
52
|
+
if isinstance(query, tuple):
|
53
|
+
q_str, *params = query
|
54
|
+
stored_procedure = {
|
55
|
+
"query": q_str,
|
56
|
+
"params": [list(p) if isinstance(p, tuple) else p for p in params]
|
57
|
+
}
|
58
|
+
self._query = json.dumps(stored_procedure)
|
59
|
+
else:
|
60
|
+
self._query = query
|
61
|
+
|
62
|
+
@property
|
63
|
+
def query(self):
|
64
|
+
return self._query
|
65
|
+
|
66
|
+
class Result(object):
|
67
|
+
|
68
|
+
class Count(BaseResult):
|
69
|
+
def __init__(self, query : str | tuple, result : int | str, status_code : int, error: Error):
|
70
|
+
super().__init__(query)
|
71
|
+
self._count = result
|
15
72
|
self._status_code = status_code
|
16
|
-
self.
|
17
|
-
|
73
|
+
self._success = bool(status_code == 200)
|
74
|
+
self._error = error
|
75
|
+
|
76
|
+
def model_dump(self):
|
77
|
+
if self.success:
|
78
|
+
return {'status_code': self.status_code, 'count': self.count}
|
79
|
+
else:
|
80
|
+
return {'status_code': self.status_code, 'message': self.error.message}
|
81
|
+
|
82
|
+
@property
|
83
|
+
def count(self):
|
84
|
+
return self._count
|
85
|
+
|
86
|
+
@property
|
87
|
+
def status_code(self):
|
88
|
+
return self._status_code
|
89
|
+
|
90
|
+
@property
|
91
|
+
def success(self):
|
92
|
+
return self._success
|
93
|
+
|
94
|
+
@property
|
95
|
+
def error(self):
|
96
|
+
return self._error
|
97
|
+
|
98
|
+
class Fetchone(BaseResult):
|
99
|
+
def __init__(self, query : str | tuple, cursor, result, exception: Exception = None):
|
100
|
+
super().__init__(query)
|
101
|
+
self._error = Error(exception)
|
102
|
+
self._list = []
|
103
|
+
self._dict : dict[str, Any] = {}
|
104
|
+
if cursor != None:
|
105
|
+
self._status_code = 200
|
18
106
|
self._success = True
|
19
|
-
|
20
|
-
|
107
|
+
if result:
|
108
|
+
sensitive_fields = SensitiveFields.get()
|
109
|
+
columns = [column[0] for column in cursor.description]
|
110
|
+
raw_dict = dict(zip(columns, result))
|
111
|
+
self._dict = {
|
112
|
+
k: v for k, v in raw_dict.items()
|
113
|
+
if k not in sensitive_fields
|
114
|
+
}
|
115
|
+
self._list = result
|
21
116
|
else:
|
117
|
+
self._status_code = SQL_ERROR_HTTP_CODES.get(self._error.type, 500)
|
22
118
|
self._success = False
|
23
|
-
|
24
|
-
|
119
|
+
|
120
|
+
def _organize_joined_tables(self, joins: list):
|
121
|
+
alias_to_table_name = {
|
122
|
+
join.model.TABLE_ALIAS: join.model.__class__.__name__ for join in joins
|
123
|
+
}
|
124
|
+
|
125
|
+
if not self._dict:
|
126
|
+
return
|
127
|
+
|
128
|
+
alias_data = defaultdict(dict)
|
129
|
+
keys_to_remove = []
|
130
|
+
|
131
|
+
for key, value in self._dict.items():
|
132
|
+
for alias_table, table_name in alias_to_table_name.items():
|
133
|
+
if alias_table in key:
|
134
|
+
column_name = key.replace(alias_table, '')
|
135
|
+
alias_data[table_name][column_name] = value
|
136
|
+
keys_to_remove.append(key)
|
137
|
+
break
|
138
|
+
|
139
|
+
for key in keys_to_remove:
|
140
|
+
self._dict.pop(key)
|
141
|
+
|
142
|
+
if alias_data:
|
143
|
+
self._dict['joined_tables'] = dict(alias_data)
|
144
|
+
|
145
|
+
if self._list:
|
146
|
+
columns = [col for col in self._dict.keys() if col]
|
147
|
+
self._list = [self._dict[col] for col in columns]
|
148
|
+
|
149
|
+
def model_dump(self, *, include: set[str] = None):
|
150
|
+
if not self.success:
|
151
|
+
return {
|
152
|
+
'status_code': self.status_code,
|
153
|
+
'message': self.error.message
|
154
|
+
}
|
155
|
+
|
156
|
+
result_dict = self._dict.copy()
|
157
|
+
|
158
|
+
if include is not None:
|
159
|
+
include = set(include)
|
160
|
+
result_dict = {k: v for k, v in result_dict.items() if k in include or k == 'joined_tables'}
|
161
|
+
|
162
|
+
return {
|
163
|
+
'status_code': self.status_code,
|
164
|
+
'data': result_dict
|
165
|
+
}
|
25
166
|
|
26
167
|
@property
|
27
168
|
def status_code(self):
|
@@ -40,24 +181,110 @@ class Result(object):
|
|
40
181
|
return self._success
|
41
182
|
|
42
183
|
@property
|
43
|
-
def
|
44
|
-
return self.
|
184
|
+
def error(self):
|
185
|
+
return self._error
|
45
186
|
|
187
|
+
class FetchoneModel(Generic[T]):
|
188
|
+
def __init__(self, model_instance: T, fetchone_result: 'Result.Fetchone'):
|
189
|
+
self._model = model_instance
|
190
|
+
self._fetchone = fetchone_result
|
46
191
|
|
47
|
-
|
192
|
+
@property
|
193
|
+
def query(self):
|
194
|
+
return self._fetchone.query
|
48
195
|
|
49
|
-
|
50
|
-
|
51
|
-
self.
|
52
|
-
|
196
|
+
@property
|
197
|
+
def model(self) -> T:
|
198
|
+
return self._model
|
199
|
+
|
200
|
+
@property
|
201
|
+
def success(self):
|
202
|
+
return self._fetchone.success
|
203
|
+
|
204
|
+
@property
|
205
|
+
def dict(self):
|
206
|
+
return self._fetchone.dict
|
207
|
+
|
208
|
+
@property
|
209
|
+
def list(self):
|
210
|
+
return self._fetchone.list
|
211
|
+
|
212
|
+
@property
|
213
|
+
def status_code(self):
|
214
|
+
return self._fetchone.status_code
|
215
|
+
|
216
|
+
@property
|
217
|
+
def error(self):
|
218
|
+
return self._fetchone.error
|
219
|
+
|
220
|
+
def model_dump(self, *, include: set[str] = None):
|
221
|
+
return self._fetchone.model_dump(include=include)
|
222
|
+
|
223
|
+
|
224
|
+
class Fetchall(BaseResult):
|
225
|
+
|
226
|
+
def __init__(self, query : str | tuple, cursor, result, exception: Exception = None):
|
227
|
+
super().__init__(query)
|
228
|
+
self._error = Error(exception)
|
229
|
+
self._list_dict : list[dict[str, Any]] = []
|
230
|
+
if cursor != None:
|
231
|
+
self._status_code = 200
|
53
232
|
self._success = True
|
54
|
-
|
55
|
-
|
56
|
-
|
233
|
+
if result:
|
234
|
+
sensitive_fields = SensitiveFields.get()
|
235
|
+
columns = [column[0] for column in cursor.description]
|
236
|
+
for r in result:
|
237
|
+
raw_dict = dict(zip(columns, r))
|
238
|
+
clean_dict = {
|
239
|
+
k: v for k, v in raw_dict.items()
|
240
|
+
if k not in sensitive_fields
|
241
|
+
}
|
242
|
+
self._list_dict.append(clean_dict)
|
57
243
|
else:
|
244
|
+
self._status_code = SQL_ERROR_HTTP_CODES.get(self._error.type, 500)
|
58
245
|
self._success = False
|
59
|
-
self._list_dict = []
|
60
246
|
|
247
|
+
def _organize_joined_tables(self, joins: list):
|
248
|
+
alias_to_table_name = {
|
249
|
+
join.model.TABLE_ALIAS: join.model.__class__.__name__ for join in joins
|
250
|
+
}
|
251
|
+
|
252
|
+
for item in self._list_dict:
|
253
|
+
alias_data = defaultdict(dict)
|
254
|
+
keys_to_remove = []
|
255
|
+
|
256
|
+
for key, value in item.items():
|
257
|
+
for alias_table, table_name in alias_to_table_name.items():
|
258
|
+
if alias_table in key:
|
259
|
+
column_name = key.replace(alias_table, '')
|
260
|
+
alias_data[table_name][column_name] = value
|
261
|
+
keys_to_remove.append(key)
|
262
|
+
break
|
263
|
+
|
264
|
+
for key in keys_to_remove:
|
265
|
+
del item[key]
|
266
|
+
|
267
|
+
if alias_data:
|
268
|
+
item['joined_tables'] = dict(alias_data)
|
269
|
+
|
270
|
+
def model_dump(self, *, include: set[str] = None):
|
271
|
+
if not self.success:
|
272
|
+
return {
|
273
|
+
'status_code': self.status_code,
|
274
|
+
'message': self.error.message
|
275
|
+
}
|
276
|
+
|
277
|
+
data = self._list_dict
|
278
|
+
|
279
|
+
if include is not None:
|
280
|
+
include = set(include)
|
281
|
+
data = [{k: v for k, v in d.items() if k in include or k == 'joined_tables'} for d in data]
|
282
|
+
|
283
|
+
return {
|
284
|
+
'status_code': self.status_code,
|
285
|
+
'data': data
|
286
|
+
}
|
287
|
+
|
61
288
|
@property
|
62
289
|
def status_code(self):
|
63
290
|
return self._status_code
|
@@ -66,24 +293,60 @@ class Result(object):
|
|
66
293
|
def list_dict(self):
|
67
294
|
return self._list_dict
|
68
295
|
|
69
|
-
@property
|
70
|
-
def dict(self):
|
71
|
-
return self._dict
|
72
|
-
|
73
296
|
@property
|
74
297
|
def success(self):
|
75
298
|
return self._success
|
76
299
|
|
77
300
|
@property
|
78
|
-
def
|
79
|
-
return self.
|
301
|
+
def error(self):
|
302
|
+
return self._error
|
303
|
+
|
304
|
+
class FetchallModel(Generic[T]):
|
305
|
+
def __init__(self, model_list: list[T], fetchall_result: 'Result.Fetchall'):
|
306
|
+
self._models = model_list
|
307
|
+
self._fetchall = fetchall_result
|
308
|
+
|
309
|
+
@property
|
310
|
+
def query(self):
|
311
|
+
return self._fetchall.query
|
312
|
+
|
313
|
+
@property
|
314
|
+
def models(self) -> list[T]:
|
315
|
+
return self._models
|
316
|
+
|
317
|
+
@property
|
318
|
+
def success(self):
|
319
|
+
return self._fetchall.success
|
320
|
+
|
321
|
+
@property
|
322
|
+
def list_dict(self):
|
323
|
+
return self._fetchall.list_dict
|
324
|
+
|
325
|
+
@property
|
326
|
+
def status_code(self):
|
327
|
+
return self._fetchall.status_code
|
328
|
+
|
329
|
+
@property
|
330
|
+
def error(self):
|
331
|
+
return self._fetchall.error
|
80
332
|
|
81
|
-
|
82
|
-
|
333
|
+
def model_dump(self, *, include: set[str] = None):
|
334
|
+
include = set(include)
|
335
|
+
return self._fetchall.model_dump(include=include)
|
336
|
+
|
337
|
+
class Insert(BaseResult):
|
338
|
+
def __init__(self, query : str | tuple, result : int | str, status_code : int, error: Error):
|
339
|
+
super().__init__(query)
|
83
340
|
self._id = result
|
84
341
|
self._status_code = status_code
|
85
|
-
self._success = bool(
|
86
|
-
self.
|
342
|
+
self._success = bool(status_code == 200)
|
343
|
+
self._error = error
|
344
|
+
|
345
|
+
def model_dump(self):
|
346
|
+
if self.success:
|
347
|
+
return {'status_code': self.status_code, 'id': self.id}
|
348
|
+
else:
|
349
|
+
return {'status_code': self.status_code, 'message': self.error.message}
|
87
350
|
|
88
351
|
@property
|
89
352
|
def id(self):
|
@@ -98,15 +361,22 @@ class Result(object):
|
|
98
361
|
return self._success
|
99
362
|
|
100
363
|
@property
|
101
|
-
def
|
102
|
-
return self.
|
364
|
+
def error(self):
|
365
|
+
return self._error
|
103
366
|
|
104
|
-
class Send:
|
105
|
-
def __init__(self,
|
106
|
-
|
367
|
+
class Send(BaseResult):
|
368
|
+
def __init__(self, query : str | tuple, result : bool, exception: Exception = None):
|
369
|
+
super().__init__(query)
|
370
|
+
self._error = Error(exception)
|
371
|
+
self._status_code = 200 if result else SQL_ERROR_HTTP_CODES.get(self._error.type, 500)
|
107
372
|
self._success = result
|
108
|
-
self._message = message
|
109
373
|
|
374
|
+
def model_dump(self):
|
375
|
+
if self.success:
|
376
|
+
return {'status_code': self.status_code}
|
377
|
+
else:
|
378
|
+
return {'status_code': self.status_code, 'message': self.error.message}
|
379
|
+
|
110
380
|
@property
|
111
381
|
def status_code(self):
|
112
382
|
return self._status_code
|
@@ -116,8 +386,8 @@ class Result(object):
|
|
116
386
|
return self._success
|
117
387
|
|
118
388
|
@property
|
119
|
-
def
|
120
|
-
return self.
|
389
|
+
def error(self):
|
390
|
+
return self._error
|
121
391
|
|
122
392
|
|
123
393
|
|
dapper_sqls/sqlite/__init__.py
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
from .local_database import
|
1
|
+
from .local_database import BaseLocalDatabase
|
2
|
+
from .async_local_database import BaseAsyncLocalDatabase
|
3
|
+
from .installer import DataBaseInstall
|
4
|
+
from .models import BaseTables
|
5
|
+
from .decorators import safe_sqlite_operation, SqliteErrorType
|
2
6
|
|
3
7
|
|
4
8
|
|