jxORM 0.1.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.
- jxORM/__init__.py +10 -0
- jxORM/orm/__init__.py +5 -0
- jxORM/orm/common.py +426 -0
- jxORM/orm/jxUtils.py +399 -0
- jxORM/orm/orm.py +421 -0
- jxORM/orm/query.py +462 -0
- jxorm-0.1.0.dist-info/LICENSE +12 -0
- jxorm-0.1.0.dist-info/METADATA +162 -0
- jxorm-0.1.0.dist-info/RECORD +11 -0
- jxorm-0.1.0.dist-info/WHEEL +5 -0
- jxorm-0.1.0.dist-info/top_level.txt +1 -0
jxORM/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
os.makedirs("logs", exist_ok=True)
|
|
5
|
+
|
|
6
|
+
from .orm.jxUtils import set_host_id, logger as jxORMLogger
|
|
7
|
+
from .orm.orm import ORM, get_by_type_id, get_by_type_name, select
|
|
8
|
+
from .orm.common import DBDataType, ColType, jxDB, DBType, set_default_db, get_default_db, get_db, register_create_db
|
|
9
|
+
|
|
10
|
+
__all__ = ['set_host_id', 'jxORMLogger', 'ORM', 'select', 'DBDataType', 'ColType', 'jxDB', 'DBType', 'set_default_db', 'get_default_db', 'get_db', 'register_create_db', ]
|
jxORM/orm/__init__.py
ADDED
jxORM/orm/common.py
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
|
|
4
|
+
import traceback
|
|
5
|
+
import datetime
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from functools import wraps
|
|
9
|
+
from threading import RLock
|
|
10
|
+
import datetime
|
|
11
|
+
import enum
|
|
12
|
+
|
|
13
|
+
from jxORM.orm.jxUtils import logger, checkAssert, transValue2Datetime, transValue2Dict, transValue2String, stringAdd, trip_quotes
|
|
14
|
+
|
|
15
|
+
class DBType(enum.IntEnum):
|
|
16
|
+
No = 0
|
|
17
|
+
SQLite = 1
|
|
18
|
+
MySQL = 2
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_string(cls, s):
|
|
22
|
+
if s == 'mysql':
|
|
23
|
+
return DBType.MySQL
|
|
24
|
+
if s == 'sqlite':
|
|
25
|
+
return DBType.SQLite
|
|
26
|
+
return DBType.No
|
|
27
|
+
|
|
28
|
+
class ColType(enum.IntEnum):
|
|
29
|
+
PrimaryKey = 0
|
|
30
|
+
Index = 1
|
|
31
|
+
|
|
32
|
+
class DBDataType(enum.IntEnum):
|
|
33
|
+
No = -1
|
|
34
|
+
Int = 1
|
|
35
|
+
Long = 2
|
|
36
|
+
Float = 3
|
|
37
|
+
Double = 4
|
|
38
|
+
Bool = 5
|
|
39
|
+
Chars = 6
|
|
40
|
+
String = 7
|
|
41
|
+
DataTime = 8
|
|
42
|
+
Json = 9
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_string(cls, s):
|
|
46
|
+
if s == 'int':
|
|
47
|
+
return DBDataType.Int
|
|
48
|
+
if s == 'long':
|
|
49
|
+
return DBDataType.Long
|
|
50
|
+
if s == 'float':
|
|
51
|
+
return DBDataType.Float
|
|
52
|
+
if s == 'double':
|
|
53
|
+
return DBDataType.Double
|
|
54
|
+
if s == 'bool':
|
|
55
|
+
return DBDataType.Bool
|
|
56
|
+
if s == 'chars':
|
|
57
|
+
return DBDataType.Chars
|
|
58
|
+
if s == 'string':
|
|
59
|
+
return DBDataType.String
|
|
60
|
+
if s == 'datetime':
|
|
61
|
+
return DBDataType.DataTime
|
|
62
|
+
if s == 'json':
|
|
63
|
+
return DBDataType.Json
|
|
64
|
+
return DBDataType.No
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
_dbConfig = {}
|
|
68
|
+
_db_create_funcs = {}
|
|
69
|
+
_dbPool = {}
|
|
70
|
+
_db_name = None
|
|
71
|
+
def set_default_db(dbName):
|
|
72
|
+
global _db_name
|
|
73
|
+
_db_name = dbName
|
|
74
|
+
def get_default_dbname():
|
|
75
|
+
return _db_name
|
|
76
|
+
def register_create_db(db_type, func):
|
|
77
|
+
_db_create_funcs[db_type] = func
|
|
78
|
+
def get_ty(cl, attr):
|
|
79
|
+
for c in cl:
|
|
80
|
+
dt = c.get_ty_with_field(attr)
|
|
81
|
+
if dt is not None:
|
|
82
|
+
return dt
|
|
83
|
+
return None
|
|
84
|
+
def get_db_result(result_list, descriptions, cls_list:list, need_trans=True):
|
|
85
|
+
if result_list is None:
|
|
86
|
+
return None
|
|
87
|
+
if not need_trans or descriptions is None:
|
|
88
|
+
return result_list
|
|
89
|
+
column_names = [description[0] for description in descriptions]
|
|
90
|
+
rs = []
|
|
91
|
+
for r in result_list:
|
|
92
|
+
result = dict(zip(column_names, r))
|
|
93
|
+
d = {}
|
|
94
|
+
rs.append(d)
|
|
95
|
+
for k, v in result.items():
|
|
96
|
+
dt = get_ty(cls_list, k)
|
|
97
|
+
if dt is not None:
|
|
98
|
+
d[k] = transFromDB(dt, v)
|
|
99
|
+
continue
|
|
100
|
+
d[k] = v
|
|
101
|
+
return rs
|
|
102
|
+
|
|
103
|
+
def transFromDB(dt: DBDataType, dv):
|
|
104
|
+
if dt == DBDataType.Int:
|
|
105
|
+
return int(dv)
|
|
106
|
+
if dt == DBDataType.Long:
|
|
107
|
+
return int(dv)
|
|
108
|
+
if dt == DBDataType.Float:
|
|
109
|
+
return float(dv)
|
|
110
|
+
if dt == DBDataType.Double:
|
|
111
|
+
return float(dv)
|
|
112
|
+
if dt == DBDataType.Bool:
|
|
113
|
+
try:
|
|
114
|
+
return bool(dv)
|
|
115
|
+
except:
|
|
116
|
+
return False
|
|
117
|
+
if dt == DBDataType.String:
|
|
118
|
+
s = str(dv)
|
|
119
|
+
return trip_quotes(s)
|
|
120
|
+
if dt == DBDataType.Chars:
|
|
121
|
+
s = str(dv)
|
|
122
|
+
return trip_quotes(s)
|
|
123
|
+
if dt == DBDataType.DataTime:
|
|
124
|
+
if isinstance(dv, str):
|
|
125
|
+
dv = trip_quotes(dv)
|
|
126
|
+
return transValue2Datetime(dv)
|
|
127
|
+
if dt == DBDataType.Json:
|
|
128
|
+
if isinstance(dv, str):
|
|
129
|
+
dv = dv[1:-1]
|
|
130
|
+
return transValue2Dict(dv)
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
def transToDB(dt: DBDataType, dv):
|
|
134
|
+
if dt == DBDataType.Int:
|
|
135
|
+
return int(dv)
|
|
136
|
+
if dt == DBDataType.Long:
|
|
137
|
+
return int(dv)
|
|
138
|
+
if dt == DBDataType.Float:
|
|
139
|
+
return float(dv)
|
|
140
|
+
if dt == DBDataType.Double:
|
|
141
|
+
return float(dv)
|
|
142
|
+
if dt == DBDataType.Bool:
|
|
143
|
+
try:
|
|
144
|
+
b = bool(dv)
|
|
145
|
+
if b:
|
|
146
|
+
return 1
|
|
147
|
+
return 0
|
|
148
|
+
except:
|
|
149
|
+
return 0
|
|
150
|
+
if dt == DBDataType.String:
|
|
151
|
+
return f'\"{str(dv)}\"'
|
|
152
|
+
if dt == DBDataType.Chars:
|
|
153
|
+
return f'\"{str(dv)}\"'
|
|
154
|
+
if dt == DBDataType.DataTime:
|
|
155
|
+
if isinstance(dv, datetime.datetime):
|
|
156
|
+
return f'\"{dv.strftime("%Y-%m-%d %H:%M:%S")}\"'
|
|
157
|
+
elif isinstance(dv, str):
|
|
158
|
+
return f'\"{dv}\"'
|
|
159
|
+
else:
|
|
160
|
+
try:
|
|
161
|
+
dv = int(dv)
|
|
162
|
+
dt = datetime.datetime.fromtimestamp(dv)
|
|
163
|
+
return f'\"{dt.strftime("%Y-%m-%d %H:%M:%S")}\"'
|
|
164
|
+
except:
|
|
165
|
+
return ''
|
|
166
|
+
if dt == DBDataType.Json:
|
|
167
|
+
s = json.dumps(dv)
|
|
168
|
+
return f"'{s}'"
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
class jxDB:
|
|
172
|
+
@classmethod
|
|
173
|
+
def set(cls, dbName, type=DBType.MySQL, is_default=True, host='127.0.0.1', port=3306, user='root', password='root', **kwargs):
|
|
174
|
+
dc = jxDB(type=type, host=host, port=port, user=user, password=password)
|
|
175
|
+
dc.attrs = kwargs
|
|
176
|
+
_dbConfig[dbName] = dc
|
|
177
|
+
if is_default:
|
|
178
|
+
set_default_db(dbName)
|
|
179
|
+
|
|
180
|
+
def __init__(self, type=DBType.MySQL, host='127.0.0.1', port=3306, user='root', password='root'):
|
|
181
|
+
self.type = type
|
|
182
|
+
self.host = host
|
|
183
|
+
self.port = port
|
|
184
|
+
self.user = user
|
|
185
|
+
self.password = password
|
|
186
|
+
self.attrs = {}
|
|
187
|
+
|
|
188
|
+
class DB_interface:
|
|
189
|
+
def __init__(self):
|
|
190
|
+
self.conn = None
|
|
191
|
+
self.cursor = None
|
|
192
|
+
self._name = None
|
|
193
|
+
|
|
194
|
+
def type(self):
|
|
195
|
+
# 数据库类型
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
def name(self):
|
|
199
|
+
# 数据库类型
|
|
200
|
+
return self._name
|
|
201
|
+
|
|
202
|
+
def need_trans(self):
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
def description(self):
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
def _get_create_table_sql(self, table_name, fields, keys, indexs):
|
|
209
|
+
return []
|
|
210
|
+
def create_table(self, table_name, fields, keys, indexs):
|
|
211
|
+
sl = self._get_create_table_sql(table_name, fields, keys, indexs)
|
|
212
|
+
for sql in sl:
|
|
213
|
+
self.execute(sql)
|
|
214
|
+
|
|
215
|
+
def commit(self):
|
|
216
|
+
self.conn.commit()
|
|
217
|
+
|
|
218
|
+
def get_db_type(self, ty):
|
|
219
|
+
return ''
|
|
220
|
+
|
|
221
|
+
def rollback(self):
|
|
222
|
+
self.conn.rollback()
|
|
223
|
+
|
|
224
|
+
def execute(self, sql):
|
|
225
|
+
logger.info(f'execute sql:{sql}')
|
|
226
|
+
return self.cursor.execute(sql)
|
|
227
|
+
|
|
228
|
+
def fetchone(self):
|
|
229
|
+
return self.cursor.fetchone()
|
|
230
|
+
|
|
231
|
+
def fetchall(self):
|
|
232
|
+
return self.cursor.fetchall()
|
|
233
|
+
|
|
234
|
+
def trans2String(self, vt, v):
|
|
235
|
+
return transValue2String(vt, v, withQuote=True, to_db=True)
|
|
236
|
+
|
|
237
|
+
def transaction(self):
|
|
238
|
+
return DB_interface._transaction(self)
|
|
239
|
+
|
|
240
|
+
def __enter__(self):
|
|
241
|
+
if self.conn is not None:
|
|
242
|
+
self.cursor = self.conn.cursor()
|
|
243
|
+
self.conn.begin()
|
|
244
|
+
return self
|
|
245
|
+
|
|
246
|
+
def __exit__(self, type, value, trace):
|
|
247
|
+
if self.conn is not None:
|
|
248
|
+
self.cursor.close()
|
|
249
|
+
self.conn.close()
|
|
250
|
+
|
|
251
|
+
class _transaction:
|
|
252
|
+
def __init__(self, db):
|
|
253
|
+
self.db = db
|
|
254
|
+
def __enter__(self):
|
|
255
|
+
self.db.__enter__()
|
|
256
|
+
def __exit__(self, type, value, trace):
|
|
257
|
+
if type is None:
|
|
258
|
+
self.db.commit()
|
|
259
|
+
else:
|
|
260
|
+
self.db.rollback()
|
|
261
|
+
self.db.__exit__(type, value, trace)
|
|
262
|
+
|
|
263
|
+
class DB_mysql(DB_interface):
|
|
264
|
+
def __init__(self, dbname):
|
|
265
|
+
super().__init__()
|
|
266
|
+
self._name = dbname
|
|
267
|
+
self._pool = _dbPool.get(dbname)
|
|
268
|
+
if self._pool is None:
|
|
269
|
+
dc = _dbConfig.get(dbname)
|
|
270
|
+
import pymysql
|
|
271
|
+
from dbutils.pooled_db import PooledDB
|
|
272
|
+
config = {
|
|
273
|
+
'creator': pymysql,
|
|
274
|
+
'host': dc.host,
|
|
275
|
+
'port': dc.port,
|
|
276
|
+
'user': dc.user,
|
|
277
|
+
'password': dc.password,
|
|
278
|
+
'db': dbname,
|
|
279
|
+
'charset': "utf8",
|
|
280
|
+
'cursorclass': pymysql.cursors.DictCursor
|
|
281
|
+
}
|
|
282
|
+
config.update(dc.attrs)
|
|
283
|
+
self._pool = PooledDB(**config)
|
|
284
|
+
_dbPool[dbname] = self._pool
|
|
285
|
+
self.conn = self._pool.connection()
|
|
286
|
+
|
|
287
|
+
def type(self):
|
|
288
|
+
# 数据库类型
|
|
289
|
+
return 'mysql'
|
|
290
|
+
|
|
291
|
+
def get_db_type(self, ty):
|
|
292
|
+
if ty == DBDataType.Int:
|
|
293
|
+
return 'int'
|
|
294
|
+
if ty == DBDataType.Long:
|
|
295
|
+
return 'bigint'
|
|
296
|
+
if ty == DBDataType.Float:
|
|
297
|
+
return 'float'
|
|
298
|
+
if ty == DBDataType.Double:
|
|
299
|
+
return 'double'
|
|
300
|
+
if ty == DBDataType.Bool:
|
|
301
|
+
return 'tinyint'
|
|
302
|
+
if ty == DBDataType.Chars:
|
|
303
|
+
return 'varchar(126)'
|
|
304
|
+
if ty == DBDataType.String:
|
|
305
|
+
return 'mediumtext'
|
|
306
|
+
if ty == DBDataType.DataTime:
|
|
307
|
+
return 'datetime'
|
|
308
|
+
if ty == DBDataType.Json:
|
|
309
|
+
return 'mediumtext'
|
|
310
|
+
return 'mediumtext'
|
|
311
|
+
|
|
312
|
+
def _get_create_table_sql(self, table_name, fields, keys, indexs):
|
|
313
|
+
rs = []
|
|
314
|
+
fl = {}
|
|
315
|
+
pk = None
|
|
316
|
+
il = []
|
|
317
|
+
for k, dt in fields.items():
|
|
318
|
+
fs = f'`{k}` {self.get_db_type(dt)} NOT NULL'
|
|
319
|
+
fl[k] = fs
|
|
320
|
+
if len(keys) > 0:
|
|
321
|
+
ks = None
|
|
322
|
+
for k in keys:
|
|
323
|
+
ks = stringAdd(ks, f'`{k}`', ',')
|
|
324
|
+
pk = f'PRIMARY KEY ({ks})'
|
|
325
|
+
if len(indexs) > 0:
|
|
326
|
+
for i, ins in indexs.items():
|
|
327
|
+
ifs = None
|
|
328
|
+
for ikn in ins:
|
|
329
|
+
ifs = stringAdd(ifs, f'`{ikn}`', ',')
|
|
330
|
+
tis = f'KEY `{table_name}_index_{i}` ({ifs})'
|
|
331
|
+
il.append(tis)
|
|
332
|
+
fs = ','.join(fl.values())
|
|
333
|
+
fs = stringAdd(fs, pk, ',')
|
|
334
|
+
ils = ','.join(il)
|
|
335
|
+
fs = stringAdd(fs, ils, ',')
|
|
336
|
+
cs = f'CREATE TABLE `{table_name}` ({fs}) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;'
|
|
337
|
+
rs.append(cs)
|
|
338
|
+
return rs
|
|
339
|
+
|
|
340
|
+
class DB_sqlite(DB_interface):
|
|
341
|
+
def __init__(self, dbname):
|
|
342
|
+
super().__init__()
|
|
343
|
+
self._name = dbname
|
|
344
|
+
self._pool = _dbPool.get(dbname)
|
|
345
|
+
if self._pool is None:
|
|
346
|
+
import sqlite3
|
|
347
|
+
from dbutils.pooled_db import PooledDB
|
|
348
|
+
config = {
|
|
349
|
+
'creator': sqlite3,
|
|
350
|
+
'mincached': 5,
|
|
351
|
+
'maxcached': 30,
|
|
352
|
+
'check_same_thread': False,
|
|
353
|
+
'database': f'{dbname}.db',
|
|
354
|
+
}
|
|
355
|
+
self._pool = PooledDB(**config)
|
|
356
|
+
_dbPool[dbname] = self._pool
|
|
357
|
+
self.conn = self._pool.connection()
|
|
358
|
+
|
|
359
|
+
def type(self):
|
|
360
|
+
# 数据库类型
|
|
361
|
+
return 'sqlite'
|
|
362
|
+
|
|
363
|
+
def need_trans(self):
|
|
364
|
+
return True
|
|
365
|
+
|
|
366
|
+
def description(self):
|
|
367
|
+
return self.cursor.description
|
|
368
|
+
|
|
369
|
+
def get_db_type(self, ty):
|
|
370
|
+
if ty in [DBDataType.Int, DBDataType.Long, DBDataType.Float, DBDataType.Double,DBDataType.Bool]:
|
|
371
|
+
return 'INTEGER'
|
|
372
|
+
return 'TEXT'
|
|
373
|
+
|
|
374
|
+
def _get_create_table_sql(self, table_name, fields, keys, indexs):
|
|
375
|
+
rs = []
|
|
376
|
+
fl = {}
|
|
377
|
+
for k, dt in fields.items():
|
|
378
|
+
fs = f'`{k}` {self.get_db_type(dt)} NOT NULL'
|
|
379
|
+
fl[k] = fs
|
|
380
|
+
for k in keys:
|
|
381
|
+
fs = fl.get(k, None)
|
|
382
|
+
fs += ' PRIMARY KEY'
|
|
383
|
+
fl[k] = fs
|
|
384
|
+
fls = ','.join(fl.values())
|
|
385
|
+
cs = f'CREATE TABLE `{table_name}` ({fls});'
|
|
386
|
+
rs.append(cs)
|
|
387
|
+
|
|
388
|
+
for i, il in indexs.items():
|
|
389
|
+
fs = None
|
|
390
|
+
for ikn in il:
|
|
391
|
+
fs = stringAdd(fs, f'`{ikn}`', ',')
|
|
392
|
+
cis = f'CREATE INDEX {table_name}_index_{i} ON {table_name} ({fs});'
|
|
393
|
+
rs.append(cis)
|
|
394
|
+
return rs
|
|
395
|
+
|
|
396
|
+
def get_default_db():
|
|
397
|
+
return get_db(_db_name)
|
|
398
|
+
|
|
399
|
+
def get_db(dbname=None):
|
|
400
|
+
if dbname is None:
|
|
401
|
+
dbname = _db_name
|
|
402
|
+
if dbname is not None:
|
|
403
|
+
dc = _dbConfig.get(dbname)
|
|
404
|
+
if dc is not None:
|
|
405
|
+
func = _db_create_funcs.get(dc.type, None)
|
|
406
|
+
if func is not None:
|
|
407
|
+
return func(dbname)
|
|
408
|
+
return None
|
|
409
|
+
|
|
410
|
+
def DB(func):
|
|
411
|
+
@wraps(func)
|
|
412
|
+
def wrapper(obj, dbname, *args, **kw):
|
|
413
|
+
db = get_db(dbname)
|
|
414
|
+
checkAssert(not db is None, '数据库未配置:{}', dbname)
|
|
415
|
+
with db:
|
|
416
|
+
try:
|
|
417
|
+
result = func(obj, db, *args, **kw)
|
|
418
|
+
db.commit()
|
|
419
|
+
return result
|
|
420
|
+
except Exception as e:
|
|
421
|
+
db.rollback()
|
|
422
|
+
raise e
|
|
423
|
+
return wrapper
|
|
424
|
+
|
|
425
|
+
register_create_db(DBType.MySQL, lambda dbname:DB_mysql(dbname))
|
|
426
|
+
register_create_db(DBType.SQLite, lambda dbname:DB_sqlite(dbname))
|