Files
aaPanel/class/public/aaModel/fields.py
T
Jack 8eadc94c98 Update to 7.32.0
1. Added: Mail Server - Add Domain with Domain management center to Automatic DNS record & apply SSL
2. Added: Mail Server - DNS record with One-click Parsing
3. Added: Mail Server - Compatible with Domain management center to apply for renewal SSL
4. Added: Mail Server - Mail Marketing - Add Template supports Upload HTML
5. Added: bt default command supports displaying separate IPV4, IPV6 and bound domain info
6. Added: Settings - Panel SSL supports using Domains SSL Certificate
7. Added: PHP Project - Automatic Add Record & SSL with progress display
8. Fixed: Mail Server quota limit issue
9. Fixed: WP Toolkit incorrect cache directory issue
10. Optimized: Enhanced domain validation in the Domain module
11. Fixed: Issue with automatic domain resolution prompts in Mail Server editing
12. Fixed: "Internal Server Error" occurring in certain scenarios
2025-04-15 17:11:22 +08:00

379 lines
9.5 KiB
Python

# coding: utf-8
import itertools
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] = (
"contains",
"any_contains",
)
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
COMPARE = tuple(set(
itertools.chain(
*[
StrField.compare,
IntField.compare,
FloatField.compare,
ListField.compare,
DictField.compare,
DateTimeStrField.compare,
]
)
))