fastlifeweb 0.4.1__tar.gz → 0.5.0__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.
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/PKG-INFO +1 -1
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/pyproject.toml +1 -1
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/request/form_data.py +12 -3
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Checkbox.jinja +4 -2
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Boolean.jinja +1 -1
- fastlifeweb-0.5.0/src/fastlife/templates/pydantic_form/Checklist.jinja +20 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/boolean.py +1 -3
- fastlifeweb-0.5.0/src/fastlife/templating/renderer/widgets/checklist.py +36 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/factory.py +75 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/LICENSE +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/README.md +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/configurator/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/configurator/base.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/configurator/configurator.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/configurator/registry.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/configurator/settings.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/py.typed +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/request/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/security/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/security/csrf.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/security/policy.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/session/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/session/middleware.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/session/serializer.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/shared_utils/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/shared_utils/infer.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/shared_utils/resolver.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/A.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Button.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/CsrfToken.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Form.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/H1.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/H2.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/H3.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/H4.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/H5.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/H6.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Hidden.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Input.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Label.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Option.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/P.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Radio.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/Select.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Dropdown.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Hidden.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Hint.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Model.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Sequence.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Text.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Union.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templates/pydantic_form/Widget.jinja +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/binding.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/abstract.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/jinjax.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/base.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/dropdown.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/hidden.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/model.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/sequence.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/text.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/union.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/testing/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/testing/testclient.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/views/__init__.py +0 -0
- {fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/views/pydantic_form.py +0 -0
@@ -25,8 +25,8 @@ def unflatten_struct(
|
|
25
25
|
csrf_token_name: Optional[str] = None,
|
26
26
|
) -> Mapping[str, Any] | Sequence[Any]:
|
27
27
|
# we sort to ensure that list index are ordered
|
28
|
-
formkeys = sorted(flatten_input.keys())
|
29
|
-
for key in
|
28
|
+
# formkeys = sorted(flatten_input.keys())
|
29
|
+
for key in flatten_input:
|
30
30
|
if csrf_token_name is not None and key == csrf_token_name:
|
31
31
|
continue
|
32
32
|
lkey, sep, rest = key.partition(".")
|
@@ -78,8 +78,17 @@ async def unflatten_mapping_form_data(
|
|
78
78
|
request: Request, reg: Registry
|
79
79
|
) -> Mapping[str, Any]:
|
80
80
|
form_data = await request.form()
|
81
|
+
form_data_decode_list: MutableMapping[str, Any] = {}
|
82
|
+
for key, val in form_data.multi_items():
|
83
|
+
if key in form_data_decode_list:
|
84
|
+
if not isinstance(form_data_decode_list, list):
|
85
|
+
form_data_decode_list[key] = [form_data_decode_list[key]]
|
86
|
+
form_data_decode_list[key].append(val)
|
87
|
+
else:
|
88
|
+
form_data_decode_list[key] = val
|
89
|
+
|
81
90
|
ret = unflatten_struct(
|
82
|
-
|
91
|
+
form_data_decode_list, {}, csrf_token_name=reg.settings.csrf_token_name
|
83
92
|
) # type: ignore
|
84
93
|
return ret # type: ignore
|
85
94
|
|
@@ -1,7 +1,8 @@
|
|
1
1
|
{# def
|
2
2
|
name,
|
3
|
-
value,
|
4
3
|
id,
|
4
|
+
value=None,
|
5
|
+
checked=False,
|
5
6
|
class_="""
|
6
7
|
bg-neutral-100
|
7
8
|
border-neutral-300
|
@@ -17,4 +18,5 @@ focus:ring-2
|
|
17
18
|
focus:ring-primary-500
|
18
19
|
"""
|
19
20
|
#}
|
20
|
-
<input name="{{name}}" {% if value
|
21
|
+
<input name="{{name}}" {% if value is not none %}value="{{value}}" {% endif %} {% if checked %}checked{% endif %}
|
22
|
+
type="checkbox" id="{{id}}" class="{{attrs.class or class_}}" />
|
@@ -2,6 +2,6 @@
|
|
2
2
|
<pydantic_form.Widget widget={widget} removable={widget.removable}>
|
3
3
|
<div class="pt-4">
|
4
4
|
<Label for={widget.id}>{{widget.title}}</Label>
|
5
|
-
<Checkbox name={widget.name} type="checkbox" id={widget.id} {
|
5
|
+
<Checkbox name={widget.name} type="checkbox" id={widget.id} checked={widget.value} />
|
6
6
|
</div>
|
7
7
|
</pydantic_form.Widget>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{# def
|
2
|
+
widget,
|
3
|
+
#}
|
4
|
+
|
5
|
+
<pydantic_form.Widget widget={widget} removable={widget.removable}>
|
6
|
+
<div class="pt-4">
|
7
|
+
<details open>
|
8
|
+
<summary class="justify-between items-center font-medium cursor-pointer">
|
9
|
+
<H3>{{widget.title}}</H3>
|
10
|
+
</summary>
|
11
|
+
<div>
|
12
|
+
{% for value in widget.value %}
|
13
|
+
<div class="flex items-center mb-4">
|
14
|
+
<Checkbox name={widget.name} type="checkbox" id={value.id} value={value.value} checked={value.checked} />
|
15
|
+
<Label for={value.id} class="ms-2 text-base text-neutral-900 dark:text-white">{{value.label}}</Label>
|
16
|
+
</div>
|
17
|
+
{% endfor %}
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
</pydantic_form.Widget>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
from typing import Sequence
|
2
|
+
|
3
|
+
from pydantic import BaseModel
|
4
|
+
|
5
|
+
from .base import Widget
|
6
|
+
|
7
|
+
|
8
|
+
class Checkable(BaseModel):
|
9
|
+
label: str
|
10
|
+
name: str
|
11
|
+
value: str
|
12
|
+
token: str
|
13
|
+
checked: bool
|
14
|
+
|
15
|
+
@property
|
16
|
+
def id(self) -> str:
|
17
|
+
id = f"{self.name}-{self.value}-{self.token}"
|
18
|
+
return id.replace(".", "-").replace("_", "-")
|
19
|
+
|
20
|
+
|
21
|
+
class ChecklistWidget(Widget[Sequence[Checkable]]):
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
name: str,
|
25
|
+
*,
|
26
|
+
title: str | None,
|
27
|
+
value: Sequence[Checkable],
|
28
|
+
token: str,
|
29
|
+
removable: bool,
|
30
|
+
) -> None:
|
31
|
+
super().__init__(
|
32
|
+
name, value=value, token=token, title=title, removable=removable
|
33
|
+
)
|
34
|
+
|
35
|
+
def get_template(self) -> str:
|
36
|
+
return "pydantic_form.Checklist"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import secrets
|
2
2
|
from collections.abc import MutableSequence, Sequence
|
3
3
|
from decimal import Decimal
|
4
|
+
from enum import Enum
|
4
5
|
from types import NoneType
|
5
6
|
from typing import Any, Literal, Mapping, Optional, Type, cast, get_origin
|
6
7
|
from uuid import UUID
|
@@ -12,6 +13,7 @@ from pydantic.fields import FieldInfo
|
|
12
13
|
from fastlife.shared_utils.infer import is_complex_type, is_union
|
13
14
|
from fastlife.templating.renderer.abstract import AbstractTemplateRenderer
|
14
15
|
from fastlife.templating.renderer.widgets.boolean import BooleanWidget
|
16
|
+
from fastlife.templating.renderer.widgets.checklist import Checkable, ChecklistWidget
|
15
17
|
from fastlife.templating.renderer.widgets.dropdown import DropDownWidget
|
16
18
|
from fastlife.templating.renderer.widgets.hidden import HiddenWidget
|
17
19
|
from fastlife.templating.renderer.widgets.sequence import SequenceWidget
|
@@ -96,6 +98,12 @@ class WidgetFactory:
|
|
96
98
|
if type_origin is Literal:
|
97
99
|
return self.build_literal(name, typ, field, value, removable)
|
98
100
|
|
101
|
+
if type_origin is set:
|
102
|
+
return self.build_set(name, typ, field, value, removable)
|
103
|
+
|
104
|
+
if issubclass(typ, Enum): # if it raises here, the type_origin is unknown
|
105
|
+
return self.build_enum(name, typ, field, value, removable)
|
106
|
+
|
99
107
|
if issubclass(typ, BaseModel): # if it raises here, the type_origin is unknown
|
100
108
|
return self.build_model(name, typ, field, value or {}, removable)
|
101
109
|
|
@@ -228,6 +236,55 @@ class WidgetFactory:
|
|
228
236
|
removable=removable,
|
229
237
|
)
|
230
238
|
|
239
|
+
def build_set(
|
240
|
+
self,
|
241
|
+
field_name: str,
|
242
|
+
field_type: Type[Any],
|
243
|
+
field: Optional[FieldInfo],
|
244
|
+
value: Optional[Sequence[Any]],
|
245
|
+
removable: bool,
|
246
|
+
) -> Widget[Any]:
|
247
|
+
choice_wrapper = field_type.__args__[0]
|
248
|
+
choices = []
|
249
|
+
choice_wrapper_origin = get_origin(choice_wrapper)
|
250
|
+
if choice_wrapper_origin:
|
251
|
+
if choice_wrapper_origin is Literal:
|
252
|
+
litchoice: list[str] = choice_wrapper.__args__ # type: ignore
|
253
|
+
choices = [
|
254
|
+
Checkable(
|
255
|
+
label=c,
|
256
|
+
value=c,
|
257
|
+
checked=c in value if value else False, # type: ignore
|
258
|
+
name=field_name,
|
259
|
+
token=self.token,
|
260
|
+
)
|
261
|
+
for c in litchoice
|
262
|
+
]
|
263
|
+
|
264
|
+
else:
|
265
|
+
raise NotImplementedError
|
266
|
+
elif issubclass(choice_wrapper, Enum):
|
267
|
+
choices = [
|
268
|
+
Checkable(
|
269
|
+
label=e.value,
|
270
|
+
value=e.name,
|
271
|
+
checked=e.name in value if value else False, # type: ignore
|
272
|
+
name=field_name,
|
273
|
+
token=self.token,
|
274
|
+
)
|
275
|
+
for e in choice_wrapper
|
276
|
+
]
|
277
|
+
else:
|
278
|
+
raise NotImplementedError
|
279
|
+
|
280
|
+
return ChecklistWidget(
|
281
|
+
field_name,
|
282
|
+
title=field.title if field else "",
|
283
|
+
token=self.token,
|
284
|
+
value=choices,
|
285
|
+
removable=removable,
|
286
|
+
)
|
287
|
+
|
231
288
|
def build_boolean(
|
232
289
|
self,
|
233
290
|
field_name: str,
|
@@ -306,6 +363,24 @@ class WidgetFactory:
|
|
306
363
|
value=str(value),
|
307
364
|
)
|
308
365
|
|
366
|
+
def build_enum(
|
367
|
+
self,
|
368
|
+
field_name: str,
|
369
|
+
field_type: Type[Any], # an enum subclass
|
370
|
+
field: FieldInfo | None,
|
371
|
+
value: str | int | float,
|
372
|
+
removable: bool,
|
373
|
+
) -> Widget[Any]:
|
374
|
+
options = [(item.name, item.value) for item in field_type] # type: ignore
|
375
|
+
return DropDownWidget(
|
376
|
+
field_name,
|
377
|
+
options=options, # type: ignore
|
378
|
+
removable=removable,
|
379
|
+
title=field.title if field else "",
|
380
|
+
token=self.token,
|
381
|
+
value=str(value),
|
382
|
+
)
|
383
|
+
|
309
384
|
def build_simpletype(
|
310
385
|
self,
|
311
386
|
field_name: str,
|
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
|
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
|
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
|
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
|
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
|
{fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/__init__.py
RENAMED
File without changes
|
File without changes
|
{fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/dropdown.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{fastlifeweb-0.4.1 → fastlifeweb-0.5.0}/src/fastlife/templating/renderer/widgets/sequence.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|