Skip to content

Repository files navigation

NiceView

CI Docs

NiceView simplifies NiceGUI programming by deriving forms and tables from Pydantic or SqlModel models — the widget for each type, the layout, and validation against the model, shown inline at the field it belongs to and including cross-field rules. Persistence is a swappable adapter: a JSON file, a directory of files, SQL through SqlModel, or your own, with save, refresh, autosave and optimistic locking already wired up. The same model renders as a desktop table or as a mobile list ↔ detail drill-down. Inspired by declarative UI libraries like Django's admin integration, MagicGUI and NiceCRUD.

A ModelForm rendered from a Pydantic model — text, select, toggle, number, slider, switch, multi-select chips, color and textarea widgets
One ModelForm.from_item(...) call, rendered from a Pydantic model.

Installation

uv add git+https://github.com/clausgf/niceview          # or: pip install git+https://...
uv add "niceview[sqlmodel] @ git+https://github.com/clausgf/niceview"   # with SqlModelAdapter

SqlModelAdapter is the only component with an extra dependency (sqlmodel); everything else works with the base install. All public names are importable directly from niceview (from niceview import ModelForm, ModelGrid, ...).

Quick Start

import pydantic
from nicegui import ui
from niceview import ModelForm

class User(pydantic.BaseModel):
    name: str = pydantic.Field(default='', max_length=50, title='Name')
    age: int = pydantic.Field(default=0, ge=0, le=150)
    active: bool = True

user = User(name='Alice', age=30)

@ui.page('/')
def main():
    form = ModelForm.from_item(user)
    form.render()

ui.run()

API Design

NiceView follows a consistent factory pattern across all backends and UI components:

ModelForm (single item, fields only) EditFormWrapper (single item + chrome) ModelGrid / ModelGridInlineEdit (list)
In-memory ModelForm.from_item(Type, instance) EditFormWrapper.from_item(Type, instance) ModelGrid.from_list(Type, items)
EditGridWrapper.from_list(Type, items)
JSON file ModelForm.from_json(Type, path, lock_field=, created_field=) EditFormWrapper.from_json(Type, path, lock_field=, created_field=) ModelGrid.from_json(Type, path)
EditGridWrapper.from_json(Type, path)
Any adapter ModelForm.from_adapter(Type, adapter, key?) EditFormWrapper.from_adapter(Type, adapter, key?) ModelGrid.from_adapter(Type, adapter)
EditGridWrapper.from_adapter(Type, adapter)

All from_* methods accept the same keyword options; unknown keyword arguments raise TypeError instead of being silently ignored. All components follow the same create-then-render pattern: the factory returns the instance, render() draws it into the current NiceGUI context and returns the instance again, so the fluent one-liner X.from_list(...).render() always works.

Data adapters are the abstraction layer between UI components and storage backends. The from_* convenience methods create and hide the adapter; pass an adapter explicitly for full control or when using SQL / custom backends.

Without a model, render_field(field_info, value) renders a single widget from a niceview.Field() and field_value(widget, field_info) reads it back — the same widgets, styling, conversions and required handling a form uses, for fields your code decides at runtime instead of declaring as a class:

fi = niceview.Field(label='Name', widget_type='ui.input', required=True)
widget = niceview.render_field(fi, 'Alice')
...
name = niceview.field_value(widget, fi)

Components at a glance

Component Purpose
ModelForm A Pydantic model as an editable form (fields only, no chrome)
ModelGrid / ModelGridInlineEdit A list as a read-only or inline-editable AgGrid table
EditGridWrapper / EditFormWrapper Grid/form plus title, description and CRUD/action buttons
Card-based list editing One autosaving ModelForm per item, custom layout
ModelList / DrillDownWrapper Mobile-first list ↔ detail drill-down navigation
render_field / field_value One widget from one Field(), without a model

Screenshots

EditGridWrapper: a table with add, edit, delete and refresh buttons
EditGridWrapper — table with add / edit / delete / refresh
DrillDownWrapper: tapping a row slides to its detail form and back
DrillDownWrapper — mobile list ↔ detail drill-down

(Screenshots are regenerated with docs/screenshots/capture.py — see docs/img/README.md.)

Documentation

clausgf.github.io/niceview — the pages below as a searchable site, plus an API reference generated from the docstrings.

  • ComponentsModelForm (layout, validation), ModelGrid, the edit wrappers, card lists, ModelList/DrillDownWrapper, chrome styling, and the model-free render_field
  • Data Adapters — storage backends, lenient loading, optimistic locking, reactive updates, adapter protocols
  • Field Types & Customization — type→widget mapping, niceview.Field() options, Meta profiles, validation
  • Field Metadata Comparison — how niceview.Field(), NiceGUI widget options, pydantic.Field() and JSON Schema correspond, and where they deviate
  • Dialogsconfirm_dialog, input_dialog, submit_dialog
  • Concepts — how the chrome, field and text cascades fit together
  • DESIGN.md — design decisions and accepted technical debt
  • TODO.md — open questions and planned work
  • Changelog · License: MIT

Development

Install dependencies and run the checks:

uv sync --dev
uv run pytest
uv run mypy niceview/ --ignore-missing-imports
uv run ruff check

Build the documentation site (docs/ plus the generated API reference):

uv sync --group docs
uv run mkdocs serve            # preview on http://127.0.0.1:8000
uv run mkdocs build --strict   # what CI publishes; a broken link fails the build

Run examples (after uv sync --dev, which editable-installs niceview into .venv):

uv run python examples/01_form_basic.py

In VS Code, open the folder and pick the .venv interpreter (auto-detected; a .vscode/ config is included). Then just press the ▶ Run button — or F5 — with any example open; no sys.path setup is needed because niceview is installed into the venv.

Example Topic
01_form_basic.py ModelForm — basic usage
02_field_types.py ModelForm — all supported field types
03_form_binding.py ModelForm — NiceGUI binding
04_form_json.py ModelForm — JSON persistence
05_grid.py ModelGrid
06_edit_wrapper.py EditGridWrapper / EditFormWrapper
07_sqlmodel.py SqlModelAdapter — SQL-backed grid/form, relationships, optimistic locking
08_reactive_grid.py Reactive grid — auto-update via ObservableList
09_drilldown.py DrillDownWrapper / ModelList — embeddable list ↔ detail navigation
10_complex_form_navigation.py ModelForm in a responsive split-panel: side panel on desktop, full page on mobile
11_tree_navigation.py Multi-level tree navigation — URL factory, FilteredAdapter, Meta.profiles
12_card_list.py Card-based list editing — autosaving ModelForm per item, @model_validator, confirm_dialog
13_directory_drilldown.py DrillDownWrapper over DirectoryAdapter — one file per item, rename via a "Name" field
14_render_field.py render_field / field_value — a form built from field metadata, without a model
15_validation.py Validation — the three layers, item vs draft, cross-field rules, required, frozen
16_form_layout.py Form layout — rows, titled sections, per-field classes, uniform base_props
17_styling.py ChromeStyle / FieldStyle / ChromeText — styling presets and German texts, switchable live
18_form_actions.py Actions — FormAction, '@name' in the layout, chrome_actions in every wrapper's title row, list_actions/detail_actions for DrillDownWrapper's two views

Unit tests cover data adapters, field resolution, validation logic, and pure CRUD operations. Acceptance tests use the NiceGUI User fixture (headless, no browser) to verify render output and widget↔model interaction. AgGrid cell content is JS-rendered and not inspectable via the User fixture; row data is covered by unit tests instead.

Contributions are welcome — see CONTRIBUTING.md.

About

NiceGUI view generation from Pydantic and SqlModel

Resources

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages