xn-model 0.11.4__py3-none-any.whl → 1.0.0__py3-none-any.whl

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.
x_model/models.py CHANGED
@@ -1,147 +1,41 @@
1
1
  from datetime import datetime
2
- from pydantic import create_model
3
- from tortoise import Model as TortoiseModel
4
- from tortoise.contrib.pydantic import pydantic_model_creator, PydanticModel
5
- from tortoise import fields
6
- from tortoise.exceptions import FieldError
7
- from tortoise.models import MetaInfo
8
- from tortoise.queryset import QuerySet
9
- from x_model import HTTPException, FailReason
10
-
11
- from x_model.field import DatetimeSecField
12
- from x_model.pydantic import PydList
13
-
14
2
 
15
- class BaseModel(TortoiseModel):
16
- # todo: resolve ownable + add only own list method
17
- # todo: refact: clean old garbage
18
- id: int = fields.IntField(True)
3
+ from pydantic import ConfigDict
4
+ from tortoise import Model as BaseModel
5
+ from tortoise.contrib.pydantic import pydantic_model_creator, PydanticModel
6
+ from tortoise.fields import DatetimeField, IntField
19
7
 
20
- _name: tuple[str] = ("name",)
21
- _sorts: tuple[str] = ("-id",)
22
8
 
23
- def repr(self, sep: str = " ") -> str:
24
- return sep.join(getattr(self, name_fragment) for name_fragment in self._name)
9
+ class DatetimeSecField(DatetimeField):
10
+ class _db_postgres:
11
+ SQL_TYPE = "TIMESTAMPTZ(0)"
25
12
 
26
- @classmethod
27
- async def get_or_create_by_name(cls, name: str, attr_name: str = None, def_dict: dict = None) -> TortoiseModel:
28
- attr_name = attr_name or list(cls._name)[0]
29
- if not (obj := await cls.get_or_none(**{attr_name: name})):
30
- next_id = (await cls.all().order_by("-id").first()).id + 1
31
- obj = await cls.create(id=next_id, **{attr_name: name}, **(def_dict or {}))
32
- return obj
33
13
 
34
- @classmethod
35
- def _page_query(cls, sorts: tuple[str], limit: int = 1000, offset: int = 0, q: str = None, **filters) -> QuerySet:
36
- query = cls.filter(**filters).order_by(*sorts).limit(limit).offset(offset)
37
- if q:
38
- query = query.filter(**{f"{cls._name[0]}__icontains": q})
39
- return query
40
-
41
- @classmethod
42
- async def upsert(cls, data: dict, oid=None):
43
- meta: MetaInfo = cls._meta
44
-
45
- # pop fields for relations from general data dict # todo: add backwards fields for save
46
- m2ms = {k: data.pop(k) for k in meta.m2m_fields if k in data}
47
- # bfks = {k: data.pop(k) for k in meta.backward_fk_fields if k in data}
48
- # bo2os = {k: data.pop(k) for k in meta.backward_o2o_fields if k in data}
49
-
50
- # save general model
51
- # if pk := meta.pk_attr in data.keys():
52
- # unq = {pk: data.pop(pk)}
53
- # else:
54
- # unq = {key: data.pop(key) for key, ft in meta.fields_map.items() if ft.unique and key in data.keys()}
55
- # # unq = meta.unique_together
56
- # obj, is_created = await cls.update_or_create(data, **unq)
57
- obj = (await cls.update_or_create(data, id=oid))[0] if oid else await cls.create(**data)
58
-
59
- # save relations
60
- for k, ids in m2ms.items():
61
- if ids:
62
- m2m_rel: fields.ManyToManyRelation = getattr(obj, k)
63
- items = [await m2m_rel.remote_model[i] for i in ids]
64
- await m2m_rel.clear() # for updating, not just adding
65
- await m2m_rel.add(*items)
66
- # for k, ids in bfks.items():
67
- # bfk_rel: ReverseRelation = getattr(obj, k)
68
- # items = [await bfk_rel.remote_model[i] for i in ids]
69
- # [await item.update_from_dict({bfk_rel.relation_field: obj.pk}).save() for item in items]
70
- # for k, oid in bo2os.items():
71
- # bo2o_rel: QuerySet = getattr(obj, k)
72
- # item = await bo2o_rel.model[oid]
73
- # await item.update_from_dict({obj._meta.db_table: obj}).save()
74
-
75
- await obj.fetch_related(*cls._meta.fetch_fields)
76
- return obj
77
-
78
- class Meta:
79
- abstract = True
14
+ class TsTrait:
15
+ created_at: datetime | None = DatetimeSecField(auto_now_add=True)
16
+ updated_at: datetime | None = DatetimeSecField(auto_now=True)
80
17
 
81
18
 
82
19
  class Model(BaseModel):
83
- _pyd: type[PydanticModel] = None
84
- _pydIn: type[PydanticModel] = None
85
- _pydListItem: type[PydanticModel] = None
20
+ id: int = IntField(True)
86
21
 
87
- class PydanticMeta:
88
- # include: tuple[str, ...] = ()
89
- # exclude: tuple[str, ...] = ()
90
- # computed: tuple[str, ...] = ()
91
- exclude_raw_fields = False # default: True
92
- max_recursion: int = 1 # default: 3
93
-
94
- class PydanticMetaIn:
95
- max_recursion: int = 0 # default: 3
96
- backward_relations: bool = False # no need to disable when max_recursion=0 # default: True
97
- exclude_raw_fields: bool = False # default: True
98
-
99
- class PydanticMetaListItem:
100
- max_recursion: int = 0 # default: 3
101
- backward_relations: bool = False # default: True
102
- exclude_raw_fields = False # default: True
103
- sort_alphabetically: bool = True # default: False
22
+ _name: tuple[str] = ("name",)
23
+ _sorts: tuple[str] = ("-id",)
104
24
 
105
- @classmethod
106
- def pyd(cls) -> type[PydanticModel]:
107
- cls._pyd = cls._pyd or pydantic_model_creator(cls, name=cls.__name__)
108
- return cls._pyd
25
+ def repr(self) -> str:
26
+ return " ".join(getattr(self, name_fragment) for name_fragment in self._name)
109
27
 
110
28
  @classmethod
111
- def pyd_in(cls) -> type[PydanticModel]:
112
- if not cls._pydIn:
113
- opts = tuple(k for k, v in cls._meta.fields_map.items() if not v.required)
114
- cls._pydIn = pydantic_model_creator(
115
- cls,
116
- name=cls.__name__ + "In",
117
- meta_override=cls.PydanticMetaIn,
118
- optional=opts,
119
- exclude_readonly=True,
120
- exclude=("created_at", "updated_at"),
121
- )
122
- if m2ms := cls._meta.m2m_fields: # hack for direct inserting m2m values
123
- cls._pydIn = create_model(
124
- cls._pydIn.__name__, __base__=cls._pydIn, **{m2m: (list[int] | None, None) for m2m in m2ms}
125
- )
126
- return cls._pydIn
29
+ def _pyd(cls, suffix: str, **kwargs) -> type[PydanticModel]:
30
+ return pydantic_model_creator(cls, name=cls.__name__ + suffix, **kwargs)
127
31
 
128
32
  @classmethod
129
- def pyd_list_item(cls) -> type[PydanticModel]:
130
- if not cls._pydListItem:
131
- cls._pydListItem = pydantic_model_creator(
132
- cls, name=cls.__name__ + "ListItem", meta_override=cls.PydanticMetaListItem
133
- )
134
- return cls._pydListItem
33
+ def pyd(cls):
34
+ return cls._pyd("Root")
135
35
 
136
36
  @classmethod
137
- def pyds_list(cls) -> type[PydList]:
138
- return create_model(
139
- cls.__name__ + "List",
140
- data=(list[cls.pyd_list_item()], []),
141
- total=(int, 0),
142
- filtered=(int | None, None),
143
- __base__=PydList[cls.pyd_list_item()],
144
- )
37
+ def pyd_in(cls):
38
+ return cls._pyd("In", exclude_readonly=True)
145
39
 
146
40
  # # # CRUD Methods # # #
147
41
  @classmethod
@@ -150,26 +44,24 @@ class Model(BaseModel):
150
44
  return await cls.pyd().from_queryset_single(q)
151
45
 
152
46
  @classmethod
153
- async def page_pyd(cls, sorts: tuple[str], limit: int = 1000, offset: int = 0, q: str = None, **filters) -> PydList:
154
- filters = {k: v for k, v in filters.items() if v is not None}
155
- pyd_item = cls.pyd_list_item()
156
- query = cls._page_query(sorts, limit, offset, q, **filters)
157
- try:
158
- data = await pyd_item.from_queryset(query)
159
- except FieldError as e:
160
- raise HTTPException(FailReason.body, e)
161
- if limit - (li := len(data)):
162
- filtered = total = li + offset
163
- else:
164
- total = await cls.all().count()
165
- filtered_query = cls.filter(**filters)
166
- if q:
167
- filtered_query = filtered_query.filter(**{f"{cls._name}__icontains": q})
168
- filtered = await filtered_query.count()
169
- pyds = cls.pyds_list()
170
- return pyds(data=data, total=total, filtered=filtered)
47
+ async def get_or_create_by_name(cls, name: str, attr_name: str = None, def_dict: dict = None) -> "Model":
48
+ attr_name = attr_name or list(cls._name)[0]
49
+ if not (obj := await cls.get_or_none(**{attr_name: name})):
50
+ next_id = (await cls.all().order_by("-id").first()).id + 1
51
+ obj = await cls.create(id=next_id, **{attr_name: name}, **(def_dict or {}))
52
+ return obj
171
53
 
54
+ class PydanticMeta:
55
+ model_config = ConfigDict(use_enum_values=True)
56
+ # include: tuple[str, ...] = ()
57
+ # exclude: tuple[str, ...] = ("Meta",)
58
+ # computed: tuple[str, ...] = ()
59
+ # backward_relations: bool = True
60
+ max_recursion: int = 1 # default: 3
61
+ # allow_cycles: bool = False
62
+ # exclude_raw_fields: bool = True
63
+ # sort_alphabetically: bool = False
64
+ # model_config: ConfigDict | None = None
172
65
 
173
- class TsTrait:
174
- created_at: datetime | None = DatetimeSecField(auto_now_add=True)
175
- updated_at: datetime | None = DatetimeSecField(auto_now=True)
66
+ class Meta:
67
+ abstract = True
x_model/pydantic.py CHANGED
@@ -1,14 +1,14 @@
1
- from typing import TypeVar, Generic
2
- from pydantic import BaseModel, ConfigDict
1
+ # from typing import TypeVar, Generic
2
+ from pydantic import BaseModel # , ConfigDict
3
3
 
4
4
 
5
- RootModelType = TypeVar("RootModelType")
6
-
7
-
8
- class PydList(BaseModel, Generic[RootModelType]):
9
- model_config = ConfigDict(arbitrary_types_allowed=True)
10
- data: list[RootModelType]
11
- total: int
5
+ # RootModelType = TypeVar("RootModelType")
6
+ #
7
+ #
8
+ # class PydList(BaseModel, Generic[RootModelType]):
9
+ # model_config = ConfigDict(arbitrary_types_allowed=True)
10
+ # data: list[RootModelType]
11
+ # total: int
12
12
 
13
13
 
14
14
  class Names(BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xn-model
3
- Version: 0.11.4
3
+ Version: 1.0.0
4
4
  Summary: Base model for xn-api
5
5
  Author-email: Mike Artemiev <mixartemev@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,9 @@
1
+ x_model/__init__.py,sha256=leq1K2Lq0zlTY2s5sdhDiGEyfNZPySCOJi0xB5xUSG0,1231
2
+ x_model/field.py,sha256=S461M94ryQG7yu8lreXtWnZo3YdCP97xhbcCJ3BzXsY,2751
3
+ x_model/func.py,sha256=E7jDoHJGaFpKvxbHnT_lyBxUZeMo-GRd5gv9dLw7B9s,289
4
+ x_model/models.py,sha256=wrH9WLrXsj521d9_YYgdZVracwuR8bGlMJmW4qykTHA,2189
5
+ x_model/pydantic.py,sha256=fe8yOCVlnlBcT64bhNodxegnA6f4YdJ0WcXDIs6Ug20,608
6
+ xn_model-1.0.0.dist-info/METADATA,sha256=pJMOvyxmYm9E3uplCGpVD-TyDHgKe4uCr_2jfo8pOLM,971
7
+ xn_model-1.0.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
8
+ xn_model-1.0.0.dist-info/top_level.txt,sha256=QCYyfv5AA_8jPPtCpShkBXzQRUCGuuW7Ro0mqysDE8E,8
9
+ xn_model-1.0.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- x_model/__init__.py,sha256=leq1K2Lq0zlTY2s5sdhDiGEyfNZPySCOJi0xB5xUSG0,1231
2
- x_model/field.py,sha256=S461M94ryQG7yu8lreXtWnZo3YdCP97xhbcCJ3BzXsY,2751
3
- x_model/func.py,sha256=E7jDoHJGaFpKvxbHnT_lyBxUZeMo-GRd5gv9dLw7B9s,289
4
- x_model/models.py,sha256=V1lrDJH-DPnGznhxCkNKFI58igzO2KTVCQNX2MDKzuM,7048
5
- x_model/pydantic.py,sha256=OjNCp4ZFGvUIxZSbDCmXISl9mOjI_UnWi5JMVntSqjM,590
6
- xn_model-0.11.4.dist-info/METADATA,sha256=1Njxvb3fKUa8Vrjec4Mdy_2Oh7tjpIk8DR2WJO34SjY,972
7
- xn_model-0.11.4.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
8
- xn_model-0.11.4.dist-info/top_level.txt,sha256=QCYyfv5AA_8jPPtCpShkBXzQRUCGuuW7Ro0mqysDE8E,8
9
- xn_model-0.11.4.dist-info/RECORD,,