cloudoll 2.2.20__tar.gz → 2.2.24__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.
- {cloudoll-2.2.20 → cloudoll-2.2.24}/PKG-INFO +1 -1
- cloudoll-2.2.24/cloudoll/__init__.py +1 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/orm/__init__.py +115 -21
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/orm/base.py +8 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/orm/field.py +2 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/orm/model.py +123 -15
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/robot/feishu.py +2 -1
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/utils/m2d.py +32 -14
- cloudoll-2.2.24/cloudoll/utils/snowflake.py +86 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll.egg-info/PKG-INFO +1 -1
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll.egg-info/SOURCES.txt +1 -0
- cloudoll-2.2.20/cloudoll/__init__.py +0 -1
- {cloudoll-2.2.20 → cloudoll-2.2.24}/LICENSE +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/README.md +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/__main__.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/cli.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/error/__init__.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/error/errors.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/logging/__init__.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/mail/__init__.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/mail/smtp.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/orm/err.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/orm/mysql.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/orm/parse.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/orm/postgres.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/robot/__init__.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/robot/dingtalk.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/robot/slack.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/utils/__init__.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/utils/common.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/utils/patch_loop.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/utils/watch.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/web/__init__.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/web/exception.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/web/html.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/web/http.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/web/jwt.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll/web/settings.py +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll.egg-info/dependency_links.txt +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll.egg-info/entry_points.txt +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll.egg-info/requires.txt +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/cloudoll.egg-info/top_level.txt +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/setup.cfg +0 -0
- {cloudoll-2.2.20 → cloudoll-2.2.24}/setup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.24"
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
from redis import asyncio as aioredis
|
|
2
2
|
from .parse import parse_coon
|
|
3
|
-
import aiopg, aiomysql
|
|
4
3
|
from .base import MeteBase, QueryTypes
|
|
5
4
|
from typing import Any
|
|
6
5
|
from ..logging import print_info, print_error
|
|
7
6
|
import traceback
|
|
7
|
+
import aiomysql
|
|
8
8
|
from aiomysql import Pool as MYPool
|
|
9
|
+
|
|
10
|
+
import aiopg as pg
|
|
9
11
|
from aiopg.pool import Pool as PGPool
|
|
10
12
|
|
|
13
|
+
# import asyncpg as pg
|
|
14
|
+
# from asyncpg.pool import Pool as PGPool
|
|
15
|
+
|
|
16
|
+
__ALL__ = "create_engine"
|
|
17
|
+
|
|
11
18
|
|
|
12
19
|
async def create_engine(**kw):
|
|
13
20
|
url = kw.get("url")
|
|
@@ -26,7 +33,7 @@ async def create_engine(**kw):
|
|
|
26
33
|
|
|
27
34
|
if driver == "mysql":
|
|
28
35
|
return await Mysql().create_engine(**configs, **query)
|
|
29
|
-
elif driver == "postgres":
|
|
36
|
+
elif driver == "postgres" or driver == "postgressql":
|
|
30
37
|
return await Postgres().create_engine(**configs, **query)
|
|
31
38
|
elif driver == "redis" or driver == "rediss":
|
|
32
39
|
"""
|
|
@@ -48,6 +55,65 @@ class Postgres(MeteBase):
|
|
|
48
55
|
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
49
56
|
self.__init__(*args, **kwds)
|
|
50
57
|
|
|
58
|
+
# async def query(self, sql, params=None, query_type: QueryTypes = 2, size: int = 10):
|
|
59
|
+
# sql = sql.replace("?", "%s").replace("`", "")
|
|
60
|
+
# print_info("sql", sql, params)
|
|
61
|
+
# if not self.pool:
|
|
62
|
+
# raise ValueError("must be create_engine first.")
|
|
63
|
+
# if self.pool._closing or self.pool._closed:
|
|
64
|
+
# return None
|
|
65
|
+
|
|
66
|
+
# # async with self.pool.acquire() as conn:
|
|
67
|
+
# async with self.pool.acquire() as conn:
|
|
68
|
+
# # current_cursor = getattr(cursor, 'lastrowid', None)
|
|
69
|
+
# if (
|
|
70
|
+
# query_type == QueryTypes.CREATEBATCH
|
|
71
|
+
# or query_type == QueryTypes.UPDATEBATCH
|
|
72
|
+
# ):
|
|
73
|
+
# result = await conn.executemany(sql, params) # aiopg don't support executemany
|
|
74
|
+
# else:
|
|
75
|
+
# result = await conn.execute(sql, params)
|
|
76
|
+
|
|
77
|
+
# # result = None
|
|
78
|
+
|
|
79
|
+
# return result
|
|
80
|
+
|
|
81
|
+
# if query_type == QueryTypes.ALL:
|
|
82
|
+
# columns = [desc[0] for desc in cursor.description]
|
|
83
|
+
# rows = await cursor.fetchall()
|
|
84
|
+
# result = [dict(zip(columns, row)) for row in rows]
|
|
85
|
+
# return result
|
|
86
|
+
# elif query_type == QueryTypes.ONE:
|
|
87
|
+
# columns = [desc[0] for desc in cursor.description]
|
|
88
|
+
# row = await cursor.fetchone()
|
|
89
|
+
# # print("rows_reuslt", rows)
|
|
90
|
+
# result = dict(zip(columns, row)) if row else {}
|
|
91
|
+
# return result
|
|
92
|
+
# elif query_type == QueryTypes.MANY:
|
|
93
|
+
# columns = [desc[0] for desc in cursor.description]
|
|
94
|
+
# rows = await cursor.fetchmany(size)
|
|
95
|
+
# result = [dict(zip(columns, row)) for row in rows]
|
|
96
|
+
# return result
|
|
97
|
+
# elif query_type == QueryTypes.COUNT:
|
|
98
|
+
# rs = await cursor.fetchone()
|
|
99
|
+
# return rs[0] if row else 0
|
|
100
|
+
# elif query_type == QueryTypes.CREATE:
|
|
101
|
+
# result = cursor.rowcount > 0
|
|
102
|
+
# id = cursor.lastrowid
|
|
103
|
+
# return result, id
|
|
104
|
+
# elif query_type == QueryTypes.CREATEBATCH:
|
|
105
|
+
# count = cursor.rowcount
|
|
106
|
+
# id = cursor.lastrowid
|
|
107
|
+
# return count, id
|
|
108
|
+
# elif query_type == QueryTypes.UPDATE:
|
|
109
|
+
# return cursor.rowcount > 0
|
|
110
|
+
# elif query_type == QueryTypes.UPDATEBATCH:
|
|
111
|
+
# return cursor.rowcount
|
|
112
|
+
# elif query_type == QueryTypes.DELETE:
|
|
113
|
+
# return cursor.rowcount > 0
|
|
114
|
+
|
|
115
|
+
# self.pool.release(cursor)
|
|
116
|
+
|
|
51
117
|
async def query(self, sql, params=None, query_type: QueryTypes = 2, size: int = 10):
|
|
52
118
|
sql = sql.replace("?", "%s").replace("`", "")
|
|
53
119
|
print_info("sql", sql, params)
|
|
@@ -55,43 +121,56 @@ class Postgres(MeteBase):
|
|
|
55
121
|
raise ValueError("must be create_engine first.")
|
|
56
122
|
if self.pool._closing or self.pool._closed:
|
|
57
123
|
return None
|
|
124
|
+
|
|
58
125
|
async with self.pool.acquire() as conn:
|
|
59
126
|
async with conn.cursor() as cursor:
|
|
60
127
|
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
61
|
-
|
|
128
|
+
if (
|
|
129
|
+
query_type == QueryTypes.CREATEBATCH
|
|
130
|
+
or query_type == QueryTypes.UPDATEBATCH
|
|
131
|
+
):
|
|
132
|
+
# aiopg don't support executemany
|
|
133
|
+
await cursor.executemany(sql, params)
|
|
134
|
+
else:
|
|
135
|
+
await cursor.execute(sql, params)
|
|
62
136
|
|
|
63
|
-
# if query_type.value > 4:
|
|
64
|
-
# if self.driver == "mysql":
|
|
65
|
-
# await conn.commit()
|
|
66
|
-
# elif self.driver == "postgres":
|
|
67
|
-
columns = [desc[0] for desc in cursor.description]
|
|
68
137
|
result = None
|
|
69
138
|
|
|
70
139
|
if query_type == QueryTypes.ALL:
|
|
140
|
+
columns = [desc[0] for desc in cursor.description]
|
|
71
141
|
rows = await cursor.fetchall()
|
|
72
142
|
result = [dict(zip(columns, row)) for row in rows]
|
|
73
143
|
return result
|
|
74
144
|
elif query_type == QueryTypes.ONE:
|
|
75
|
-
|
|
76
|
-
|
|
145
|
+
columns = [desc[0] for desc in cursor.description]
|
|
146
|
+
row = await cursor.fetchone()
|
|
147
|
+
# print("rows_reuslt", rows)
|
|
148
|
+
result = dict(zip(columns, row)) if row else {}
|
|
77
149
|
return result
|
|
78
150
|
elif query_type == QueryTypes.MANY:
|
|
151
|
+
columns = [desc[0] for desc in cursor.description]
|
|
79
152
|
rows = await cursor.fetchmany(size)
|
|
80
153
|
result = [dict(zip(columns, row)) for row in rows]
|
|
81
154
|
return result
|
|
82
155
|
elif query_type == QueryTypes.COUNT:
|
|
83
156
|
rs = await cursor.fetchone()
|
|
84
|
-
return rs[0]
|
|
157
|
+
return rs[0] if row else 0
|
|
85
158
|
elif query_type == QueryTypes.CREATE:
|
|
86
159
|
result = cursor.rowcount > 0
|
|
87
160
|
id = cursor.lastrowid
|
|
88
161
|
return result, id
|
|
162
|
+
elif query_type == QueryTypes.CREATEBATCH:
|
|
163
|
+
count = cursor.rowcount
|
|
164
|
+
id = cursor.lastrowid
|
|
165
|
+
return count, id
|
|
89
166
|
elif query_type == QueryTypes.UPDATE:
|
|
90
167
|
return cursor.rowcount > 0
|
|
168
|
+
elif query_type == QueryTypes.UPDATEBATCH:
|
|
169
|
+
return cursor.rowcount
|
|
91
170
|
elif query_type == QueryTypes.DELETE:
|
|
92
171
|
return cursor.rowcount > 0
|
|
93
172
|
|
|
94
|
-
self.pool.release(
|
|
173
|
+
self.pool.release(cursor)
|
|
95
174
|
|
|
96
175
|
async def create_engine(self, **kw):
|
|
97
176
|
try:
|
|
@@ -100,17 +179,20 @@ class Postgres(MeteBase):
|
|
|
100
179
|
user = (kw.get("username"),)
|
|
101
180
|
password = (str(kw.get("password", "")),)
|
|
102
181
|
db = (kw.get("db"),)
|
|
103
|
-
dsn = f"dbname={db[0]} user={user[0]} password={password[0]} host={host[0]} port={port[0]}"
|
|
104
|
-
|
|
182
|
+
dsn = f"dbname={db[0]} user={user[0]} password={password[0]} host={host[0]} port={port[0]}" # aiopg
|
|
183
|
+
# dsn = f"postgres://{user[0]}:{password[0]}@{host[0]}:{port[0]}/{db[0]}" #asyncpg
|
|
184
|
+
self.pool = await pg.create_pool(
|
|
105
185
|
dsn=dsn,
|
|
106
186
|
timeout=kw.get("timeout"),
|
|
107
|
-
echo=kw.get("echo", False),
|
|
108
|
-
|
|
109
|
-
|
|
187
|
+
echo=kw.get("echo", False), # aiopg
|
|
188
|
+
# max_size=kw.get("maxsize", 10), #asyncpg
|
|
189
|
+
# min_size=kw.get("minsize", 5), #asyncpg
|
|
190
|
+
maxsize=kw.get("maxsize", 10), # aiopg
|
|
191
|
+
minsize=kw.get("minsize", 5), # aiopg
|
|
110
192
|
)
|
|
111
193
|
print_info(f"Database connection successfuly for postgres/{kw.get('db')}")
|
|
112
194
|
except Exception as e:
|
|
113
|
-
|
|
195
|
+
print_error(e)
|
|
114
196
|
# print(traceback.format_exc())
|
|
115
197
|
print_error(
|
|
116
198
|
f"Database connection failed,the instance : postgres/{kw.get('db')}"
|
|
@@ -144,7 +226,7 @@ class Mysql(MeteBase):
|
|
|
144
226
|
|
|
145
227
|
async def query(self, sql, params=None, query_type: QueryTypes = 2, size: int = 10):
|
|
146
228
|
sql = sql.replace("?", "%s")
|
|
147
|
-
print_info("sql", sql, params)
|
|
229
|
+
# print_info("sql", sql, params)
|
|
148
230
|
if not self.pool:
|
|
149
231
|
raise ValueError("must be create_engine first.")
|
|
150
232
|
if self.pool._closing or self.pool._closed:
|
|
@@ -152,7 +234,13 @@ class Mysql(MeteBase):
|
|
|
152
234
|
async with self.pool.acquire() as conn:
|
|
153
235
|
async with conn.cursor() as cursor:
|
|
154
236
|
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
155
|
-
|
|
237
|
+
if (
|
|
238
|
+
query_type == QueryTypes.CREATEBATCH
|
|
239
|
+
or query_type == QueryTypes.UPDATEBATCH
|
|
240
|
+
):
|
|
241
|
+
await cursor.executemany(sql, params)
|
|
242
|
+
else:
|
|
243
|
+
await cursor.execute(sql, params)
|
|
156
244
|
|
|
157
245
|
# if query_type.value > 4:
|
|
158
246
|
# if self.driver == "mysql":
|
|
@@ -176,8 +264,14 @@ class Mysql(MeteBase):
|
|
|
176
264
|
result = cursor.rowcount > 0
|
|
177
265
|
id = cursor.lastrowid
|
|
178
266
|
return result, id
|
|
267
|
+
elif query_type == QueryTypes.CREATEBATCH:
|
|
268
|
+
count = cursor.rowcount
|
|
269
|
+
id = cursor.lastrowid
|
|
270
|
+
return count, id
|
|
179
271
|
elif query_type == QueryTypes.UPDATE:
|
|
180
272
|
return cursor.rowcount > 0
|
|
273
|
+
elif query_type == QueryTypes.UPDATEBATCH:
|
|
274
|
+
return cursor.rowcount
|
|
181
275
|
elif query_type == QueryTypes.DELETE:
|
|
182
276
|
return cursor.rowcount > 0
|
|
183
277
|
|
|
@@ -195,7 +289,7 @@ class Mysql(MeteBase):
|
|
|
195
289
|
charset=kw.get("charset", "utf8"),
|
|
196
290
|
autocommit=False, # kw.get("autocommit", False),
|
|
197
291
|
maxsize=kw.get("maxsize", 10),
|
|
198
|
-
minsize=kw.get("
|
|
292
|
+
minsize=kw.get("minsize", 5),
|
|
199
293
|
cursorclass=AttrDictCursor,
|
|
200
294
|
loop=loop,
|
|
201
295
|
)
|
|
@@ -9,6 +9,8 @@ class QueryTypes(Enum):
|
|
|
9
9
|
CREATE = 5
|
|
10
10
|
UPDATE = 6
|
|
11
11
|
DELETE = 7
|
|
12
|
+
CREATEBATCH = 8
|
|
13
|
+
UPDATEBATCH = 9
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class MeteBase:
|
|
@@ -60,9 +62,15 @@ class MeteBase:
|
|
|
60
62
|
|
|
61
63
|
async def update(self, sql, params):
|
|
62
64
|
return await self.query(sql, params, QueryTypes.UPDATE)
|
|
65
|
+
|
|
66
|
+
async def update_batch(self, sql, params):
|
|
67
|
+
return await self.query(sql, params, QueryTypes.UPDATEBATCH)
|
|
63
68
|
|
|
64
69
|
async def delete(self, sql, params):
|
|
65
70
|
return await self.query(sql, params, QueryTypes.DELETE)
|
|
66
71
|
|
|
67
72
|
async def create(self, sql, params):
|
|
68
73
|
return await self.query(sql, params, QueryTypes.CREATE)
|
|
74
|
+
|
|
75
|
+
async def create_batch(self, sql, params):
|
|
76
|
+
return await self.query(sql, params, QueryTypes.CREATEBATCH)
|
|
@@ -354,6 +354,7 @@ class Field(FieldBase):
|
|
|
354
354
|
primary_key=False, # 主键
|
|
355
355
|
charset=None, # 编码
|
|
356
356
|
max_length=None, # 长度
|
|
357
|
+
scale_length=None, # 精度
|
|
357
358
|
auto_increment=False, # 自增
|
|
358
359
|
NOT_NULL=False, # 非空
|
|
359
360
|
created_generated=False, # 创建时for datetime
|
|
@@ -371,6 +372,7 @@ class Field(FieldBase):
|
|
|
371
372
|
self.charset = charset
|
|
372
373
|
self.default = default
|
|
373
374
|
self.max_length = max_length
|
|
375
|
+
self.scale_length = scale_length
|
|
374
376
|
self.auto_increment = auto_increment
|
|
375
377
|
self.NOT_NULL = NOT_NULL
|
|
376
378
|
self.created_generated = created_generated
|
|
@@ -3,9 +3,12 @@ from ..logging import print_warn
|
|
|
3
3
|
from functools import reduce
|
|
4
4
|
import operator
|
|
5
5
|
import copy
|
|
6
|
+
import re
|
|
6
7
|
import datetime
|
|
7
8
|
from ..utils.common import Object
|
|
8
9
|
|
|
10
|
+
__ALL__ = ("models", "Model")
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
class ModelMetaclass(type):
|
|
11
14
|
# def __init__(self, **kw):
|
|
@@ -127,6 +130,8 @@ class Model(metaclass=ModelMetaclass):
|
|
|
127
130
|
|
|
128
131
|
def _get_primary(self):
|
|
129
132
|
pk = self.__primary_key__
|
|
133
|
+
if pk is None:
|
|
134
|
+
return None, None
|
|
130
135
|
pkf = getattr(self, pk)
|
|
131
136
|
return pk, pkf.value
|
|
132
137
|
|
|
@@ -144,14 +149,14 @@ class Model(metaclass=ModelMetaclass):
|
|
|
144
149
|
@classmethod
|
|
145
150
|
def use(cls, pool=None):
|
|
146
151
|
cls.__pool__ = pool
|
|
152
|
+
cls.__ispg = cls.__pool__.driver == "postgres"
|
|
147
153
|
return cls()
|
|
148
154
|
|
|
149
155
|
def select(cls, *args):
|
|
150
156
|
cols = []
|
|
151
|
-
is_pg = cls.__pool__.driver == "postgres"
|
|
152
157
|
for col in args:
|
|
153
158
|
if isinstance(col, Field):
|
|
154
|
-
cols.append(col.name if
|
|
159
|
+
cols.append(col.name if cls.__ispg else col.full_name)
|
|
155
160
|
elif isinstance(col, Function):
|
|
156
161
|
q, p = col.sql()
|
|
157
162
|
cols.append(q)
|
|
@@ -190,12 +195,11 @@ class Model(metaclass=ModelMetaclass):
|
|
|
190
195
|
|
|
191
196
|
def order_by(self, *args):
|
|
192
197
|
by = []
|
|
193
|
-
is_pg = self.__pool__.driver == "postgres"
|
|
194
198
|
for f in args:
|
|
195
199
|
if isinstance(f, str):
|
|
196
200
|
by.append(f)
|
|
197
201
|
else:
|
|
198
|
-
by.append(f"{f.lhs.name if
|
|
202
|
+
by.append(f"{f.lhs.name if self.__ispg else f.lhs.full_name} {f.op}")
|
|
199
203
|
if self.__order_by__ is not None:
|
|
200
204
|
by = self.__order_by__ + by
|
|
201
205
|
self.__order_by__ = by
|
|
@@ -203,17 +207,15 @@ class Model(metaclass=ModelMetaclass):
|
|
|
203
207
|
|
|
204
208
|
def group_by(self, *args):
|
|
205
209
|
by = []
|
|
206
|
-
is_pg = self.__pool__.driver == "postgres"
|
|
207
210
|
for f in args:
|
|
208
|
-
by.append(f.name if
|
|
211
|
+
by.append(f.name if self.__ispg else f.full_name)
|
|
209
212
|
if self.__group_by__ is not None:
|
|
210
213
|
by = self.__group_by__ + by
|
|
211
214
|
self.__group_by__ = by
|
|
212
215
|
return self
|
|
213
216
|
|
|
214
|
-
def
|
|
217
|
+
def _format_data(self, action, args):
|
|
215
218
|
data = dict()
|
|
216
|
-
md = None
|
|
217
219
|
if args is None or not args:
|
|
218
220
|
for k in self.__fields__:
|
|
219
221
|
f = getattr(self, k)
|
|
@@ -223,13 +225,19 @@ class Model(metaclass=ModelMetaclass):
|
|
|
223
225
|
data = args
|
|
224
226
|
else:
|
|
225
227
|
for item in args:
|
|
226
|
-
md = item
|
|
227
228
|
if isinstance(item, Model):
|
|
228
229
|
for k in item.__dict__:
|
|
229
|
-
|
|
230
|
+
if (action == "u" and item[k].value) or action == "i":
|
|
231
|
+
data[k] = item[k]
|
|
232
|
+
|
|
230
233
|
else: # for object
|
|
231
234
|
for k, v in item.items():
|
|
232
235
|
data[k] = v
|
|
236
|
+
|
|
237
|
+
return data
|
|
238
|
+
|
|
239
|
+
def _get_update_key_args(self, action, args):
|
|
240
|
+
data = self._format_data(action, args)
|
|
233
241
|
keys = []
|
|
234
242
|
params = []
|
|
235
243
|
for k, v in data.items():
|
|
@@ -246,7 +254,48 @@ class Model(metaclass=ModelMetaclass):
|
|
|
246
254
|
elif v is not None: # fix sql format %s
|
|
247
255
|
keys.append("`%s`=?" % k)
|
|
248
256
|
params.append(v)
|
|
249
|
-
return ",".join(keys), params
|
|
257
|
+
return ",".join(keys), params
|
|
258
|
+
|
|
259
|
+
def _get_insert_key_args(self, action, args):
|
|
260
|
+
data = self._format_data(action, args)
|
|
261
|
+
keys = []
|
|
262
|
+
params = []
|
|
263
|
+
for k, v in data.items():
|
|
264
|
+
if isinstance(v, Field):
|
|
265
|
+
value = v.value
|
|
266
|
+
if value is None:
|
|
267
|
+
if v.created_generated == True or v.update_generated == True:
|
|
268
|
+
value = datetime.datetime.now()
|
|
269
|
+
else:
|
|
270
|
+
value = v.default
|
|
271
|
+
if value is not None:
|
|
272
|
+
keys.append(k)
|
|
273
|
+
params.append(value)
|
|
274
|
+
elif v is not None: # fix sql format %s
|
|
275
|
+
keys.append(k)
|
|
276
|
+
params.append(v)
|
|
277
|
+
return keys, params
|
|
278
|
+
|
|
279
|
+
def _get_batch_keys_values(self, items: list):
|
|
280
|
+
keys = []
|
|
281
|
+
values = []
|
|
282
|
+
item = items[0]
|
|
283
|
+
if isinstance(item, Model):
|
|
284
|
+
keys = item.__dict__.keys()
|
|
285
|
+
elif isinstance(item, dict):
|
|
286
|
+
keys = item.keys()
|
|
287
|
+
|
|
288
|
+
for item in items:
|
|
289
|
+
value = []
|
|
290
|
+
if isinstance(item, Model):
|
|
291
|
+
for k in item.__dict__:
|
|
292
|
+
value.append(item[k].value)
|
|
293
|
+
elif isinstance(item, dict): # for object
|
|
294
|
+
for k, v in item.items():
|
|
295
|
+
value.append(v)
|
|
296
|
+
value = tuple(key for key in value)
|
|
297
|
+
values.append(value)
|
|
298
|
+
return keys, values
|
|
250
299
|
|
|
251
300
|
def _merge_params(self, p):
|
|
252
301
|
if p is not None:
|
|
@@ -292,10 +341,11 @@ class Model(metaclass=ModelMetaclass):
|
|
|
292
341
|
async def one(self):
|
|
293
342
|
self.limit(1)
|
|
294
343
|
sql = self._sql()
|
|
344
|
+
sql = self._exchange_sql(sql)
|
|
295
345
|
args = self.__params__
|
|
296
346
|
rs = await self.__pool__.one(sql, args)
|
|
297
347
|
if rs:
|
|
298
|
-
|
|
348
|
+
# join 时返回dict
|
|
299
349
|
result = Object(rs) if self.__join__ is not None else self(**rs)
|
|
300
350
|
self._reset()
|
|
301
351
|
return result
|
|
@@ -306,10 +356,29 @@ class Model(metaclass=ModelMetaclass):
|
|
|
306
356
|
|
|
307
357
|
async def all(self):
|
|
308
358
|
sql = self._sql()
|
|
359
|
+
sql = self._exchange_sql(sql)
|
|
309
360
|
args = self.__params__
|
|
310
361
|
self._reset()
|
|
311
362
|
return await self.__pool__.all(sql, args)
|
|
312
363
|
|
|
364
|
+
def _exchange_sql(self, sql: str):
|
|
365
|
+
if self.__ispg:
|
|
366
|
+
sql = sql.replace("CURDATE()", "CURRENT_DATE")
|
|
367
|
+
sql = sql.replace("NOW()", "CURRENT_TIMESTAMP")
|
|
368
|
+
|
|
369
|
+
pattern = r"INTERVAL\s+(\d+)\s+(DAY|MONTH|YEAR|HOUR|MINUTE|SECOND)"
|
|
370
|
+
matches = re.findall(pattern, sql)
|
|
371
|
+
|
|
372
|
+
def replacement(match):
|
|
373
|
+
number = match.group(1)
|
|
374
|
+
unit = match.group(2).lower() # 转换为小写
|
|
375
|
+
return f"INTERVAL '{number} {unit}'"
|
|
376
|
+
|
|
377
|
+
if matches:
|
|
378
|
+
sql = re.sub(pattern, replacement, sql)
|
|
379
|
+
|
|
380
|
+
return sql
|
|
381
|
+
|
|
313
382
|
async def update(self, *args, **kw) -> bool:
|
|
314
383
|
"""
|
|
315
384
|
Update data
|
|
@@ -318,7 +387,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
318
387
|
# where = self.__where__
|
|
319
388
|
where = self._literal("WHERE", self.__where__)
|
|
320
389
|
|
|
321
|
-
keys, params
|
|
390
|
+
keys, params = self._get_update_key_args("u", args or kw)
|
|
322
391
|
|
|
323
392
|
sql = f"update `{table}` set {keys} {where}"
|
|
324
393
|
|
|
@@ -339,6 +408,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
339
408
|
else:
|
|
340
409
|
raise "Need where or primary key"
|
|
341
410
|
self._reset()
|
|
411
|
+
sql = self._exchange_sql(sql)
|
|
342
412
|
return await self.__pool__.update(sql, params)
|
|
343
413
|
|
|
344
414
|
async def delete(self) -> bool:
|
|
@@ -358,15 +428,26 @@ class Model(metaclass=ModelMetaclass):
|
|
|
358
428
|
else:
|
|
359
429
|
raise "need where or primary key"
|
|
360
430
|
self._reset()
|
|
431
|
+
sql = self._exchange_sql(sql)
|
|
361
432
|
return await self.__pool__.delete(sql, args)
|
|
362
433
|
|
|
363
434
|
async def insert(self, *args, **kw):
|
|
364
435
|
table = self.__table__
|
|
365
|
-
keys, params
|
|
366
|
-
sql = f"insert into `{table}`
|
|
436
|
+
keys, params = self._get_insert_key_args("i", args or kw)
|
|
437
|
+
sql = f"insert into `{table}` ({','.join(keys)}) values ({','.join(['?' for k in keys])})"
|
|
367
438
|
self._reset()
|
|
439
|
+
params = tuple(key for key in params)
|
|
368
440
|
return await self.__pool__.create(sql, params)
|
|
369
441
|
|
|
442
|
+
async def insert_batch(self, items: list):
|
|
443
|
+
table = self.__table__
|
|
444
|
+
if len(items) == 0:
|
|
445
|
+
return 0
|
|
446
|
+
keys, params = self._get_batch_keys_values(items)
|
|
447
|
+
sql = f"insert into `{table}` ({','.join(keys)}) values ({','.join(['?' for k in keys])})"
|
|
448
|
+
self._reset()
|
|
449
|
+
return await self.__pool__.create_batch(sql, params)
|
|
450
|
+
|
|
370
451
|
async def count(self):
|
|
371
452
|
__where__ = copy.copy(self.__where__)
|
|
372
453
|
__join__ = copy.copy(self.__join__)
|
|
@@ -376,6 +457,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
376
457
|
aft = " ".join([JOIN, WHERE])
|
|
377
458
|
sql = f"SELECT COUNT(*) FROM {cls.__table__} {aft}"
|
|
378
459
|
args = cls.__params__
|
|
460
|
+
sql = self._exchange_sql(sql)
|
|
379
461
|
return await cls.__pool__.count(sql, args)
|
|
380
462
|
|
|
381
463
|
|
|
@@ -512,6 +594,7 @@ class Models(object):
|
|
|
512
594
|
default=None,
|
|
513
595
|
not_null=False,
|
|
514
596
|
max_length=None,
|
|
597
|
+
scale_length=None,
|
|
515
598
|
unsigned=False,
|
|
516
599
|
comment=None,
|
|
517
600
|
):
|
|
@@ -521,10 +604,33 @@ class Models(object):
|
|
|
521
604
|
default,
|
|
522
605
|
NOT_NULL=not_null,
|
|
523
606
|
max_length=max_length,
|
|
607
|
+
scale_length=scale_length,
|
|
524
608
|
unsigned=unsigned,
|
|
525
609
|
comment=comment,
|
|
526
610
|
)
|
|
527
611
|
|
|
612
|
+
class NumericField(Field):
|
|
613
|
+
def __init__(
|
|
614
|
+
self,
|
|
615
|
+
name=None,
|
|
616
|
+
default=0.0,
|
|
617
|
+
not_null=False,
|
|
618
|
+
max_length=None,
|
|
619
|
+
scale_length=None,
|
|
620
|
+
unsigned=False,
|
|
621
|
+
comment=None,
|
|
622
|
+
):
|
|
623
|
+
super().__init__(
|
|
624
|
+
name,
|
|
625
|
+
"numeric",
|
|
626
|
+
default,
|
|
627
|
+
NOT_NULL=not_null,
|
|
628
|
+
unsigned=unsigned,
|
|
629
|
+
max_length=max_length,
|
|
630
|
+
scale_length=scale_length,
|
|
631
|
+
comment=comment,
|
|
632
|
+
)
|
|
633
|
+
|
|
528
634
|
class DecimalField(Field):
|
|
529
635
|
def __init__(
|
|
530
636
|
self,
|
|
@@ -532,6 +638,7 @@ class Models(object):
|
|
|
532
638
|
default=0.0,
|
|
533
639
|
not_null=False,
|
|
534
640
|
max_length=None,
|
|
641
|
+
scale_length=None,
|
|
535
642
|
unsigned=False,
|
|
536
643
|
comment=None,
|
|
537
644
|
):
|
|
@@ -542,6 +649,7 @@ class Models(object):
|
|
|
542
649
|
NOT_NULL=not_null,
|
|
543
650
|
unsigned=unsigned,
|
|
544
651
|
max_length=max_length,
|
|
652
|
+
scale_length=scale_length,
|
|
545
653
|
comment=comment,
|
|
546
654
|
)
|
|
547
655
|
|
|
@@ -5,7 +5,8 @@ __author__ = "chuchur/chuchur.com"
|
|
|
5
5
|
|
|
6
6
|
import hashlib, hmac, time, base64
|
|
7
7
|
from curl_cffi import requests as http
|
|
8
|
-
from ..logging import print_error,print_info
|
|
8
|
+
from ..logging import print_error, print_info
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
class Client(object):
|
|
11
12
|
def __init__(self, **kw):
|
|
@@ -6,12 +6,12 @@ import importlib
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def snake_to_camel(snake_str):
|
|
9
|
-
components = snake_str.split(
|
|
9
|
+
components = snake_str.split("_")
|
|
10
10
|
# camel_str = components[0] + ''.join(x.title() for x in components[1:])
|
|
11
11
|
# x.capitalize() //first
|
|
12
12
|
# x.title() //all
|
|
13
13
|
# return camel_str[0].upper() + camel_str[1:]
|
|
14
|
-
return
|
|
14
|
+
return "".join(x.title() for x in components[0:])
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
async def create_model(pool, table_name) -> str:
|
|
@@ -27,6 +27,7 @@ async def create_model(pool, table_name) -> str:
|
|
|
27
27
|
tb += f"\t__table__ = '{table_name}'\n\n"
|
|
28
28
|
for f in rows:
|
|
29
29
|
fields = get_col(f, pool.driver)
|
|
30
|
+
# print("fields",fields)
|
|
30
31
|
name = fields["name"]
|
|
31
32
|
column_type = fields["column_type"]
|
|
32
33
|
values = []
|
|
@@ -40,6 +41,8 @@ async def create_model(pool, table_name) -> str:
|
|
|
40
41
|
if isinstance(fields["max_length"], str) and "," in fields["max_length"]
|
|
41
42
|
else f"max_length={fields['max_length']}"
|
|
42
43
|
)
|
|
44
|
+
if fields["scale_length"]:
|
|
45
|
+
values.append(f"scale_length={fields['scale_length']}")
|
|
43
46
|
if fields["default"]:
|
|
44
47
|
values.append(f"default='{fields['default']}'")
|
|
45
48
|
if fields["auto_increment"]:
|
|
@@ -65,16 +68,28 @@ async def get_table_cols(pool, table_name):
|
|
|
65
68
|
if pool.driver == "mysql":
|
|
66
69
|
return await pool.all(f"show full COLUMNS from `{table_name}`", None)
|
|
67
70
|
elif pool.driver == "postgres":
|
|
71
|
+
pri_row = await pool.one(
|
|
72
|
+
f"SELECT column_name from information_schema.key_column_usage WHERE table_name = '{table_name}'",
|
|
73
|
+
None,
|
|
74
|
+
)
|
|
68
75
|
sql = f"""SELECT column_name Field,
|
|
69
76
|
column_default Default,
|
|
70
77
|
data_type column_type,
|
|
71
78
|
is_nullable Null,
|
|
72
79
|
numeric_precision num_length,
|
|
80
|
+
numeric_scale scale_length,
|
|
73
81
|
character_maximum_length str_length,
|
|
74
82
|
datetime_precision date_length,
|
|
75
83
|
col_description('{table_name}'::regclass, ordinal_position) Comment
|
|
76
84
|
FROM information_schema.columns WHERE table_name ='{table_name}'"""
|
|
77
|
-
|
|
85
|
+
|
|
86
|
+
rows = await pool.all(sql, None)
|
|
87
|
+
for row in rows:
|
|
88
|
+
row["Key"] = "PRI" if pri_row and pri_row["column_name"] == row["field"] else None
|
|
89
|
+
|
|
90
|
+
# print("rows", rows)
|
|
91
|
+
|
|
92
|
+
return rows
|
|
78
93
|
else:
|
|
79
94
|
raise ValueError("Database not support.")
|
|
80
95
|
|
|
@@ -91,7 +106,6 @@ async def get_all_tables(pool):
|
|
|
91
106
|
raise ValueError("Database not support.")
|
|
92
107
|
|
|
93
108
|
|
|
94
|
-
|
|
95
109
|
async def create_models(pool, save_path: str = None, tables: list = None):
|
|
96
110
|
"""
|
|
97
111
|
Create models
|
|
@@ -201,16 +215,17 @@ def get_col(field, driver="mysql"):
|
|
|
201
215
|
fields["column_type"] = t.groups()[0]
|
|
202
216
|
fields["max_length"] = t.groups()[1]
|
|
203
217
|
return fields
|
|
204
|
-
elif driver == "postgres":
|
|
218
|
+
elif driver == "postgres" or driver == "postgresql":
|
|
205
219
|
fields = {
|
|
206
220
|
"name": field["field"],
|
|
207
221
|
"column_type": field["column_type"].replace(" ", "_"),
|
|
208
|
-
"primary_key":
|
|
222
|
+
"primary_key": field["Key"] == "PRI",
|
|
209
223
|
"default": field["default"],
|
|
210
224
|
"charset": None,
|
|
211
225
|
"max_length": field["num_length"]
|
|
212
226
|
or field["str_length"]
|
|
213
227
|
or field["date_length"],
|
|
228
|
+
"scale_length": field["scale_length"],
|
|
214
229
|
"auto_increment": None,
|
|
215
230
|
"NOT_NULL": field["null"] == "NO",
|
|
216
231
|
"created_generated": None,
|
|
@@ -224,11 +239,12 @@ def get_col_sql(field):
|
|
|
224
239
|
sql = f"`{field.name}` {field.column_type}"
|
|
225
240
|
|
|
226
241
|
if field.max_length:
|
|
227
|
-
|
|
228
|
-
f"{field.max_length}"
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
242
|
+
if isinstance(field.max_length, tuple):
|
|
243
|
+
sql += f"{field.max_length}"
|
|
244
|
+
elif field.scale_length:
|
|
245
|
+
sql += f"({field.max_length},{field.scale_length})"
|
|
246
|
+
else:
|
|
247
|
+
sql += f"({field.max_length})"
|
|
232
248
|
|
|
233
249
|
if field.charset:
|
|
234
250
|
# _ci 不区分大小写 _cs Yes
|
|
@@ -237,7 +253,8 @@ def get_col_sql(field):
|
|
|
237
253
|
if field.primary_key:
|
|
238
254
|
sql += " PRIMARY KEY"
|
|
239
255
|
if field.auto_increment:
|
|
240
|
-
|
|
256
|
+
# todo: postgres SERIAL
|
|
257
|
+
sql += " AUTO_INCREMENT"
|
|
241
258
|
if field.NOT_NULL:
|
|
242
259
|
sql += " NOT NULL"
|
|
243
260
|
if field.default:
|
|
@@ -279,14 +296,15 @@ class ColTypes(enum.Enum):
|
|
|
279
296
|
float = "Float"
|
|
280
297
|
double = "Double"
|
|
281
298
|
decimal = "Decimal"
|
|
299
|
+
numeric = "Numeric"
|
|
282
300
|
date = "Date"
|
|
283
301
|
datetime = "Datetime"
|
|
284
302
|
timestamp = "Timestamp"
|
|
285
303
|
json = "Json"
|
|
286
304
|
# pg
|
|
287
305
|
timestamp_with_time_zone = "Timestamp"
|
|
306
|
+
timestamp_without_time_zone = "Timestamp"
|
|
288
307
|
character_varying = "VarChar"
|
|
308
|
+
character = "Char"
|
|
289
309
|
integer = "Integer"
|
|
290
310
|
boolean = "Boolean"
|
|
291
|
-
|
|
292
|
-
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
|
|
2
|
+
import time
|
|
3
|
+
import threading
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
雪花算法(全局唯一id)
|
|
8
|
+
|
|
9
|
+
# 最早是Twitter公司在其内部用于分布式环境下生成唯一ID
|
|
10
|
+
|
|
11
|
+
# Twitter's Snowflake algorithm implementation which is used to generate distributed IDs.
|
|
12
|
+
|
|
13
|
+
# https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class IdWorker:
|
|
18
|
+
def __init__(self, worker_id, datacenter_id, sequence=0):
|
|
19
|
+
# 位移值
|
|
20
|
+
self.worker_id_bits = 5
|
|
21
|
+
self.datacenter_id_bits = 5
|
|
22
|
+
self.sequence_bits = 12
|
|
23
|
+
|
|
24
|
+
self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
|
|
25
|
+
self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
|
|
26
|
+
|
|
27
|
+
# 参数校验
|
|
28
|
+
if worker_id > self.max_worker_id or worker_id < 0:
|
|
29
|
+
raise ValueError(f'worker_id must be between 0 and {self.max_worker_id}')
|
|
30
|
+
if datacenter_id > self.max_datacenter_id or datacenter_id < 0:
|
|
31
|
+
raise ValueError(f'datacenter_id must be between 0 and {self.max_datacenter_id}')
|
|
32
|
+
|
|
33
|
+
self.worker_id = worker_id
|
|
34
|
+
self.datacenter_id = datacenter_id
|
|
35
|
+
self.sequence = sequence
|
|
36
|
+
self.lock = threading.Lock()
|
|
37
|
+
|
|
38
|
+
# Twitter Epoch (2010-11-04 01:42:54 UTC)
|
|
39
|
+
self.twepoch = 1288834974657
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
self.worker_id_shift = self.sequence_bits
|
|
45
|
+
self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
|
|
46
|
+
self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
|
|
47
|
+
self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
|
|
48
|
+
|
|
49
|
+
self.last_timestamp = -1
|
|
50
|
+
|
|
51
|
+
def _time_gen(self):
|
|
52
|
+
return int(time.time() * 1000)
|
|
53
|
+
|
|
54
|
+
def _til_next_millis(self, last_timestamp):
|
|
55
|
+
timestamp = self._time_gen()
|
|
56
|
+
while timestamp <= last_timestamp:
|
|
57
|
+
timestamp = self._time_gen()
|
|
58
|
+
return timestamp
|
|
59
|
+
|
|
60
|
+
def next_id(self):
|
|
61
|
+
with self.lock:
|
|
62
|
+
timestamp = self._time_gen()
|
|
63
|
+
|
|
64
|
+
if timestamp < self.last_timestamp:
|
|
65
|
+
raise Exception(f'Clock moved backwards. Refusing to generate id for {self.last_timestamp - timestamp} milliseconds')
|
|
66
|
+
|
|
67
|
+
if self.last_timestamp == timestamp:
|
|
68
|
+
self.sequence = (self.sequence + 1) & self.sequence_mask
|
|
69
|
+
if self.sequence == 0:
|
|
70
|
+
timestamp = self._til_next_millis(self.last_timestamp)
|
|
71
|
+
else:
|
|
72
|
+
self.sequence = 0
|
|
73
|
+
|
|
74
|
+
self.last_timestamp = timestamp
|
|
75
|
+
|
|
76
|
+
id = ((timestamp - self.twepoch) << self.timestamp_left_shift) | \
|
|
77
|
+
(self.datacenter_id << self.datacenter_id_shift) | \
|
|
78
|
+
(self.worker_id << self.worker_id_shift) | \
|
|
79
|
+
self.sequence
|
|
80
|
+
|
|
81
|
+
return id
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# 示例使用
|
|
85
|
+
worker = IdWorker(worker_id=1, datacenter_id=1)
|
|
86
|
+
print(worker.next_id())
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.2.20"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|