Coverage for formkit_ninja / fields.py: 0.00%
56 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-03 09:21 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-03 09:21 +0000
1from __future__ import annotations
3import warnings
5# from collections import UserDict
6from typing import Any, Sequence
8from django.conf import settings
9from django.db.models import JSONField
10from django.utils import translation
13class WhitelistedKeysDict(dict):
14 """
15 Allow only certain keys.
16 >>> t = WhitelistedKeysDict("ahi!", default_key="tet")
17 >>> t["tet"]
18 'ahi!'
19 >>> t = WhitelistedKeysDict("fire!", default_key="en")
20 >>> t["en"]
21 'fire!'
22 >>> t = WhitelistedKeysDict({"en": "fire!", "tet": "ahi!"}, permitted_keys={"tet", "en"})
23 >>> t["tet"]
24 'ahi!'
25 >>> t["en"]
26 'fire!'
27 >>> set(t.keys()) == {'tet', 'en'}
28 True
29 >>> t = WhitelistedKeysDict({"en": "fire!", "tet": "ahi!", "noexist": "should-warn"}, permitted_keys={"tet", "en"})
30 >>> "noexist" in t
31 False
32 >>> set(t.keys()) == {"tet", "en"}
33 True
34 """
36 default_key: str | None
37 permitted_keys: set[str]
39 def __init__(
40 self,
41 dict_=None,
42 /,
43 permitted_keys: set[str] = set(),
44 default_key: str | None = None,
45 **kwargs,
46 ):
47 self.default_key = default_key
48 self.permitted_keys = permitted_keys
49 if default_key:
50 self.permitted_keys.add(default_key)
51 self.data: dict[str, Any] = {}
52 if isinstance(dict_, str):
53 if self.default_key:
54 super().__init__({self.default_key: dict_})
55 return
56 warnings.warn("No default key was set. You must have a default_key to initialize with a string.")
57 super().__init__()
58 return
59 super().__init__(dict_ or {})
61 def __setitem__(self, key: str, item: str) -> None:
62 if key in self.permitted_keys:
63 return super().__setitem__(key, item)
64 warnings.warn(f"ignored key not in whitelist: {key}")
67class TranslatedValues(WhitelistedKeysDict):
68 """
69 dict rejects keys not in Django's LANGUAGES
70 >>> t = TranslatedValues("ahi!")
71 >>> t["tet"]
72 'ahi!'
73 >>> t = TranslatedValues({"en": "fire!", "tet": "ahi!"})
74 >>> t["tet"]
75 'ahi!'
76 >>> t["en"]
77 'fire!'
78 >>> set(t.keys()) == {'tet', 'en'}
79 True
80 >>> t = TranslatedValues({"en": "fire!", "tet": "ahi!", "noexist": "should-warn"})
81 >>> "noexist" in t
82 False
83 >>> set(t.keys()) == {"tet", "en"}
84 True
85 >>> t.value # Note: only when in Django, if Django language is set to 'tet'
86 'ahi!'
87 """
89 def __init__(self, dict_, /, **kwargs):
90 return super().__init__(
91 dict_,
92 permitted_keys=set((lang[0] for lang in getattr(settings, "LANGUAGES", ()))),
93 default_key=translation.get_language(),
94 **kwargs,
95 )
97 @property
98 def value(self):
99 """
100 Return the value at the current language, or fallback
101 """
102 return TranslatedValues.get_str(self)
104 @staticmethod
105 def get_str(
106 dict_: TranslatedValues,
107 lang: str = translation.get_language(),
108 fallback: Sequence[str] = ("en",),
109 ):
110 """
111 Return the value at the current language, or fallback (static method)
112 """
113 # A newly created value might error out here if the `__str__` ref's the value
114 # hint the user that they may wish to `refresh_from_db`
115 if dict_ is None:
116 return ""
118 if isinstance(dict_, str):
119 warnings.warn("TranslatedValues received a str, you may have a new model which hasn't called `refresh_from_db` yet")
120 return dict_
121 if len(list(dict_)) == 0:
122 return "empty"
123 for lc in (lang, *fallback, list(dict_)[0]):
124 if lc in dict_:
125 return dict_[lc]
128class TranslatedField(JSONField):
129 """
130 When a string is passed to this field
131 it will be saved as a value corresponding
132 to the "LANGUAGE_CODE" key
133 """
135 description = "A translated char field storing content as JSON"
137 def from_db_value(self, value, expression, connection):
138 value_: dict | None = super().from_db_value(value, expression, connection)
139 return TranslatedValues(value_)
141 def to_python(self, value):
142 value_ = super().to_python(value)
143 return TranslatedValues(value_)
146if __name__ == "__main__":
147 import doctest
149 doctest.testmod()