ramifice 0.3.20__py3-none-any.whl → 0.3.21__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.
ramifice/decorators.py CHANGED
@@ -12,6 +12,7 @@ from .hooks import HooksMixin
12
12
  from .indexing import IndexMixin
13
13
  from .model import Model
14
14
  from .paladins import CheckMixin, QPaladinsMixin, ValidationMixin # type: ignore[attr-defined]
15
+ from .pseudo_model import PseudoModel
15
16
  from .store import REGEX
16
17
 
17
18
 
@@ -55,7 +56,10 @@ def model(
55
56
  raise PanicError(msg)
56
57
 
57
58
  attrs = {key: val for key, val in cls.__dict__.items()}
58
- attrs["__dict__"] = Model.__dict__["__dict__"]
59
+ if is_migrate_model:
60
+ attrs["__dict__"] = Model.__dict__["__dict__"]
61
+ else:
62
+ attrs["__dict__"] = PseudoModel.__dict__["__dict__"]
59
63
  metadata = {
60
64
  "service_name": service_name,
61
65
  "fixture_name": fixture_name,
@@ -67,7 +71,7 @@ def model(
67
71
  }
68
72
  attrs["META"] = {
69
73
  **metadata,
70
- **caching(cls, service_name),
74
+ **caching(cls, service_name, is_migrate_model),
71
75
  }
72
76
 
73
77
  if is_migrate_model:
@@ -87,7 +91,7 @@ def model(
87
91
  return type(
88
92
  cls.__name__,
89
93
  (
90
- Model,
94
+ PseudoModel,
91
95
  ValidationMixin,
92
96
  CheckMixin,
93
97
  AddValidMixin,
@@ -98,7 +102,7 @@ def model(
98
102
  return decorator
99
103
 
100
104
 
101
- def caching(cls: Any, service_name: str) -> dict[str, Any]:
105
+ def caching(cls: Any, service_name: str, is_migrate_model: bool) -> dict[str, Any]:
102
106
  """Add additional metadata to `Model.META`."""
103
107
  metadata: dict[str, Any] = {}
104
108
  model_name = cls.__name__
@@ -122,11 +126,10 @@ def caching(cls: Any, service_name: str) -> dict[str, Any]:
122
126
 
123
127
  raw_model = cls()
124
128
  raw_model.fields()
125
- default_fields: dict[str, Any] = {
126
- "_id": IDField(),
127
- "created_at": DateTimeField(),
128
- "updated_at": DateTimeField(),
129
- }
129
+ default_fields: dict[str, Any] = {"_id": IDField()}
130
+ if is_migrate_model:
131
+ default_fields["created_at"] = DateTimeField()
132
+ default_fields["updated_at"] = DateTimeField()
130
133
  fields = {**raw_model.__dict__, **default_fields}
131
134
  for f_name, f_type in fields.items():
132
135
  if not callable(f_type):
@@ -141,7 +144,8 @@ def caching(cls: Any, service_name: str) -> dict[str, Any]:
141
144
  #
142
145
  if not f_type.ignored:
143
146
  # Count fields for migrating.
144
- count_fields_for_migrating += 1
147
+ if is_migrate_model:
148
+ count_fields_for_migrating += 1
145
149
  # Get a dictionary of field names and types.
146
150
  field_name_and_type[f_name] = f_type_str
147
151
  # Build data migration storage for dynamic fields.
ramifice/model.py CHANGED
@@ -1,8 +1,6 @@
1
1
  """For converting Python classes into Ramifice Model."""
2
2
 
3
3
  import json
4
- import os
5
- import shutil
6
4
  from abc import ABCMeta, abstractmethod
7
5
  from typing import Any
8
6
 
@@ -15,7 +13,7 @@ from .fields import DateTimeField, IDField # type: ignore[attr-defined]
15
13
 
16
14
 
17
15
  class Model(metaclass=ABCMeta):
18
- """For converting Python classes into Ramifice Model."""
16
+ """For converting Python Class into Ramifice Model."""
19
17
 
20
18
  META: dict[str, Any] = {}
21
19
 
@@ -46,29 +44,6 @@ class Model(metaclass=ABCMeta):
46
44
  )
47
45
  self.fields()
48
46
  self.inject()
49
- if not self.__class__.META["is_migrate_model"]:
50
- for _, f_type in self.__dict__.items():
51
- if not callable(f_type):
52
- if f_type.group == "img":
53
- f_type.__dict__["add_width_height"] = True
54
-
55
- def __del__(self) -> None: # noqa: D105
56
- # If the model is not migrated,
57
- # it must delete files and images in the destructor.
58
- if not self.__class__.META["is_migrate_model"]:
59
- for _, f_type in self.__dict__.items():
60
- if not callable(f_type):
61
- value = f_type.value
62
- if value is None:
63
- continue
64
- if f_type.group == "file":
65
- value = value.get("path")
66
- if value is not None:
67
- os.remove(value)
68
- elif f_type.group == "img":
69
- value = value.get("imgs_dir_path")
70
- if value is not None:
71
- shutil.rmtree(value)
72
47
 
73
48
  @abstractmethod
74
49
  def fields(self) -> None:
@@ -0,0 +1,183 @@
1
+ """For converting Python classes into Ramifice Model."""
2
+
3
+ import json
4
+ import os
5
+ import shutil
6
+ from abc import ABCMeta, abstractmethod
7
+ from typing import Any
8
+
9
+ from babel.dates import format_date, format_datetime
10
+ from bson.objectid import ObjectId
11
+ from dateutil.parser import parse
12
+
13
+ from . import translations
14
+ from .fields import IDField # type: ignore[attr-defined]
15
+
16
+
17
+ class PseudoModel(metaclass=ABCMeta):
18
+ """For convert the Python class into a pseudo model Ramifice.
19
+
20
+ Used for a Model that do not migrate into the database.
21
+ """
22
+
23
+ META: dict[str, Any] = {}
24
+
25
+ def __init__(self) -> None: # noqa: D107
26
+ self._id = IDField(
27
+ label="Stub",
28
+ placeholder="Stub",
29
+ hint="Stub",
30
+ hide=True,
31
+ disabled=True,
32
+ )
33
+ self.fields()
34
+ self.inject()
35
+ for _, f_type in self.__dict__.items():
36
+ if not callable(f_type):
37
+ if f_type.group == "img":
38
+ f_type.__dict__["add_width_height"] = True
39
+
40
+ def __del__(self) -> None: # noqa: D105
41
+ # If the model is not migrated,
42
+ # it must delete files and images in the destructor.
43
+ for _, f_type in self.__dict__.items():
44
+ if not callable(f_type):
45
+ value = f_type.value
46
+ if value is None:
47
+ continue
48
+ if f_type.group == "file":
49
+ value = value.get("path")
50
+ if value is not None:
51
+ os.remove(value)
52
+ elif f_type.group == "img":
53
+ value = value.get("imgs_dir_path")
54
+ if value is not None:
55
+ shutil.rmtree(value)
56
+
57
+ @abstractmethod
58
+ def fields(self) -> None:
59
+ """For adding fields."""
60
+ pass
61
+
62
+ def model_name(self) -> str:
63
+ """Get Model name - Class name."""
64
+ return self.__class__.__name__
65
+
66
+ def full_model_name(self) -> str:
67
+ """Get full Model name - module_name + . + ClassName."""
68
+ cls = self.__class__
69
+ return f"{cls.__module__}.{cls.__name__}"
70
+
71
+ def inject(self) -> None:
72
+ """Injecting metadata from Model.META in params of fields."""
73
+ metadata = self.__class__.META
74
+ if bool(metadata):
75
+ field_attrs = metadata["field_attrs"]
76
+ data_dynamic_fields = metadata["data_dynamic_fields"]
77
+ for f_name, f_type in self.__dict__.items():
78
+ if not callable(f_type):
79
+ f_type.id = field_attrs[f_name]["id"]
80
+ f_type.name = field_attrs[f_name]["name"]
81
+ if "Dyn" in f_type.field_type:
82
+ f_type.choices = data_dynamic_fields[f_name]
83
+
84
+ # Complect of methods for converting Model to JSON and back.
85
+ # --------------------------------------------------------------------------
86
+ def to_dict(self) -> dict[str, Any]:
87
+ """Convert object instance to a dictionary."""
88
+ json_dict: dict[str, Any] = {}
89
+ for name, data in self.__dict__.items():
90
+ if not callable(data):
91
+ json_dict[name] = data.to_dict()
92
+ return json_dict
93
+
94
+ def to_json(self) -> str:
95
+ """Convert object instance to a JSON string."""
96
+ return json.dumps(self.to_dict())
97
+
98
+ @classmethod
99
+ def from_dict(cls, json_dict: dict[str, Any]) -> Any:
100
+ """Convert JSON string to a object instance."""
101
+ obj = cls()
102
+ for name, data in json_dict.items():
103
+ obj.__dict__[name] = obj.__dict__[name].__class__.from_dict(data)
104
+ return obj
105
+
106
+ @classmethod
107
+ def from_json(cls, json_str: str) -> Any:
108
+ """Convert JSON string to a object instance."""
109
+ json_dict = json.loads(json_str)
110
+ return cls.from_dict(json_dict)
111
+
112
+ # --------------------------------------------------------------------------
113
+ def to_dict_only_value(self) -> dict[str, Any]:
114
+ """Convert model.field.value (only the `value` attribute) to a dictionary."""
115
+ json_dict: dict[str, Any] = {}
116
+ current_locale = translations.CURRENT_LOCALE
117
+ for name, data in self.__dict__.items():
118
+ if callable(data):
119
+ continue
120
+ value = data.value
121
+ if value is not None:
122
+ group = data.group
123
+ if group == "date":
124
+ value = (
125
+ format_date(
126
+ date=value,
127
+ format="short",
128
+ locale=current_locale,
129
+ )
130
+ if data.field_type == "DateField"
131
+ else format_datetime(
132
+ datetime=value,
133
+ format="short",
134
+ locale=current_locale,
135
+ )
136
+ )
137
+ elif group == "id":
138
+ value = str(value)
139
+ elif group == "pass":
140
+ value = None
141
+ json_dict[name] = value
142
+ return json_dict
143
+
144
+ def to_json_only_value(self) -> str:
145
+ """Convert model.field.value (only the `value` attribute) to a JSON string."""
146
+ return json.dumps(self.to_dict_only_value())
147
+
148
+ @classmethod
149
+ def from_dict_only_value(cls, json_dict: dict[str, Any]) -> Any:
150
+ """Convert JSON string to a object instance."""
151
+ obj = cls()
152
+ for name, data in obj.__dict__.items():
153
+ if callable(data):
154
+ continue
155
+ value = json_dict.get(name)
156
+ if value is not None:
157
+ group = data.group
158
+ if group == "date":
159
+ value = parse(value)
160
+ elif group == "id":
161
+ value = ObjectId(value)
162
+ obj.__dict__[name].value = value
163
+ return obj
164
+
165
+ @classmethod
166
+ def from_json_only_value(cls, json_str: str) -> Any:
167
+ """Convert JSON string to a object instance."""
168
+ json_dict = json.loads(json_str)
169
+ return cls.from_dict_only_value(json_dict)
170
+
171
+ def refrash_fields(self, only_value_dict: dict[str, Any]) -> None:
172
+ """Partial or complete update a `value` of fields."""
173
+ for name, data in self.__dict__.items():
174
+ if callable(data):
175
+ continue
176
+ value = only_value_dict.get(name)
177
+ if value is not None:
178
+ group = data.group
179
+ if group == "date":
180
+ value = parse(value)
181
+ elif group == "id":
182
+ value = ObjectId(value)
183
+ self.__dict__[name].value = value
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ramifice
3
- Version: 0.3.20
3
+ Version: 0.3.21
4
4
  Summary: ORM-like API MongoDB for Python language.
5
5
  Project-URL: Homepage, https://github.com/kebasyaty/ramifice
6
6
  Project-URL: Documentation, https://kebasyaty.github.io/ramifice/
@@ -1,13 +1,14 @@
1
1
  ramifice/__init__.py,sha256=ISlaL2BprlJLE_N1fvtAqGrB3Dhniy9IZGoyWEYZhRU,678
2
2
  ramifice/add_valid.py,sha256=kvpMg7snL9tor0A23XRdgwiXazRwHfb8baoJUNjM_4Y,327
3
- ramifice/decorators.py,sha256=txbTYnKw5AtRZb7IshOzEHuoy8MAPB8wBcCTbk-6CcE,6181
3
+ ramifice/decorators.py,sha256=X4UnHrbuMObv10dooNip3u9ihfdNvFqeMcfLNuhLlbg,6466
4
4
  ramifice/errors.py,sha256=iuhq7fzpUmsOyeXeg2fJjta8yAuqlXLKsZVMpfUhtHE,1901
5
5
  ramifice/fixtures.py,sha256=NtxOnZslYJb4yvRpZbs3ckugmTwHQFS_9iCt2zddOC0,3102
6
6
  ramifice/hooks.py,sha256=Ri-ISfMT-IHaLO2eAqg8CODCTs3HRTxSylqspUKnVf4,873
7
7
  ramifice/indexing.py,sha256=wQpX2qei5Zc7iIq5yIV93Skp8Aa8ZD0vybnEk7cRuXs,271
8
8
  ramifice/migration.py,sha256=t_Rm1OUQYrlaPQQd1uS5S7EYMvSuKUcWzi7P4JMkrOw,11114
9
9
  ramifice/mixins.py,sha256=gKLmWQ-QrGO3K5_k-h1tDa08lkCql_dte2Jy05q1wsM,1125
10
- ramifice/model.py,sha256=hKA_BrGBWcn-mYB9dHIJoSJYEteO6SdJdgwgjdaV7mc,7632
10
+ ramifice/model.py,sha256=xhLKosxnT3HkPr6j_BSkB7pvG2WNY_7uylcHo3Oq0vM,6521
11
+ ramifice/pseudo_model.py,sha256=AdETZEvffFvHogtL0NYzp1rqLaI5lq3XcVX1n5v62bw,6816
11
12
  ramifice/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
13
  ramifice/store.py,sha256=MpDEPvUvbs11FXjakNtHPm9MekIv5p1U3as2Y80lTyc,1860
13
14
  ramifice/translations.py,sha256=GNGE0ULAA0aOY5pTxUd3MQW-nVaKvp6BeXWEcsR0s0o,4048
@@ -75,7 +76,7 @@ ramifice/paladins/groups/num_group.py,sha256=Jvb-lwHxapQybbLerC4t-_yO8N7Coo1fIlZ
75
76
  ramifice/paladins/groups/pass_group.py,sha256=SEKpR2voNQtmywugDXJKY4XqPTL91CrJ87h0QNMqQqs,1952
76
77
  ramifice/paladins/groups/slug_group.py,sha256=_IRil2PwpY7cH7WaExNksKz61kQjvc27blrEufgUB30,2323
77
78
  ramifice/paladins/groups/text_group.py,sha256=nYZGwAIsJD-tX8RBtFlWvngO9RU4V0CnREUhxvV2UDo,3493
78
- ramifice-0.3.20.dist-info/METADATA,sha256=ZpWGFOazkmaGbW9rMKja65dApz_DHCrA2_PBCynIqzw,18904
79
- ramifice-0.3.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
80
- ramifice-0.3.20.dist-info/licenses/LICENSE,sha256=LrEL0aTZx90HDwFUQCJutORiDjJL9AnuVvCtspXIqt4,1095
81
- ramifice-0.3.20.dist-info/RECORD,,
79
+ ramifice-0.3.21.dist-info/METADATA,sha256=4pWgH5EVC0dvdG2SGf4smhNUkvdXzbOEymF3YcYfvzQ,18904
80
+ ramifice-0.3.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
81
+ ramifice-0.3.21.dist-info/licenses/LICENSE,sha256=LrEL0aTZx90HDwFUQCJutORiDjJL9AnuVvCtspXIqt4,1095
82
+ ramifice-0.3.21.dist-info/RECORD,,