codeforms 0.2.0__py3-none-any.whl → 0.2.2__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.
- codeforms/__init__.py +25 -22
- codeforms/export.py +354 -101
- codeforms/fields.py +86 -38
- codeforms/forms.py +343 -318
- codeforms/i18n.py +1 -30
- codeforms/registry.py +43 -18
- codeforms-0.2.2.dist-info/METADATA +675 -0
- codeforms-0.2.2.dist-info/RECORD +10 -0
- {codeforms-0.2.0.dist-info → codeforms-0.2.2.dist-info}/WHEEL +1 -1
- codeforms-0.2.0.dist-info/METADATA +0 -325
- codeforms-0.2.0.dist-info/RECORD +0 -10
- {codeforms-0.2.0.dist-info → codeforms-0.2.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: codeforms
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Python library for creating, validating, and rendering web forms using Pydantic
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Requires-Python: >=3.9
|
|
8
|
-
Requires-Dist: pydantic[email]>=2.0
|
|
9
|
-
Provides-Extra: dev
|
|
10
|
-
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
|
|
13
|
-
# codeforms
|
|
14
|
-
|
|
15
|
-
A Python library for dynamically creating, validating, and rendering web forms using [Pydantic](https://docs.pydantic.dev/).
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
pip install codeforms
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Or with [uv](https://docs.astral.sh/uv/):
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
uv add codeforms
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Requires Python 3.9+.
|
|
30
|
-
|
|
31
|
-
## Quick Start
|
|
32
|
-
|
|
33
|
-
### Creating a Form
|
|
34
|
-
|
|
35
|
-
Everything starts with the `Form` class. A form is defined with a name and a list of fields.
|
|
36
|
-
|
|
37
|
-
```python
|
|
38
|
-
from codeforms import Form, TextField, EmailField, NumberField
|
|
39
|
-
|
|
40
|
-
form = Form(
|
|
41
|
-
name="UserRegistration",
|
|
42
|
-
fields=[
|
|
43
|
-
TextField(name="full_name", label="Full Name", required=True),
|
|
44
|
-
EmailField(name="email", label="Email", required=True),
|
|
45
|
-
NumberField(name="age", label="Age"),
|
|
46
|
-
]
|
|
47
|
-
)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### The `Form` Class
|
|
51
|
-
|
|
52
|
-
The `Form` class is the main container for your form structure.
|
|
53
|
-
|
|
54
|
-
- `id` — Auto-generated UUID.
|
|
55
|
-
- `name` — Form name (used in HTML export and validation).
|
|
56
|
-
- `fields` — A list of field objects (e.g. `TextField`, `EmailField`).
|
|
57
|
-
- `css_classes` — Optional CSS classes for the `<form>` tag.
|
|
58
|
-
- `version` — Form version number.
|
|
59
|
-
- `attributes` — Dictionary of additional HTML attributes for the `<form>` tag.
|
|
60
|
-
|
|
61
|
-
## Field Types
|
|
62
|
-
|
|
63
|
-
All fields inherit from `FormFieldBase` and share these common attributes:
|
|
64
|
-
|
|
65
|
-
- `name` — Field name (maps to `name` in HTML).
|
|
66
|
-
- `label` — User-visible label.
|
|
67
|
-
- `field_type` — Field type (`FieldType` enum).
|
|
68
|
-
- `required` — Whether the field is mandatory.
|
|
69
|
-
- `placeholder` — Placeholder text inside the field.
|
|
70
|
-
- `default_value` — Default value.
|
|
71
|
-
- `help_text` — Help text displayed below the field.
|
|
72
|
-
- `css_classes` — CSS classes for the field element.
|
|
73
|
-
- `readonly` — Whether the field is read-only.
|
|
74
|
-
- `attributes` — Additional HTML attributes for the `<input>` tag.
|
|
75
|
-
|
|
76
|
-
### Available Fields
|
|
77
|
-
|
|
78
|
-
- **`TextField`** — Generic text input (`<input type="text">`).
|
|
79
|
-
- `minlength`, `maxlength`: Min/max text length.
|
|
80
|
-
- `pattern`: Regex pattern for validation.
|
|
81
|
-
- **`EmailField`** — Email address (`<input type="email">`).
|
|
82
|
-
- **`NumberField`** — Numeric value (`<input type="number">`).
|
|
83
|
-
- `min_value`, `max_value`: Allowed value range.
|
|
84
|
-
- `step`: Increment step.
|
|
85
|
-
- **`DateField`** — Date picker (`<input type="date">`).
|
|
86
|
-
- `min_date`, `max_date`: Allowed date range.
|
|
87
|
-
- **`SelectField`** — Dropdown select (`<select>`).
|
|
88
|
-
- `options`: List of `SelectOption(value="...", label="...")`.
|
|
89
|
-
- `multiple`: Enables multi-select.
|
|
90
|
-
- `min_selected`, `max_selected`: Selection count limits (multi-select only).
|
|
91
|
-
- **`RadioField`** — Radio buttons (`<input type="radio">`).
|
|
92
|
-
- `options`: List of `SelectOption`.
|
|
93
|
-
- `inline`: Display options inline.
|
|
94
|
-
- **`CheckboxField`** — Single checkbox (`<input type="checkbox">`).
|
|
95
|
-
- **`CheckboxGroupField`** — Group of checkboxes.
|
|
96
|
-
- `options`: List of `SelectOption`.
|
|
97
|
-
- `inline`: Display options inline.
|
|
98
|
-
- **`FileField`** — File upload (`<input type="file">`).
|
|
99
|
-
- `accept`: Accepted file types (e.g. `"image/*,.pdf"`).
|
|
100
|
-
- `multiple`: Allow multiple file uploads.
|
|
101
|
-
- **`HiddenField`** — Hidden field (`<input type="hidden">`).
|
|
102
|
-
|
|
103
|
-
## Data Validation
|
|
104
|
-
|
|
105
|
-
codeforms offers multiple ways to validate user-submitted data, leveraging Pydantic's validation engine.
|
|
106
|
-
|
|
107
|
-
### Recommended: `FormDataValidator`
|
|
108
|
-
|
|
109
|
-
The most robust approach is `FormDataValidator.create_model`, which dynamically generates a Pydantic model from your form definition. This gives you powerful validations and detailed error messages automatically.
|
|
110
|
-
|
|
111
|
-
```python
|
|
112
|
-
from codeforms import Form, FormDataValidator, TextField, SelectField, SelectOption
|
|
113
|
-
from pydantic import ValidationError
|
|
114
|
-
|
|
115
|
-
# 1. Define your form
|
|
116
|
-
form = Form(
|
|
117
|
-
name="MyForm",
|
|
118
|
-
fields=[
|
|
119
|
-
TextField(name="name", label="Name", required=True),
|
|
120
|
-
SelectField(
|
|
121
|
-
name="country",
|
|
122
|
-
label="Country",
|
|
123
|
-
options=[
|
|
124
|
-
SelectOption(value="us", label="United States"),
|
|
125
|
-
SelectOption(value="uk", label="United Kingdom"),
|
|
126
|
-
]
|
|
127
|
-
)
|
|
128
|
-
]
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
# 2. Create the validation model
|
|
132
|
-
ValidationModel = FormDataValidator.create_model(form)
|
|
133
|
-
|
|
134
|
-
# 3. Validate incoming data
|
|
135
|
-
user_data = {"name": "John", "country": "us"}
|
|
136
|
-
|
|
137
|
-
try:
|
|
138
|
-
validated = ValidationModel.model_validate(user_data)
|
|
139
|
-
print("Valid!", validated)
|
|
140
|
-
except ValidationError as e:
|
|
141
|
-
print("Validation errors:", e.errors())
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
This approach integrates seamlessly with API backends like FastAPI or Flask, since it produces standard Pydantic models.
|
|
145
|
-
|
|
146
|
-
### Other Validation Methods
|
|
147
|
-
|
|
148
|
-
Two simpler alternatives exist, though `FormDataValidator` is preferred:
|
|
149
|
-
|
|
150
|
-
1. `form.validate_data(data)` — Built-in method on the `Form` class. Less flexible; doesn't produce Pydantic models.
|
|
151
|
-
2. `validate_form_data(form, data)` — Standalone function with basic validation logic.
|
|
152
|
-
|
|
153
|
-
## Exporting Forms
|
|
154
|
-
|
|
155
|
-
Once your form is defined, you can export it to different formats.
|
|
156
|
-
|
|
157
|
-
```python
|
|
158
|
-
# Export to plain HTML
|
|
159
|
-
html_output = form.export('html', submit=True)
|
|
160
|
-
print(html_output['output'])
|
|
161
|
-
|
|
162
|
-
# Export to HTML with Bootstrap 5 classes
|
|
163
|
-
bootstrap_output = form.export('html_bootstrap5', submit=True)
|
|
164
|
-
print(bootstrap_output['output'])
|
|
165
|
-
|
|
166
|
-
# Export to JSON
|
|
167
|
-
json_output = form.to_json()
|
|
168
|
-
print(json_output)
|
|
169
|
-
|
|
170
|
-
# Export to a Python dictionary
|
|
171
|
-
dict_output = form.to_dict()
|
|
172
|
-
print(dict_output)
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Supported Formats
|
|
176
|
-
|
|
177
|
-
| Format | Description |
|
|
178
|
-
|---|---|
|
|
179
|
-
| `html` | Semantic HTML |
|
|
180
|
-
| `html_bootstrap4` | HTML with Bootstrap 4 classes |
|
|
181
|
-
| `html_bootstrap5` | HTML with Bootstrap 5 classes |
|
|
182
|
-
| `json` | JSON representation of the form |
|
|
183
|
-
| `dict` | Python dictionary representation |
|
|
184
|
-
|
|
185
|
-
HTML export can also generate a `<script>` block for basic client-side validation.
|
|
186
|
-
|
|
187
|
-
## Internationalization (i18n)
|
|
188
|
-
|
|
189
|
-
All validation and export messages are locale-aware. **English** (`en`) and **Spanish** (`es`) are included out of the box, and you can register any additional language at runtime via `register_locale()`.
|
|
190
|
-
|
|
191
|
-
### Switching Locales
|
|
192
|
-
|
|
193
|
-
```python
|
|
194
|
-
from codeforms import set_locale, get_locale, get_available_locales
|
|
195
|
-
|
|
196
|
-
print(get_locale()) # "en"
|
|
197
|
-
print(get_available_locales()) # ["en", "es"]
|
|
198
|
-
|
|
199
|
-
set_locale("es")
|
|
200
|
-
# All validation messages will now be in Spanish
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### Registering a Custom Locale
|
|
204
|
-
|
|
205
|
-
You can add any locale at runtime. Missing keys automatically fall back to English.
|
|
206
|
-
|
|
207
|
-
```python
|
|
208
|
-
from codeforms import register_locale, set_locale
|
|
209
|
-
|
|
210
|
-
register_locale("pt", {
|
|
211
|
-
"field.required": "Este campo é obrigatório",
|
|
212
|
-
"field.required_named": "O campo {name} é obrigatório",
|
|
213
|
-
"email.invalid": "E-mail inválido",
|
|
214
|
-
"number.min_value": "O valor deve ser maior ou igual a {min}",
|
|
215
|
-
"form.validation_success": "Dados validados com sucesso",
|
|
216
|
-
"form.data_validation_error": "Erro na validação dos dados",
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
set_locale("pt")
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Using the Translation Function
|
|
223
|
-
|
|
224
|
-
The `t()` function translates a message key, with optional interpolation:
|
|
225
|
-
|
|
226
|
-
```python
|
|
227
|
-
from codeforms import t, set_locale
|
|
228
|
-
|
|
229
|
-
set_locale("en")
|
|
230
|
-
print(t("field.required")) # "This field is required"
|
|
231
|
-
print(t("field.required_named", name="email")) # "The field email is required"
|
|
232
|
-
|
|
233
|
-
set_locale("es")
|
|
234
|
-
print(t("field.required")) # "Este campo es requerido"
|
|
235
|
-
print(t("text.minlength", min=3)) # "La longitud mínima es 3"
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Locale-Aware Validation
|
|
239
|
-
|
|
240
|
-
All validation functions respect the active locale:
|
|
241
|
-
|
|
242
|
-
```python
|
|
243
|
-
from codeforms import Form, TextField, validate_form_data, set_locale
|
|
244
|
-
|
|
245
|
-
form = Form(
|
|
246
|
-
name="example",
|
|
247
|
-
fields=[TextField(name="name", label="Name", required=True)]
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
set_locale("en")
|
|
251
|
-
result = validate_form_data(form, {})
|
|
252
|
-
print(result["errors"][0]["message"]) # "The field name is required"
|
|
253
|
-
|
|
254
|
-
set_locale("es")
|
|
255
|
-
result = validate_form_data(form, {})
|
|
256
|
-
print(result["errors"][0]["message"]) # "El campo name es requerido"
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
See [`examples/i18n_usage.py`](examples/i18n_usage.py) for a full working example.
|
|
260
|
-
|
|
261
|
-
## Custom Field Types
|
|
262
|
-
|
|
263
|
-
You can create your own field types by subclassing `FormFieldBase` and registering them with `register_field_type()`. Custom fields integrate seamlessly with forms, JSON serialization, validation, and HTML export.
|
|
264
|
-
|
|
265
|
-
### Defining a Custom Field
|
|
266
|
-
|
|
267
|
-
```python
|
|
268
|
-
from codeforms import FormFieldBase, register_field_type
|
|
269
|
-
|
|
270
|
-
class PhoneField(FormFieldBase):
|
|
271
|
-
field_type: str = "phone" # unique string identifier
|
|
272
|
-
country_code: str = "+1"
|
|
273
|
-
|
|
274
|
-
class RatingField(FormFieldBase):
|
|
275
|
-
field_type: str = "rating"
|
|
276
|
-
min_rating: int = 1
|
|
277
|
-
max_rating: int = 5
|
|
278
|
-
|
|
279
|
-
register_field_type(PhoneField)
|
|
280
|
-
register_field_type(RatingField)
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### Using Custom Fields in Forms
|
|
284
|
-
|
|
285
|
-
```python
|
|
286
|
-
from codeforms import Form, TextField
|
|
287
|
-
|
|
288
|
-
form = Form(
|
|
289
|
-
name="feedback",
|
|
290
|
-
fields=[
|
|
291
|
-
TextField(name="name", label="Name", required=True),
|
|
292
|
-
PhoneField(name="phone", label="Phone", country_code="+54"),
|
|
293
|
-
RatingField(name="score", label="Score", max_rating=10),
|
|
294
|
-
],
|
|
295
|
-
)
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
### JSON Roundtrip
|
|
299
|
-
|
|
300
|
-
Custom fields serialize and deserialize automatically (as long as the field type is registered before deserialization):
|
|
301
|
-
|
|
302
|
-
```python
|
|
303
|
-
import json
|
|
304
|
-
|
|
305
|
-
json_str = form.to_json()
|
|
306
|
-
restored = Form.loads(json_str)
|
|
307
|
-
|
|
308
|
-
assert isinstance(restored.fields[1], PhoneField)
|
|
309
|
-
assert restored.fields[1].country_code == "+54"
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### Listing Registered Types
|
|
313
|
-
|
|
314
|
-
```python
|
|
315
|
-
from codeforms import get_registered_field_types
|
|
316
|
-
|
|
317
|
-
for name, classes in sorted(get_registered_field_types().items()):
|
|
318
|
-
print(f"{name}: {[c.__name__ for c in classes]}")
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
See [`examples/custom_fields.py`](examples/custom_fields.py) for a full working example.
|
|
322
|
-
|
|
323
|
-
## License
|
|
324
|
-
|
|
325
|
-
MIT
|
codeforms-0.2.0.dist-info/RECORD
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
codeforms/__init__.py,sha256=ckxP8Bn2lku8t6i5UspQNsWsrGw2KAcRyK1mxbgVHoI,1643
|
|
2
|
-
codeforms/export.py,sha256=ygY-bXEerr9bpG0isq1Qesf8F_FtKupgElqQnNcbI0o,15088
|
|
3
|
-
codeforms/fields.py,sha256=e_lGX9XPo1uqTSA490CfGPLwIp8uJzcipv1jqAZNqOo,12819
|
|
4
|
-
codeforms/forms.py,sha256=_wmJ1cDrZCklsx4H12oQ6SlBkDp053Jj7IurpYzudhQ,35766
|
|
5
|
-
codeforms/i18n.py,sha256=p8DPtnvUNMeXXP4YDhvpmIJFQNqOmwK3dEluS6Uch0k,10441
|
|
6
|
-
codeforms/registry.py,sha256=jKpOnvn5fq8x1LkO3va4F-VgruMMXoh-6FUTA-q43p8,6477
|
|
7
|
-
codeforms-0.2.0.dist-info/METADATA,sha256=hUPDRa7k_siVkny9sbU8MT4yMpanRrYyMOVXYg7kUEM,9772
|
|
8
|
-
codeforms-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
9
|
-
codeforms-0.2.0.dist-info/licenses/LICENSE,sha256=U9D6cQ9DZJQrT03MMDJcq-aOuoB_Bn8HJlzcTTN7PfM,1074
|
|
10
|
-
codeforms-0.2.0.dist-info/RECORD,,
|
|
File without changes
|