xn-model 1.0.12__tar.gz → 1.0.14__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xn-model
3
- Version: 1.0.12
3
+ Version: 1.0.14
4
4
  Summary: Base model for xn-api
5
5
  Author-email: Mike Artemiev <mixartemev@gmail.com>
6
6
  License: MIT
@@ -10,6 +10,7 @@ Keywords: tortoise,model,crud,generator,api,admin
10
10
  Requires-Python: >=3.11
11
11
  Description-Content-Type: text/markdown
12
12
  Requires-Dist: tortoise-orm[asyncpg]
13
+ Requires-Dist: pydantic
13
14
  Provides-Extra: dev
14
15
  Requires-Dist: pytest; extra == "dev"
15
16
  Requires-Dist: python-dotenv; extra == "dev"
@@ -4,7 +4,7 @@ requires-python = ">=3.11"
4
4
  authors = [
5
5
  {name = "Mike Artemiev", email = "mixartemev@gmail.com"},
6
6
  ]
7
- dependencies = ['tortoise-orm[asyncpg]']
7
+ dependencies = ['tortoise-orm[asyncpg]', 'pydantic']
8
8
  keywords = ["tortoise", "model", "crud", "generator", "api", "admin"]
9
9
  description = 'Base model for xn-api'
10
10
  readme = "README.md"
@@ -0,0 +1,90 @@
1
+ from dataclasses import make_dataclass, field, fields
2
+ from datetime import datetime
3
+ from typing import Self
4
+
5
+ from pydantic import ConfigDict
6
+ from tortoise import Model as TortModel
7
+ from tortoise.fields import IntField
8
+
9
+ from x_model.field import DatetimeSecField
10
+ from x_model.types import BaseUpd
11
+
12
+
13
+ class TsTrait:
14
+ created_at: datetime | None = DatetimeSecField(auto_now_add=True)
15
+ updated_at: datetime | None = DatetimeSecField(auto_now=True)
16
+
17
+
18
+ class Model(TortModel):
19
+ id: int = IntField(True)
20
+
21
+ _in_type: type[BaseUpd] = None # overridable
22
+ _name: tuple[str] = ("name",)
23
+ _sorts: tuple[str] = ("-id",)
24
+
25
+ def __repr__(self, sep: str = " ") -> str:
26
+ return sep.join(getattr(self, name_fragment) for name_fragment in self._name)
27
+
28
+ # @classmethod
29
+ # def out_type(cls) -> type[BaseModel]:
30
+ # if not cls._out_type:
31
+ # cls._out_type = pydantic_model_creator(cls, name=cls.__name__ + "Out")
32
+ # return cls._out_type
33
+
34
+ @classmethod
35
+ def in_type(cls, with_pk: bool = False) -> type[BaseUpd]:
36
+ if not getattr(cls, cn := "Upd" if with_pk else "New", None):
37
+ fields: list[tuple[str, type] | tuple[str, type, field]] = []
38
+ for fn in cls._meta.db_fields:
39
+ if (f := cls._meta.fields_map[fn]).pk and not with_pk:
40
+ continue
41
+ if getattr(f, "auto_now", None) or getattr(f, "auto_now_add", None):
42
+ continue
43
+ fld = fn, getattr(f, "enum_type", f.field_type)
44
+ if f.default or f.null or (f.allows_generated and not f.pk) or not f.required:
45
+ fld += (field(default_factory=dict) if f.default == {} else field(default=f.default),)
46
+ fields.append(fld)
47
+ # for fn in cls._meta.fk_fields:
48
+ # f = cls._meta.fields_map[fn]
49
+ # fld = fn+"_id", int
50
+ # if f.default or f.allows_generated or f.null or not f.required:
51
+ # fld += (field(default=f.default),)
52
+ # fields.append(fld)
53
+
54
+ dcl = make_dataclass(cls.__name__ + cn, fields, bases=(BaseUpd,), kw_only=True)
55
+ dcl._unq = (cls._meta.unique_together or ((),))[0]
56
+ if with_pk:
57
+ dcl._unq += ("id",)
58
+ setattr(cls, cn, dcl)
59
+
60
+ return getattr(cls, cn)
61
+
62
+ # # # CRUD Methods # # #
63
+ @classmethod
64
+ def validate(cls, dct: dict) -> BaseUpd:
65
+ dcl = cls.in_type("id" in dct)
66
+ field_names = [n.name for n in fields(dcl)]
67
+ return dcl(**{k: v for k, v in dct.items() if k in field_names})
68
+
69
+ @classmethod
70
+ async def get_or_create_by_name(cls, name: str, attr_name: str = None, def_dict: dict = None) -> Self:
71
+ attr_name = attr_name or list(cls._name)[0]
72
+ if not (obj := await cls.get_or_none(**{attr_name: name})):
73
+ next_id = (await cls.all().order_by("-id").first()).id + 1
74
+ obj = await cls.create(id=next_id, **{attr_name: name}, **(def_dict or {}))
75
+ return obj
76
+
77
+ class PydanticMeta:
78
+ model_config = ConfigDict(use_enum_values=True)
79
+ # include: tuple[str, ...] = ()
80
+ # exclude: tuple[str, ...] = ("Meta",)
81
+ # computed: tuple[str, ...] = ()
82
+ backward_relations: bool = False # True
83
+ max_recursion: int = 1 # default: 3
84
+ # allow_cycles: bool = False
85
+ # exclude_raw_fields: bool = False # True
86
+ # sort_alphabetically: bool = False
87
+ # model_config: ConfigDict | None = None
88
+
89
+ class Meta:
90
+ abstract = True
@@ -0,0 +1,11 @@
1
+ from dataclasses import dataclass, asdict
2
+ from typing import ClassVar
3
+
4
+
5
+ @dataclass
6
+ class BaseUpd:
7
+ _unq: ClassVar[tuple[str]]
8
+
9
+ def df_unq(self) -> dict:
10
+ d = {k: v for k, v in asdict(self).items() if v is not None or k in self._unq}
11
+ return {**{k: d.pop(k, None) for k in self._unq}, "defaults": d}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xn-model
3
- Version: 1.0.12
3
+ Version: 1.0.14
4
4
  Summary: Base model for xn-api
5
5
  Author-email: Mike Artemiev <mixartemev@gmail.com>
6
6
  License: MIT
@@ -10,6 +10,7 @@ Keywords: tortoise,model,crud,generator,api,admin
10
10
  Requires-Python: >=3.11
11
11
  Description-Content-Type: text/markdown
12
12
  Requires-Dist: tortoise-orm[asyncpg]
13
+ Requires-Dist: pydantic
13
14
  Provides-Extra: dev
14
15
  Requires-Dist: pytest; extra == "dev"
15
16
  Requires-Dist: python-dotenv; extra == "dev"
@@ -1,4 +1,5 @@
1
1
  tortoise-orm[asyncpg]
2
+ pydantic
2
3
 
3
4
  [dev]
4
5
  pytest
@@ -1,78 +0,0 @@
1
- from datetime import datetime
2
- from typing import Self
3
-
4
- from pydantic import ConfigDict, BaseModel
5
- from tortoise import Model as TortModel
6
- from tortoise.contrib.pydantic import pydantic_model_creator, PydanticModel
7
- from tortoise.fields import IntField
8
-
9
- from x_model.field import DatetimeSecField
10
-
11
-
12
- class TsTrait:
13
- created_at: datetime | None = DatetimeSecField(auto_now_add=True)
14
- updated_at: datetime | None = DatetimeSecField(auto_now=True)
15
-
16
-
17
- class Model(TortModel):
18
- id: int = IntField(True)
19
-
20
- _out_type: type[BaseModel] = None # overridable
21
- _in_type: type[BaseModel] = None # overridable
22
- _name: tuple[str] = ("name",)
23
- _sorts: tuple[str] = ("-id",)
24
-
25
- def __repr__(self, sep: str = " ") -> str:
26
- return sep.join(getattr(self, name_fragment) for name_fragment in self._name)
27
-
28
- @classmethod
29
- def out_type(cls):
30
- if not cls._out_type:
31
- cls._out_type = pydantic_model_creator(cls, name=cls.__name__ + "Out")
32
- return cls._out_type
33
-
34
- @classmethod
35
- def in_type(cls):
36
- if not cls._in_type:
37
- cls._in_type = pydantic_model_creator(
38
- cls, name=cls.__name__ + "In", exclude_readonly=True, meta_override=cls.PydanticMetaIn
39
- )
40
- return cls._in_type
41
-
42
- # # # CRUD Methods # # #
43
- @classmethod
44
- async def get_one(cls, id_: int) -> PydanticModel:
45
- if obj := await cls.get_or_none(id=id_):
46
- return await cls.out_type().from_tortoise_orm(obj)
47
- raise LookupError(f"{cls.__name__}#{id_} not found")
48
-
49
- async def one(self) -> PydanticModel:
50
- return await self.out_type().from_tortoise_orm(self)
51
-
52
- @classmethod
53
- async def get_or_create_by_name(cls, name: str, attr_name: str = None, def_dict: dict = None) -> Self:
54
- attr_name = attr_name or list(cls._name)[0]
55
- if not (obj := await cls.get_or_none(**{attr_name: name})):
56
- next_id = (await cls.all().order_by("-id").first()).id + 1
57
- obj = await cls.create(id=next_id, **{attr_name: name}, **(def_dict or {}))
58
- return obj
59
-
60
- class PydanticMeta:
61
- model_config = ConfigDict(use_enum_values=True)
62
- # include: tuple[str, ...] = ()
63
- # exclude: tuple[str, ...] = ("Meta",)
64
- # computed: tuple[str, ...] = ()
65
- # backward_relations: bool = True
66
- max_recursion: int = 1 # default: 3
67
- # allow_cycles: bool = False
68
- # exclude_raw_fields: bool = True
69
- # sort_alphabetically: bool = False
70
- # model_config: ConfigDict | None = None
71
-
72
- class PydanticMetaIn:
73
- backward_relations: bool = False
74
- max_recursion: int = 0
75
- exclude_raw_fields: bool = False
76
-
77
- class Meta:
78
- abstract = True
@@ -1,16 +0,0 @@
1
- from typing import ClassVar
2
- from pydantic import BaseModel
3
-
4
-
5
- class New(BaseModel):
6
- _unq: ClassVar[tuple[str]] = ()
7
-
8
- def df_unq(self) -> dict:
9
- d = {k: v for k, v in self.model_dump(exclude_none=True).items()}
10
- return {**{k: d.pop(k, None) for k in set(self._unq)}, "defaults": d}
11
-
12
-
13
- class Upd(New):
14
- _unq = ("id",)
15
-
16
- id: int
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