cloudoll 2.2.20__tar.gz → 2.2.23__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.
Files changed (43) hide show
  1. {cloudoll-2.2.20 → cloudoll-2.2.23}/PKG-INFO +1 -1
  2. cloudoll-2.2.23/cloudoll/__init__.py +1 -0
  3. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/orm/__init__.py +9 -5
  4. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/orm/field.py +2 -0
  5. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/orm/model.py +65 -11
  6. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/utils/m2d.py +30 -13
  7. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll.egg-info/PKG-INFO +1 -1
  8. cloudoll-2.2.20/cloudoll/__init__.py +0 -1
  9. {cloudoll-2.2.20 → cloudoll-2.2.23}/LICENSE +0 -0
  10. {cloudoll-2.2.20 → cloudoll-2.2.23}/README.md +0 -0
  11. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/__main__.py +0 -0
  12. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/cli.py +0 -0
  13. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/error/__init__.py +0 -0
  14. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/error/errors.py +0 -0
  15. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/logging/__init__.py +0 -0
  16. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/mail/__init__.py +0 -0
  17. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/mail/smtp.py +0 -0
  18. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/orm/base.py +0 -0
  19. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/orm/err.py +0 -0
  20. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/orm/mysql.py +0 -0
  21. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/orm/parse.py +0 -0
  22. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/orm/postgres.py +0 -0
  23. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/robot/__init__.py +0 -0
  24. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/robot/dingtalk.py +0 -0
  25. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/robot/feishu.py +0 -0
  26. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/robot/slack.py +0 -0
  27. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/utils/__init__.py +0 -0
  28. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/utils/common.py +0 -0
  29. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/utils/patch_loop.py +0 -0
  30. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/utils/watch.py +0 -0
  31. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/web/__init__.py +0 -0
  32. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/web/exception.py +0 -0
  33. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/web/html.py +0 -0
  34. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/web/http.py +0 -0
  35. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/web/jwt.py +0 -0
  36. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll/web/settings.py +0 -0
  37. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll.egg-info/SOURCES.txt +0 -0
  38. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll.egg-info/dependency_links.txt +0 -0
  39. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll.egg-info/entry_points.txt +0 -0
  40. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll.egg-info/requires.txt +0 -0
  41. {cloudoll-2.2.20 → cloudoll-2.2.23}/cloudoll.egg-info/top_level.txt +0 -0
  42. {cloudoll-2.2.20 → cloudoll-2.2.23}/setup.cfg +0 -0
  43. {cloudoll-2.2.20 → cloudoll-2.2.23}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudoll
3
- Version: 2.2.20
3
+ Version: 2.2.23
4
4
  Summary: 辅助快速创建可分布的微服务。
5
5
  Home-page: https://gitee.com/chuchur/cloudoll-py
6
6
  Author: chuchur
@@ -0,0 +1 @@
1
+ __version__ = "2.2.23"
@@ -26,7 +26,7 @@ async def create_engine(**kw):
26
26
 
27
27
  if driver == "mysql":
28
28
  return await Mysql().create_engine(**configs, **query)
29
- elif driver == "postgres":
29
+ elif driver == "postgres" or driver == "postgressql":
30
30
  return await Postgres().create_engine(**configs, **query)
31
31
  elif driver == "redis" or driver == "rediss":
32
32
  """
@@ -62,26 +62,30 @@ class Postgres(MeteBase):
62
62
 
63
63
  # if query_type.value > 4:
64
64
  # if self.driver == "mysql":
65
+ # print(columns)
65
66
  # await conn.commit()
66
67
  # elif self.driver == "postgres":
67
- columns = [desc[0] for desc in cursor.description]
68
68
  result = None
69
69
 
70
70
  if query_type == QueryTypes.ALL:
71
+ columns = [desc[0] for desc in cursor.description]
71
72
  rows = await cursor.fetchall()
72
73
  result = [dict(zip(columns, row)) for row in rows]
73
74
  return result
74
75
  elif query_type == QueryTypes.ONE:
75
- rows = await cursor.fetchone()
76
- result = dict(zip(columns, rows))
76
+ columns = [desc[0] for desc in cursor.description]
77
+ row = await cursor.fetchone()
78
+ # print("rows_reuslt", rows)
79
+ result = dict(zip(columns, row)) if row else {}
77
80
  return result
78
81
  elif query_type == QueryTypes.MANY:
82
+ columns = [desc[0] for desc in cursor.description]
79
83
  rows = await cursor.fetchmany(size)
80
84
  result = [dict(zip(columns, row)) for row in rows]
81
85
  return result
82
86
  elif query_type == QueryTypes.COUNT:
83
87
  rs = await cursor.fetchone()
84
- return rs[0]
88
+ return rs[0] if row else 0
85
89
  elif query_type == QueryTypes.CREATE:
86
90
  result = cursor.rowcount > 0
87
91
  id = cursor.lastrowid
@@ -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,6 +3,7 @@ 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
 
@@ -127,6 +128,8 @@ class Model(metaclass=ModelMetaclass):
127
128
 
128
129
  def _get_primary(self):
129
130
  pk = self.__primary_key__
131
+ if pk is None:
132
+ return None, None
130
133
  pkf = getattr(self, pk)
131
134
  return pk, pkf.value
132
135
 
@@ -144,14 +147,14 @@ class Model(metaclass=ModelMetaclass):
144
147
  @classmethod
145
148
  def use(cls, pool=None):
146
149
  cls.__pool__ = pool
150
+ cls.__ispg = cls.__pool__.driver == "postgres"
147
151
  return cls()
148
152
 
149
153
  def select(cls, *args):
150
154
  cols = []
151
- is_pg = cls.__pool__.driver == "postgres"
152
155
  for col in args:
153
156
  if isinstance(col, Field):
154
- cols.append(col.name if is_pg else col.full_name)
157
+ cols.append(col.name if cls.__ispg else col.full_name)
155
158
  elif isinstance(col, Function):
156
159
  q, p = col.sql()
157
160
  cols.append(q)
@@ -190,12 +193,13 @@ class Model(metaclass=ModelMetaclass):
190
193
 
191
194
  def order_by(self, *args):
192
195
  by = []
193
- is_pg = self.__pool__.driver == "postgres"
194
196
  for f in args:
195
197
  if isinstance(f, str):
196
198
  by.append(f)
197
199
  else:
198
- by.append(f"{f.lhs.name if is_pg else f.lhs.full_name} {f.op}")
200
+ by.append(
201
+ f"{f.lhs.name if self.__ispg else f.lhs.full_name} {f.op}"
202
+ )
199
203
  if self.__order_by__ is not None:
200
204
  by = self.__order_by__ + by
201
205
  self.__order_by__ = by
@@ -203,15 +207,14 @@ 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 is_pg else f.full_name)
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 _get_key_args(self, args):
217
+ def _get_key_args(self, action, args):
215
218
  data = dict()
216
219
  md = None
217
220
  if args is None or not args:
@@ -226,7 +229,9 @@ class Model(metaclass=ModelMetaclass):
226
229
  md = item
227
230
  if isinstance(item, Model):
228
231
  for k in item.__dict__:
229
- data[k] = item[k]
232
+ if (action == 'u' and item[k].value) or action == 'i':
233
+ data[k] = item[k]
234
+
230
235
  else: # for object
231
236
  for k, v in item.items():
232
237
  data[k] = v
@@ -292,10 +297,11 @@ class Model(metaclass=ModelMetaclass):
292
297
  async def one(self):
293
298
  self.limit(1)
294
299
  sql = self._sql()
300
+ sql = self._exchange_sql(sql)
295
301
  args = self.__params__
296
302
  rs = await self.__pool__.one(sql, args)
297
303
  if rs:
298
- ## join 时返回dict
304
+ # join 时返回dict
299
305
  result = Object(rs) if self.__join__ is not None else self(**rs)
300
306
  self._reset()
301
307
  return result
@@ -306,10 +312,29 @@ class Model(metaclass=ModelMetaclass):
306
312
 
307
313
  async def all(self):
308
314
  sql = self._sql()
315
+ sql = self._exchange_sql(sql)
309
316
  args = self.__params__
310
317
  self._reset()
311
318
  return await self.__pool__.all(sql, args)
312
319
 
320
+ def _exchange_sql(self, sql: str):
321
+ if self.__ispg:
322
+ sql = sql.replace("CURDATE()", "CURRENT_DATE")
323
+ sql = sql.replace("NOW()", "CURRENT_TIMESTAMP")
324
+
325
+ pattern = r"INTERVAL\s+(\d+)\s+(DAY|MONTH|YEAR|HOUR|MINUTE|SECOND)"
326
+ matches = re.findall(pattern, sql)
327
+
328
+ def replacement(match):
329
+ number = match.group(1)
330
+ unit = match.group(2).lower() # 转换为小写
331
+ return f"INTERVAL '{number} {unit}'"
332
+
333
+ if matches:
334
+ sql = re.sub(pattern, replacement, sql)
335
+
336
+ return sql
337
+
313
338
  async def update(self, *args, **kw) -> bool:
314
339
  """
315
340
  Update data
@@ -318,7 +343,7 @@ class Model(metaclass=ModelMetaclass):
318
343
  # where = self.__where__
319
344
  where = self._literal("WHERE", self.__where__)
320
345
 
321
- keys, params, md = self._get_key_args(args or kw)
346
+ keys, params, md = self._get_key_args('u', args or kw)
322
347
 
323
348
  sql = f"update `{table}` set {keys} {where}"
324
349
 
@@ -339,6 +364,7 @@ class Model(metaclass=ModelMetaclass):
339
364
  else:
340
365
  raise "Need where or primary key"
341
366
  self._reset()
367
+ sql = self._exchange_sql(sql)
342
368
  return await self.__pool__.update(sql, params)
343
369
 
344
370
  async def delete(self) -> bool:
@@ -358,11 +384,12 @@ class Model(metaclass=ModelMetaclass):
358
384
  else:
359
385
  raise "need where or primary key"
360
386
  self._reset()
387
+ sql = self._exchange_sql(sql)
361
388
  return await self.__pool__.delete(sql, args)
362
389
 
363
390
  async def insert(self, *args, **kw):
364
391
  table = self.__table__
365
- keys, params, md = self._get_key_args(args or kw)
392
+ keys, params, md = self._get_key_args('i', args or kw)
366
393
  sql = f"insert into `{table}` set {keys}"
367
394
  self._reset()
368
395
  return await self.__pool__.create(sql, params)
@@ -376,6 +403,7 @@ class Model(metaclass=ModelMetaclass):
376
403
  aft = " ".join([JOIN, WHERE])
377
404
  sql = f"SELECT COUNT(*) FROM {cls.__table__} {aft}"
378
405
  args = cls.__params__
406
+ sql = self._exchange_sql(sql)
379
407
  return await cls.__pool__.count(sql, args)
380
408
 
381
409
 
@@ -512,6 +540,7 @@ class Models(object):
512
540
  default=None,
513
541
  not_null=False,
514
542
  max_length=None,
543
+ scale_length=None,
515
544
  unsigned=False,
516
545
  comment=None,
517
546
  ):
@@ -521,7 +550,30 @@ class Models(object):
521
550
  default,
522
551
  NOT_NULL=not_null,
523
552
  max_length=max_length,
553
+ scale_length=scale_length,
554
+ unsigned=unsigned,
555
+ comment=comment,
556
+ )
557
+
558
+ class NumericField(Field):
559
+ def __init__(
560
+ self,
561
+ name=None,
562
+ default=0.0,
563
+ not_null=False,
564
+ max_length=None,
565
+ scale_length=None,
566
+ unsigned=False,
567
+ comment=None,
568
+ ):
569
+ super().__init__(
570
+ name,
571
+ "numeric",
572
+ default,
573
+ NOT_NULL=not_null,
524
574
  unsigned=unsigned,
575
+ max_length=max_length,
576
+ scale_length=scale_length,
525
577
  comment=comment,
526
578
  )
527
579
 
@@ -532,6 +584,7 @@ class Models(object):
532
584
  default=0.0,
533
585
  not_null=False,
534
586
  max_length=None,
587
+ scale_length=None,
535
588
  unsigned=False,
536
589
  comment=None,
537
590
  ):
@@ -542,6 +595,7 @@ class Models(object):
542
595
  NOT_NULL=not_null,
543
596
  unsigned=unsigned,
544
597
  max_length=max_length,
598
+ scale_length=scale_length,
545
599
  comment=comment,
546
600
  )
547
601
 
@@ -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 ''.join(x.title() for x in components[0:])
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
- return await pool.all(sql, None)
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": False,
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
- sql += (
228
- f"{field.max_length}"
229
- if isinstance(field.max_length, tuple)
230
- else f"({field.max_length})"
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
@@ -279,14 +295,15 @@ class ColTypes(enum.Enum):
279
295
  float = "Float"
280
296
  double = "Double"
281
297
  decimal = "Decimal"
298
+ numeric = "Numeric"
282
299
  date = "Date"
283
300
  datetime = "Datetime"
284
301
  timestamp = "Timestamp"
285
302
  json = "Json"
286
303
  # pg
287
304
  timestamp_with_time_zone = "Timestamp"
305
+ timestamp_without_time_zone = "Timestamp"
288
306
  character_varying = "VarChar"
307
+ character = "Char"
289
308
  integer = "Integer"
290
309
  boolean = "Boolean"
291
-
292
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudoll
3
- Version: 2.2.20
3
+ Version: 2.2.23
4
4
  Summary: 辅助快速创建可分布的微服务。
5
5
  Home-page: https://gitee.com/chuchur/cloudoll-py
6
6
  Author: chuchur
@@ -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