WTForms form generator and renderer for UOFastORM-backed UniData/U2 models
Project description
UOFastForms
WTForms form generator and renderer for UOFastORM-backed UniData/U2 models.
A RokiPark open-source project.
UOFastForms automatically builds WTForms Form classes from your UopyModel field metadata, handles U2 multi-value (MV) data, integrates lookup popups, and supports server-side validation via cataloged U2 BASIC subroutines — all with Bootstrap 5-ready Jinja2 macros.
Features
- Auto-generated forms from
UopyModel._field_mapand_mv_fieldsmetadata - FieldConfig enrichment — override types, labels, validators, choices, and render attributes per field
- Multi-value field support —
MVTextAreaField(newline-separated) andMVFieldList(dynamic repeater) - Lookup popups — declarative
LookupModelgeneratesdata-lookup-*HTML attributes for JS integration - Server-side validation —
RemoteValidatorcalls cataloged U2 BASIC subroutines and maps errors back to form fields - Flask Blueprint —
register_forms(app)wires up Jinja2 template globals in one line - Bootstrap 5 macros —
render_form,render_field,render_mv_fieldlist,render_form_errors
Installation
pip install uofastforms
With Flask rendering support:
pip install uofastforms[flask]
With U2 server-side validation:
pip install uofastforms[all]
Quick Start
1. Define a FormModel
from uofastforms import FormModel, FieldConfig, RemoteValidator, register_forms
from wtforms.validators import DataRequired, Length
class ClientFormModel(FormModel):
model = ClientModel # your UopyModel subclass
remote_validator = RemoteValidator(
subroutine_name="VALIDATE.CLIENT",
arg_count=3,
input_arg=0,
output_arg=1,
fatal_arg=2,
session_factory=lambda: uopy.connect(**cfg),
)
fields = {
"company": FieldConfig(label="Company", required=True,
validators=[Length(max=100)]),
"fname": FieldConfig(label="First Name"),
"lname": FieldConfig(label="Last Name"),
"phone": FieldConfig(label="Phones", description="One per line"),
"contact_ids": FieldConfig(exclude=True),
}
2. Register with Flask (once, in app factory)
from uofastforms import register_forms
def create_app():
app = Flask(__name__)
register_forms(app)
return app
3. Use in a view
@bp.route('/clients/<id>/edit', methods=['GET', 'POST'])
def edit_client(id):
client = ClientModel(session, id)
form = ClientFormModel.get_form(
obj=client if request.method == 'GET' else None,
data=request.form if request.method == 'POST' else None,
)
if request.method == 'POST' and form.validate():
ClientFormModel.populate_obj(form, client)
client.update()
return redirect(url_for('.list'))
return render_template('client_edit.html', form=form)
4. Render in a Jinja2 template
{% from 'uofast_forms/form_macros.html' import render_form %}
{{ render_form(form, action=url_for('.edit_client', id=client.record_id)) }}
API Reference
FormModel
Base class for all form definitions. Subclass it and declare model, fields, and optionally remote_validator.
| Class attribute | Type | Description |
|---|---|---|
model |
type[UopyModel] |
Required. The UopyModel subclass this form is based on. |
fields |
dict[str, FieldConfig] |
Per-field enrichment overrides. |
remote_validator |
RemoteValidator |
Optional. Runs after local validation. |
Class methods:
FormModel.get_form_class(base_class=Form) -> type[Form]
Returns (and caches) the generated WTForms Form subclass.
FormModel.get_form(obj=None, data=None, base_class=Form, **kwargs) -> Form
Creates a form instance, optionally pre-populated from a UopyModel instance (obj) or raw POST data (data).
FormModel.populate_obj(form, obj) -> None
Writes validated form data back to a UopyModel instance.
FormModel.get_lookup_attrs(prop_name) -> dict[str, str]
Returns data-lookup-* HTML attributes for a field that has a LookupModel or linked_file configured.
FieldConfig
Per-field enrichment dataclass.
@dataclass
class FieldConfig:
field_type: str | None = None # "string", "integer", "float", "boolean",
# "date", "datetime", "select", "textarea",
# "email", "url", "password", "hidden"
label: str | None = None # Human-readable label (auto-prettified if None)
description: str | None = None # Help text shown below the field
required: bool = False # Prepends DataRequired validator
validators: list = field(default_factory=list)
choices: list | None = None # [(value, label), ...] for SelectField
render_kw: dict = field(default_factory=dict)
exclude: bool = False # Omit this field from the form entirely
mv_style: str = "textarea" # "textarea" or "fieldlist" for MV fields
default: Any = None
linked_file: str | None = None # U2 file name for lookup popup
linked_display_field: str | None = None
linked_fill: dict | None = None # {db_field: form_prop} auto-fill map
lookup: LookupModel | None = None # Full LookupModel (overrides linked_* attrs)
LookupModel
Declarative configuration for field search-and-select popups.
class ProductLookup(LookupModel):
model = ProductModel
display_fields = ["code", "description", "price"]
search_field = "description"
fill = {"code": "product_code", "description": "product_desc"}
Use with FieldConfig(lookup=ProductLookup). The get_lookup_attrs() method returns the full data-lookup-* attribute dict for HTML rendering.
RemoteValidator
WTForms validator that calls a cataloged U2 BASIC subroutine for server-side business-rule validation.
RemoteValidator(
subroutine_name="VALIDATE.CLIENT",
arg_count=3,
input_arg=0, # index of arg receiving JSON form data
output_arg=1, # index of arg returning JSON error dict
fatal_arg=2, # index of arg returning a fatal error string
session_factory=..., # callable returning a live uopy.Session
field_name_map=None, # optional {subroutine_name: form_prop_name} mapping
)
Subroutine contract:
| Arg index | Direction | Content |
|---|---|---|
input_arg |
IN | JSON string of form data, e.g. {"company": "Acme", "phone": ["555-1234"]} |
output_arg |
OUT | JSON string of field errors, e.g. {"company": "Name already taken"} or "" |
fatal_arg |
OUT | Fatal (non-field) error string; empty string = success |
MVTextAreaField / MVFieldList
WTForms field types for U2 multi-value data.
MVTextAreaField— Renders as a<textarea>with one value per line. Useto_list()/populate_from_list(values)for programmatic access.MVFieldList— Renders as a dynamic repeater (requires companion JS). Usemake_mv_fieldlist(field_type, label, ...)factory to create instances.
Flask Integration
from uofastforms import register_forms, forms_bp
# Option A — app factory helper (recommended)
register_forms(app)
# Option B — manual Blueprint registration
app.register_blueprint(forms_bp)
register_forms also adds uofast_form_errors(form) as a Jinja2 global — a helper that returns a flat list of (field_label, error_message) tuples.
Jinja2 Macros
Import from uofast_forms/form_macros.html:
{% from 'uofast_forms/form_macros.html' import render_form, render_field,
render_mv_fieldlist, render_form_errors %}
| Macro | Description |
|---|---|
render_form(form, action, method, submit_label, css_class, enctype) |
Full <form> with all fields and submit button |
render_field(field, label_class, input_class, wrapper_class) |
Single Bootstrap 5 form-group |
render_mv_fieldlist(field, add_label, input_class, wrapper_class) |
Dynamic MV repeater |
render_form_errors(form) |
Dismissable Bootstrap 5 alert listing all errors |
All macros use Bootstrap 5 CSS classes. Browser-native validation is disabled (novalidate) — WTForms and RemoteValidator handle all validation.
Requirements
| Dependency | Required | Notes |
|---|---|---|
WTForms >= 3.0 |
Yes | Core form framework |
Flask >= 2.0 |
Optional | Required for register_forms, forms_bp, and macros |
uopy |
Optional | Required for RemoteValidator |
UOFastORM |
Runtime | UopyModel subclasses must come from your UOFastORM models |
License
MIT — see LICENSE.
Maintained by RokiPark · github.com/RokiPark/UOFastForms
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uofastforms-0.1.0.tar.gz.
File metadata
- Download URL: uofastforms-0.1.0.tar.gz
- Upload date:
- Size: 24.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0e55974e9348953a6bc4783307967ad08fb74cdcba84ed359e6e10414d46aa4
|
|
| MD5 |
59014941b254cc5df5411bf237caaeed
|
|
| BLAKE2b-256 |
2eb5b98dc73e837f8382f617e5a4f9d939a5f7ff3180b411cdd18953807ff052
|
File details
Details for the file uofastforms-0.1.0-py3-none-any.whl.
File metadata
- Download URL: uofastforms-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bdd9be380559914ef667b3eae958d90bf8c0602dec5701b3ea0a5218f318b22f
|
|
| MD5 |
4b362956656c789cf831d2582af654ff
|
|
| BLAKE2b-256 |
4b8f154335717e3750b8b83e3a1d4521b521638f3c846f2577c9b32031fb9507
|