Files
aaPanel/class/public/aaModel/fields.py
T
Jack 6f212ce3d6 Update to 7.27.0
1. Added Domains menu to manage Domains and SSL Certificates
2. Added Add Site automatic record creation when adding websites (Only supported by IPv4)
3. Added Vietnamese language
4. Added Indonesian language
5. Added HTTPS Protection function for Website (disables automatic HTTP to HTTPS redirection when enabled)
6. Added Mail Marketing -- Automation trigger tasks
7. Added Mail Marketing -- Groups Import, Export, Merge
8. Added Mail Marketing -- Subscribers to support paste import
9. Added Mail Marketing -- Template Import Export Duplicate
10. Added Mail server -- Other Settings add timed Auto Responder
11. Added Mail Marketing -- Suspend List to detect abnormal mailboxes
12. Added Mail Marketing -- Marketing Task to export error mail logs
13. Added Mail Domain -- SSL certificate expiration alert
14. Optimized Webmail management to display based on domain additions
15. Optimize Website Interface Button Integration
16. Optimize the response speed of WP Toolkit interface
2025-03-26 09:04:29 +08:00

363 lines
9.2 KiB
Python

# coding: utf-8
import json
import time
from collections.abc import Callable
from dataclasses import dataclass, field as dataclass_field
from datetime import datetime
from typing import Any, TypeVar, List, Optional
from public.exceptions import HintException
__all__ = [
"StrField",
"IntField",
"FloatField",
"BlobField",
"BoolField",
"ListField",
"DictField",
"DateTimeStrField",
]
M = TypeVar("M", bound="aaModel")
def json_func(v_type: type, value: Any, forward: bool = True):
try:
if forward is True:
if isinstance(value, v_type):
return json.dumps(value)
else:
if isinstance(value, str):
return json.loads(value)
return value
except TypeError as t:
print("type error %s" % t)
return value
except Exception as e:
print("error %s" % e)
raise e
@dataclass
class aaField(object):
"""
字段基类
default 默认值
ps 字段说明
null 是否null
primary_key 是否主键
foreign_key 外键
field_name 字段key
field_type sql类型
py_type py类型
compare 比较
transform 转换工具
"""
default: Any = None
ps: str = None
null: bool = False
primary_key: bool = False
field_name: str = None
field_type: str = None
py_type: type = None
compare: tuple = None
serialized: Callable = None
# todo
# require: bool = False
# foreign_key: str = None
def __set_name__(self, owner: M, name: str):
self.__model = owner
self.field_name = str(name)
def __get__(self, instance: object, owner):
if instance is None:
return self
try:
return instance.__dict__[self.field_name]
except KeyError:
raise AttributeError(f'{self.field_name} is not set')
def __set__(self, instance, value):
instance.__dict__[self.field_name] = value
def __delete__(self, instance):
try:
del instance.__dict__[self.field_name]
except KeyError:
raise AttributeError(f"{instance} dont have attr '{self.field_name}'")
def _raise_error(self, raise_exp: bool = True) -> bool:
if raise_exp is True:
err = f"'{self.field_name}' TypeError! It should be '{self.py_type.__name__}'"
raise HintException(err)
else:
return False
def _check_type(self, target: Any, raise_exp=True) -> bool:
target = target if not isinstance(target, Callable) else target()
flag = True if any([isinstance(target, x) for x in self.py_types]) else False
return True if flag is True else self._raise_error(raise_exp=raise_exp)
@property
def default_val_sql(self) -> str:
default_v = self.get_default_val(check=True)
default_v = default_v if self.serialized is None else self.serialized(default_v)
return f"DEFAULT '{default_v}'" if isinstance(default_v, str) else f"DEFAULT {default_v}"
@property
def py_types(self) -> List[type]:
if self.null is True:
original = [type(self.default), self.py_type, type(None)]
else:
original = [type(self.default), self.py_type]
return list(set(original))
def get_default_val(self, check: bool = False) -> Optional[Any]:
if check:
self.check_org_type(raise_exp=check)
if self.default is not None:
return self.default if not isinstance(self.default, Callable) else self.default()
else:
if self.null is True:
return None
else:
raise TypeError(
f"\n1: field '{self.field_name}' is not null, must have a default value"
f"\n2: you can add the '{self.field_name}' field's params null=True"
)
def model_check_type(self, target: Any, raise_exp=True) -> bool:
"""
检查模型当前类型结构
"""
return self._check_type(target, raise_exp=raise_exp)
def check_org_type(self, raise_exp: bool = True) -> bool:
"""
检查初始化的类型结构
"""
return self._check_type(self.default, raise_exp=raise_exp)
@dataclass()
class StrField(aaField):
"""
String field
field__like="a",
field__ne="a",
field__in=["a", "b", "c"]
field__not_in=["a", "b", "c"]
field__startswith="a"
field__endswith="a"
"""
default: str | None = ""
field_type: str = "TEXT"
py_type: type = str
max_length: int = 255 # not limit now
min_length: int = 0 # not limit now
compare: tuple[str] = (
"like",
"ne",
"in",
"not_in",
"startswith",
"endswith",
)
@dataclass
class IntField(aaField):
"""
Int field
field__gt=1,
field__gte=1,
field__lt=1,
field__lte=1,
field__ne=1,
field__in=[1, 2, 3]
field__not_in=[1, 2, 3]
"""
default: int | None | Any = 0
field_type: str = "INTEGER"
py_type: type = int
max: int = 0 # not limit now
min: int = 0 # not limit now
compare: tuple[str] = (
"gt",
"lt",
"gte",
"lte",
"ne",
"in",
"not_in",
)
@dataclass
class FloatField(aaField):
"""
Float field
"""
default: float | None = 0.0
field_type: str = "REAL"
py_type: type = float
max: float = 0.0 # not limit now
min: float = 0.0 # not limit now
compare: tuple[str] = (
"gt",
"lt",
"gte",
"lte",
"ne",
"in",
"not_in",
)
@dataclass
class BlobField(aaField):
"""
Blob field
"""
default: bytes | None = b''
field_type: str = "BLOB"
py_type: type = bytes
@dataclass
class BoolField(aaField):
"""
Bool field
"""
@staticmethod
def _serialized(value: bool | int, forward: bool = True) -> bool | int:
if forward is True:
if value is True:
return 1
elif value is False:
return 0
else:
if value == 1:
return True
elif value == 0:
return False
return value
default: bool = True
field_type: str = "INTEGER"
serialized: Callable = _serialized
py_type: type = bool
@dataclass
class ListField(aaField):
"""
List field
"""
@staticmethod
def _serialized(value: list | str, forward: bool = True) -> list | Any:
return json_func(list, value, forward)
default: list = dataclass_field(default_factory=list)
field_type: str = "TEXT"
serialized: Callable = _serialized
py_type: type = list
compare: tuple[str] = (
"has_element",
)
update: tuple[str] = (
"append",
)
@dataclass
class DictField(aaField):
"""
Dict field
"""
@staticmethod
def _serialized(value: dict | str, forward: bool = True) -> dict | Any:
return json_func(dict, value, forward)
default: dict = dataclass_field(default_factory=dict)
field_type: str = "TEXT"
serialized: Callable = _serialized
py_type: type = dict
compare: tuple[str] = (
"has_key",
"has_value",
# "has_key_value",
)
update: tuple[str] = (
"update",
)
@dataclass
class DateTimeStrField(aaField):
"""
时间戳
auto_now_add=True 创建时间自动添加
auto_now=True 更新时间自动更新
"""
@classmethod
def _current_timestamp(cls):
return time.strftime(cls.format, time.localtime())
@staticmethod
def _dynamic(obj, val):
if hasattr(obj, "auto_now_add") and obj.auto_now_add is True: # 创建时间
return val
elif hasattr(obj, "auto_now") and obj.auto_now is True: # 更新时间
return obj.get_default_val()
else:
return val
@staticmethod
def _serialized(value: int | str, forward: bool = True) -> str | int:
try:
if forward is True:
if isinstance(value, str): # save will be str
return int(
datetime.strptime(value, DateTimeStrField.format).timestamp() * DateTimeStrField.accuracy)
else:
if isinstance(value, int):
return datetime.fromtimestamp(value / DateTimeStrField.accuracy).strftime(DateTimeStrField.format)
return value
except Exception as e:
print("type error %s" % e)
return value
serialized: Callable = _serialized
default: str | Callable = ""
field_type: str = "INTEGER"
py_type: type = str
dynamic: bool = True
auto_now_add: bool = False
auto_now: bool = False
accuracy: int = 1000
format: str = "%Y-%m-%d %H:%M:%S"
compare: tuple[str] = (
"gt",
"lt",
"gte",
"lte",
)
def __post_init__(self):
if self.auto_now_add is True and self.auto_now is True:
raise TypeError("auto_now_add and auto_now can not be used at the same time")
if self.auto_now is True:
self.default = self._current_timestamp
elif self.auto_now_add is True:
self.default = self._current_timestamp