ramifice 0.3.34__py3-none-any.whl → 0.4.1__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/commons/tools.py CHANGED
@@ -13,7 +13,7 @@ def password_to_none(
13
13
  ) -> dict[str, Any]:
14
14
  """Create object instance from Mongo document."""
15
15
  for f_name, t_name in field_name_and_type.items():
16
- if "Pass" in t_name:
16
+ if "PasswordField" == t_name:
17
17
  mongo_doc[f_name] = None
18
18
  return mongo_doc
19
19
 
@@ -26,7 +26,14 @@ def from_mongo_doc(
26
26
  obj = cls_model()
27
27
  for name, data in mongo_doc.items():
28
28
  field = obj.__dict__[name]
29
- field.value = data if field.group != "pass" else None
29
+ if "TextField" == field.field_type:
30
+ field.value = (
31
+ data[translations.CURRENT_LOCALE] if isinstance(field.value, dict) else None
32
+ )
33
+ elif field.group == "pass":
34
+ field.value = None
35
+ else:
36
+ field.value = data
30
37
  return obj
31
38
 
32
39
 
@@ -47,7 +54,9 @@ def mongo_doc_to_raw_doc(
47
54
  for f_name, t_name in field_name_and_type.items():
48
55
  value = mongo_doc[f_name]
49
56
  if value is not None:
50
- if "Date" in t_name:
57
+ if "TextField" == t_name:
58
+ value = value[current_locale] if isinstance(value, dict) else None
59
+ elif "Date" in t_name:
51
60
  if "Time" in t_name:
52
61
  value = format_datetime(
53
62
  datetime=value,
@@ -60,9 +69,9 @@ def mongo_doc_to_raw_doc(
60
69
  format="short",
61
70
  locale=current_locale,
62
71
  )
63
- elif "ID" in t_name:
72
+ elif "IDField" == t_name:
64
73
  value = str(value)
65
- elif "Pass" in t_name:
74
+ elif "PasswordField" == t_name:
66
75
  value = None
67
76
  doc[f_name] = value
68
77
  return doc
@@ -26,3 +26,10 @@ class TextGroup:
26
26
  self.required = required
27
27
  self.readonly = readonly
28
28
  self.unique = unique
29
+
30
+ def __len__(self) -> int:
31
+ """Return length of field `value`."""
32
+ value = self.value
33
+ if value is None:
34
+ return 0
35
+ return len(value)
@@ -3,10 +3,9 @@
3
3
  from ..utils import globals
4
4
  from ..utils.mixins.json_converter import JsonMixin
5
5
  from .general.field import Field
6
- from .general.text_group import TextGroup
7
6
 
8
7
 
9
- class TextField(Field, TextGroup, JsonMixin):
8
+ class TextField(Field, JsonMixin):
10
9
  """Field of Model for enter text."""
11
10
 
12
11
  def __init__( # noqa: D107
@@ -19,7 +18,6 @@ class TextField(Field, TextGroup, JsonMixin):
19
18
  warning: list[str] | None = None,
20
19
  textarea: bool = False,
21
20
  use_editor: bool = False,
22
- default: str | None = None,
23
21
  placeholder: str = "",
24
22
  required: bool = False,
25
23
  readonly: bool = False,
@@ -29,15 +27,6 @@ class TextField(Field, TextGroup, JsonMixin):
29
27
  if globals.DEBUG:
30
28
  if not isinstance(maxlength, int):
31
29
  raise AssertionError("Parameter `maxlength` - Not а `int` type!")
32
- if default is not None:
33
- if not isinstance(default, str):
34
- raise AssertionError("Parameter `default` - Not а `str` type!")
35
- if len(default) == 0:
36
- raise AssertionError(
37
- "The `default` parameter should not contain an empty string!"
38
- )
39
- if len(default) > maxlength:
40
- raise AssertionError("Parameter `default` exceeds the size of `maxlength`!")
41
30
  if not isinstance(label, str):
42
31
  raise AssertionError("Parameter `default` - Not а `str` type!")
43
32
  if not isinstance(disabled, bool):
@@ -78,17 +67,28 @@ class TextField(Field, TextGroup, JsonMixin):
78
67
  field_type="TextField",
79
68
  group="text",
80
69
  )
81
- TextGroup.__init__(
82
- self,
83
- input_type="text",
84
- placeholder=placeholder,
85
- required=required,
86
- readonly=readonly,
87
- unique=unique,
88
- )
89
70
  JsonMixin.__init__(self)
90
71
 
91
- self.default = default
72
+ self.value: str | dict[str, str] | None = None
73
+ self.input_type = "text"
74
+ self.placeholder = placeholder
75
+ self.required = required
76
+ self.readonly = readonly
77
+ self.unique = unique
92
78
  self.textarea = textarea
93
79
  self.use_editor = use_editor
94
80
  self.maxlength = maxlength
81
+
82
+ def __len__(self) -> int:
83
+ """Return length of field `value`."""
84
+ value = self.value
85
+ if isinstance(value, str):
86
+ return len(value)
87
+ elif isinstance(value, dict):
88
+ count = 0
89
+ for text in value.values():
90
+ tmp = len(text)
91
+ if tmp > count:
92
+ count = tmp
93
+ return count
94
+ return 0
ramifice/models/model.py CHANGED
@@ -45,6 +45,11 @@ class Model(metaclass=ABCMeta):
45
45
  self.fields()
46
46
  self.inject()
47
47
 
48
+ @property
49
+ def id(self) -> IDField:
50
+ """Getter for the field `_id`."""
51
+ return self._id
52
+
48
53
  @abstractmethod
49
54
  def fields(self) -> None:
50
55
  """For adding fields."""
@@ -84,6 +84,9 @@ class CheckMixin(
84
84
  "field_data": None,
85
85
  "full_model_name": cls_model.META["full_model_name"],
86
86
  "is_migrate_model": is_migrate_model,
87
+ "curr_doc": (
88
+ await collection.find_one({"_id": doc_id}) if is_save and is_update else None
89
+ ),
87
90
  }
88
91
 
89
92
  # Run checking fields.
@@ -128,9 +131,7 @@ class CheckMixin(
128
131
  if not is_update:
129
132
  self._id.value = None
130
133
  # Delete orphaned files.
131
- curr_doc: dict[str, Any] | None = (
132
- await collection.find_one({"_id": doc_id}) if is_update else None
133
- )
134
+ curr_doc: dict[str, Any] | None = params["curr_doc"]
134
135
  for field_name, field_data in self.__dict__.items():
135
136
  if callable(field_data) or field_data.ignored:
136
137
  continue
@@ -25,11 +25,17 @@ class TextGroupMixin:
25
25
  async def text_group(self, params: dict[str, Any]) -> None:
26
26
  """Checking text fields."""
27
27
  field = params["field_data"]
28
+ field_type: str = field.field_type
29
+ is_text_field: bool = "TextField" == field_type
28
30
  # Get current value.
29
- value = field.value or field.default or None
31
+ value = field.value or field.__dict__.get("default")
30
32
 
31
- if not isinstance(value, (str, type(None))):
32
- panic_type_error("str | None", params)
33
+ if is_text_field:
34
+ if not isinstance(value, (str, dict, type(None))):
35
+ panic_type_error("str | dict | None", params)
36
+ else:
37
+ if not isinstance(value, (str, type(None))):
38
+ panic_type_error("str | None", params)
33
39
 
34
40
  if value is None:
35
41
  if field.required:
@@ -40,7 +46,7 @@ class TextGroupMixin:
40
46
  return
41
47
  # Validation the `maxlength` field attribute.
42
48
  maxlength: int | None = field.__dict__.get("maxlength")
43
- if maxlength is not None and len(value) > maxlength:
49
+ if maxlength is not None and len(field) > maxlength:
44
50
  err_msg = translations._("The length of the string exceeds maxlength=%d !" % maxlength)
45
51
  accumulate_error(err_msg, params)
46
52
  # Validation the `unique` field attribute.
@@ -48,8 +54,7 @@ class TextGroupMixin:
48
54
  err_msg = translations._("Is not unique !")
49
55
  accumulate_error(err_msg, params)
50
56
  # Validation Email, Url, IP, Color, Phone.
51
- field_type = field.field_type
52
- if "Email" in field_type:
57
+ if "EmailField" == field_type:
53
58
  try:
54
59
  emailinfo = validate_email(
55
60
  str(value),
@@ -60,18 +65,28 @@ class TextGroupMixin:
60
65
  except EmailNotValidError:
61
66
  err_msg = translations._("Invalid Email address !")
62
67
  accumulate_error(err_msg, params)
63
- elif "URL" in field_type and not is_url(value):
68
+ elif "URLField" == field_type and not is_url(value):
64
69
  err_msg = translations._("Invalid URL address !")
65
70
  accumulate_error(err_msg, params)
66
- elif "IP" in field_type and not is_ip(value):
71
+ elif "IPField" == field_type and not is_ip(value):
67
72
  err_msg = translations._("Invalid IP address !")
68
73
  accumulate_error(err_msg, params)
69
- elif "Color" in field_type and not is_color(value):
74
+ elif "ColorField" == field_type and not is_color(value):
70
75
  err_msg = translations._("Invalid Color code !")
71
76
  accumulate_error(err_msg, params)
72
- elif "Phone" in field_type and not is_phone(value):
77
+ elif "PhoneField" == field_type and not is_phone(value):
73
78
  err_msg = translations._("Invalid Phone number !")
74
79
  accumulate_error(err_msg, params)
75
80
  # Insert result.
76
81
  if params["is_save"]:
82
+ if is_text_field:
83
+ mult_lang_text: dict[str, str] = (
84
+ params["curr_doc"][field.name] if params["is_update"] else {}
85
+ )
86
+ if isinstance(value, dict):
87
+ for lang, text in value.items():
88
+ mult_lang_text[lang] = text
89
+ else:
90
+ mult_lang_text[translations.CURRENT_LOCALE] = value
91
+ value = mult_lang_text
77
92
  params["result_map"][field.name] = value
@@ -2,7 +2,7 @@
2
2
 
3
3
  from typing import Any
4
4
 
5
- from ..utils.errors import PanicError
5
+ from ..utils import errors, translations
6
6
 
7
7
 
8
8
  def ignored_fields_to_none(inst_model: Any) -> None:
@@ -16,7 +16,14 @@ def refresh_from_mongo_doc(inst_model: Any, mongo_doc: dict[str, Any]) -> None:
16
16
  """Update object instance from Mongo document."""
17
17
  for name, data in mongo_doc.items():
18
18
  field = inst_model.__dict__[name]
19
- field.value = data if field.group != "pass" else None
19
+ if "TextField" == field.field_type:
20
+ field.value = (
21
+ data[translations.CURRENT_LOCALE] if isinstance(field.value, dict) else None
22
+ )
23
+ elif field.group == "pass":
24
+ field.value = None
25
+ else:
26
+ field.value = data
20
27
 
21
28
 
22
29
  def panic_type_error(value_type: str, params: dict[str, Any]) -> None:
@@ -26,7 +33,7 @@ def panic_type_error(value_type: str, params: dict[str, Any]) -> None:
26
33
  + f"Field: `{params['field_data'].name}` > "
27
34
  + f"Parameter: `value` => Must be `{value_type}` type!"
28
35
  )
29
- raise PanicError(msg)
36
+ raise errors.PanicError(msg)
30
37
 
31
38
 
32
39
  def accumulate_error(err_msg: str, params: dict[str, Any]) -> None:
@@ -41,7 +48,7 @@ def accumulate_error(err_msg: str, params: dict[str, Any]) -> None:
41
48
  + f"Field: `{params['field_data'].name}`"
42
49
  + f" => {err_msg}"
43
50
  )
44
- raise PanicError(msg)
51
+ raise errors.PanicError(msg)
45
52
 
46
53
 
47
54
  async def check_uniqueness(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ramifice
3
- Version: 0.3.34
3
+ Version: 0.4.1
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/
@@ -135,7 +135,7 @@ from datetime import datetime
135
135
 
136
136
  from pymongo import AsyncMongoClient
137
137
  from ramifice import model, translations, migration
138
- from ramifice.fields import DateField, EmailField, ImageField, PasswordField, TextField
138
+ from ramifice.fields import DateField, EmailField, ImageField, PasswordField, TextField, FileField
139
139
 
140
140
 
141
141
  @model(service_name="Accounts")
@@ -157,6 +157,10 @@ class User:
157
157
  # Hint: By default = 2 MB
158
158
  max_size=524288 # 0.5 MB = 524288 Bytes (in binary)
159
159
  )
160
+ self.resume = FileField(
161
+ label=gettext("Resume"),
162
+ default="public/media/default/no_doc.odt",
163
+ )
160
164
  self.username = TextField(
161
165
  label=gettext("Username"),
162
166
  required=True,
@@ -199,11 +203,13 @@ async def main():
199
203
  ).migrat()
200
204
 
201
205
  # If you need to change the language of translation.
202
- # translations.change_locale("ru")
206
+ # Hint: For Ramifice by default = "en"
207
+ translations.change_locale("en")
203
208
 
204
209
  user = User()
205
210
  user.username.value = "pythondev"
206
211
  user.avatar.from_path("public/media/default/no-photo.png")
212
+ user.resume.from_path("public/media/default/no_doc.odt")
207
213
  user.first_name.value = "John"
208
214
  user.last_name.value = "Smith"
209
215
  user.email.value = "John_Smith@gmail.com"
@@ -222,7 +228,7 @@ async def main():
222
228
  user.print_err()
223
229
 
224
230
  print("User details:")
225
- user_details = await User.find_one_to_raw_doc({"_id": user._id.value})
231
+ user_details = await User.find_one_to_raw_doc({"_id": user.id.value})
226
232
  pprint.pprint(user_details)
227
233
 
228
234
  # Remove User.
@@ -5,7 +5,7 @@ ramifice/commons/general.py,sha256=dy2GTpekLyshrNG68XV2dxNgwsS4QODtIcG-c6Xvalw,4
5
5
  ramifice/commons/indexes.py,sha256=hAcWKZ9MMgLbRtoQ6Af0b8r-PY0dbEYmMBl8z_d1DRo,3646
6
6
  ramifice/commons/many.py,sha256=KGt7licVJmLADvyPtH7-GbXHig-EnGFbnk7QKu3mfvQ,8214
7
7
  ramifice/commons/one.py,sha256=h9R-Ev-7h-xln4lp4SMJIIU9Cf9acqwjj4YYaML6uFg,5832
8
- ramifice/commons/tools.py,sha256=v80Hf2O5NZ5xml4NFxINO9tZXrK4JQ9xC6xgZ9V3_tM,2002
8
+ ramifice/commons/tools.py,sha256=ND1s-wt2wf8KhriUrLdCAQ56e7GlxrFZ69GWSwgoocA,2386
9
9
  ramifice/commons/unit_manager.py,sha256=IkWqXu1PHHal0aGfx6zme81iXPeygxPqEWO-u_8RXoI,4356
10
10
  ramifice/fields/__init__.py,sha256=yRfX7Tvpuh27Ggcx5u9e1RRYK7Wu59EVJYxxetmuP1w,1290
11
11
  ramifice/fields/bool_field.py,sha256=WWubSwsFJZu8b3MviDd2zXnNYOQaYw8toklFNSTVFLA,2072
@@ -34,7 +34,7 @@ ramifice/fields/ip_field.py,sha256=tsxdwKd-MhkhnE1F2FmFg67jwh9Q-RvXECv1aG8PitE,3
34
34
  ramifice/fields/password_field.py,sha256=m3n_qJRRHCtgyBC7nDB0dYBO_82Lp1Ws48d1_jxHA8o,3216
35
35
  ramifice/fields/phone_field.py,sha256=5qZ_rR82M9cuW7Dv-D5d9jDaw7DJp9y60lRB4GEmi4w,3615
36
36
  ramifice/fields/slug_field.py,sha256=9rH1VdxYPoK60QCKHRB91bA3ZmIeIuFmf6WttzB1yTI,2685
37
- ramifice/fields/text_field.py,sha256=sihbdO425xYrIY4yAUGtZT4yShJ8OKM0OfhCupTSA2o,4001
37
+ ramifice/fields/text_field.py,sha256=OrZJ337C9YUT7LOzIZR5xRBxL7dSb7ERddiJWQaT3zI,3797
38
38
  ramifice/fields/url_field.py,sha256=DTy4B22juav9dzIrqKy3NzM2vuICHvDzIDbzkkyF1C0,3993
39
39
  ramifice/fields/general/__init__.py,sha256=5OE0TwPQjvpB3lBPuEwKrhjR_1ehOWxB98OJP_n32MA,20
40
40
  ramifice/fields/general/choice_group.py,sha256=TBJblwH8mB71wd1z2jcSs28H-zx3JZVBfkk4YCE1-pI,916
@@ -42,18 +42,18 @@ ramifice/fields/general/date_group.py,sha256=RKockc_X1bYAbpzxTzXdc0kxgx51MK0bjko
42
42
  ramifice/fields/general/field.py,sha256=Iwjx0idIUIFwcQw4TQEqejKct1EGaaJv1zZ1poSW8lo,1338
43
43
  ramifice/fields/general/file_group.py,sha256=n45KfPzFI_l5hXoKkPDG0Q-0mdC2obExV-3PJ-MN5tA,1071
44
44
  ramifice/fields/general/number_group.py,sha256=AqlCY-t6JHZ2QVBe7mk5nPt6z8M4VJ_RARRlSBoIxms,715
45
- ramifice/fields/general/text_group.py,sha256=gP6mUGXr-LTI1UegqgEMQ-5vtUJuJs0wDYKVkknW_5E,873
45
+ ramifice/fields/general/text_group.py,sha256=6GD2Fe6Toz6zZjAAlcy5rPVCAGh6Yn1ltdIrFg9RF18,1057
46
46
  ramifice/models/__init__.py,sha256=h_QQ5rSJNZ-7kmx7wIFd8E8RmUS94b_x4jdwMbq8zm4,15
47
47
  ramifice/models/decorator.py,sha256=jOEn-0v0m_tM7mow45-tM-8LADUmwlG2DWZsEuC9S_w,6834
48
- ramifice/models/model.py,sha256=6iD4c6Rk9Y5SFXnKNULWk7PlgVfzU_i4arF6SX6nysg,6495
48
+ ramifice/models/model.py,sha256=ZvgIbcuSXuAkBl_FofOZXGwWMwlqKB-PwTMyXiqK-Fo,6610
49
49
  ramifice/models/pseudo.py,sha256=PhLQM4zXdaOtTSmNFzwN4twlUmdvA1D_-YOMJtaOIwM,6754
50
50
  ramifice/paladins/__init__.py,sha256=PIP3AXI2KBRXNcLJUF0d7ygJ7VLOAxlhb4HRKQ9MGYY,516
51
- ramifice/paladins/check.py,sha256=6ywXXQLfX7GR2I1M7ka71J82v770JJ9GhPN0bVfsw5Y,6747
51
+ ramifice/paladins/check.py,sha256=SdDC9cE4I_Pt9L9NTatEzNYtwpcCIy03yCJqi1O6qM8,6796
52
52
  ramifice/paladins/delete.py,sha256=tw50E98D5eFZ7gHGnh_8ztUB1LeTeWWKZvIcQqlgbF8,3352
53
53
  ramifice/paladins/password.py,sha256=w1XWh3bsncH1VTVjCLxyKI2waxMvltwcsPWW3V9Ib84,3027
54
54
  ramifice/paladins/refrash.py,sha256=fw-9x_NKGzreipBt_F9KF6FTsYm9hSzfq4ubi1FHYrQ,1065
55
55
  ramifice/paladins/save.py,sha256=EG0_v-imCQPax2pJIAXPoQcSL99g8abY2EeGJMvXBW4,3864
56
- ramifice/paladins/tools.py,sha256=SrYeZJ0A8yahosvxNXGoPUM3Af9xeyggEQVoSvn6rSI,2044
56
+ ramifice/paladins/tools.py,sha256=d3sKH6GzPlt7SxR9JyTEY3YgmqhwNvwPOg3hg80-zoU,2297
57
57
  ramifice/paladins/validation.py,sha256=gcEJXIEPu1g7Z54vl14YTs5rCmxOEYsgQH1usFfyy7k,1730
58
58
  ramifice/paladins/groups/__init__.py,sha256=hpqmWLsYAMvZHAbmMXluQSqLhkHOSTUAgLHyTM1LTYI,472
59
59
  ramifice/paladins/groups/bool_group.py,sha256=oJc9mw9KGrnK_Pj7uXixYYQAJphcXLr_xSQv3PMUlcU,792
@@ -65,7 +65,7 @@ ramifice/paladins/groups/img_group.py,sha256=RoD_QnW0F0DAzrGQmRi8jMZnWy2IomlFn6A
65
65
  ramifice/paladins/groups/num_group.py,sha256=FHT9D2S3TsxdMypqoBgZsIzlaLY0KjXdPYqB95vvTRI,2229
66
66
  ramifice/paladins/groups/pass_group.py,sha256=YU5a-NwohEutoEx2N5JmGfg8uPiYiW0XJ8XYsOih6eA,1859
67
67
  ramifice/paladins/groups/slug_group.py,sha256=o-lr1ssfhTcBovlS6pWA2TQdg1qI6tlDjZ2iKkz-584,2191
68
- ramifice/paladins/groups/text_group.py,sha256=tmuzW2YWgUvRCCUxGZMm7dErC7gw3kBUZrdq5QkLE4o,3136
68
+ ramifice/paladins/groups/text_group.py,sha256=HZTocj8Y8tbVDkvKm8jSQQSGhsotPJWtOcb4zEpt6OY,3881
69
69
  ramifice/utils/__init__.py,sha256=xixHoOX4ja5jIUZemem1qn4k4aonv3G3Q76azQK_pkU,43
70
70
  ramifice/utils/errors.py,sha256=iuhq7fzpUmsOyeXeg2fJjta8yAuqlXLKsZVMpfUhtHE,1901
71
71
  ramifice/utils/fixtures.py,sha256=NtxOnZslYJb4yvRpZbs3ckugmTwHQFS_9iCt2zddOC0,3102
@@ -79,7 +79,7 @@ ramifice/utils/mixins/add_valid.py,sha256=TLOObedzXNA9eCylfAVbVCqIKE5sV-P5AdIN7a
79
79
  ramifice/utils/mixins/hooks.py,sha256=33jvJRhfnJeL2Hd_YFXk3M_7wjqHaByU2wRjKyboL6s,914
80
80
  ramifice/utils/mixins/indexing.py,sha256=Z0427HoaVRyNmSNN8Fx0mSICgAKV-gDdu3iR5qYUEbs,329
81
81
  ramifice/utils/mixins/json_converter.py,sha256=WhigXyDAV-FfILaZuwvRFRIk0D90Rv3dG5t-mv5fVyc,1107
82
- ramifice-0.3.34.dist-info/METADATA,sha256=rSgkCAXvxcA4ZrFQuzpy4R4igA3Ce67A64YasrDl7a0,20719
83
- ramifice-0.3.34.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
84
- ramifice-0.3.34.dist-info/licenses/LICENSE,sha256=LrEL0aTZx90HDwFUQCJutORiDjJL9AnuVvCtspXIqt4,1095
85
- ramifice-0.3.34.dist-info/RECORD,,
82
+ ramifice-0.4.1.dist-info/METADATA,sha256=AYPSoWwW4UiCfShGcS4HDvmfkVhHbru6B2dsYCZZXy0,20961
83
+ ramifice-0.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
84
+ ramifice-0.4.1.dist-info/licenses/LICENSE,sha256=LrEL0aTZx90HDwFUQCJutORiDjJL9AnuVvCtspXIqt4,1095
85
+ ramifice-0.4.1.dist-info/RECORD,,