Skip to main content

viur-models

SQLModel definitions for ViUR applications.

Tests License: MIT

Status

Alpha. Implemented: Field, the bone-type field types (Text, Email, Country, + BoneType/register_bone_type for your own), the skeleton-compatible structure/dump emission, fromClient error mapping, opaque key encoding — the SQLList module prototype (list/view/add/edit/delete/structure over envelope v2, hooks from viur-actions, session-per-action) — and schema migrations (viur.models.migrations, Alembic) with automatically generated bone-level data migrations. All of it is verified against the real viur-core (and the real envelope renderer) by the integration suite. See analysis/ for the design and CHANGELOG.md for the running summary.

Requirements

  • Python ≥ 3.12
  • viur-core ≥ 3.8, < 4

Install

pip install spltz-viur-models

Quick taste

from viur.models import Country, Email, Text, Field, Model

class Feedback(Model, table=True):        # a plain SQLModel underneath
    name: str = Field(descr="Name", max_length=100)
    mail: Email = Field(descr="E-Mail")   # bone type "str.email"
    message: Text = Field(default="")     # bone type "text"
    country: Country | None = Field(default=None)  # "select.country"

Feedback.viur_structure()   # skeleton-compatible structure dict
Feedback(id=42).viur_key    # opaque key string, like a datastore key

The bone type is decided by the Python typestr, int, bool, datetime, enum/Literal map automatically; semantic types come from the pydantic ecosystem (e.g. Country is pydantic-extra-types' CountryAlpha2) or are one Annotated alias away:

Slug = t.Annotated[str, BoneType("str.slug")]

The bone-by-bone mapping (skeleton declaration vs. field equivalent) is documented in docs/bones.md.

Serve a model like a skeleton module — same endpoints, same envelope-v2 wire format:

# deploy/modules/feedback.py
from viur.models.sqllist import SQLList
from models.feedback import Feedback

class feedback(SQLList):
    model = Feedback

    def can(self, instance):     # fail-closed by default; open up per
        return True              # action via canView/canEdit/… overrides

# deploy/main.py — two calls, one either side of core.setup():
import viur.models

viur.models.install(engine="postgres",              # NullPool on App Engine
                    postgres_dsn="postgresql+pg8000://…")
app = core.setup(modules, render)
viur.models.setup()                                 # needs the models imported

install() builds the engine and wires the cross-store refresh hooks; setup() reports the schema state (and bootstraps it for the in-memory preset). Details in docs/getting-started.md.

Migrations

create_all() bootstraps a schema but never changes one — it silently ignores every altered column, removed field and changed type. Install the extra and let Alembic do it:

pip install "spltz-viur-models[migrations]"

The scaffold is generated on the dev server — one argument, no alembic init:

# deploy/main.py, after core.setup()
viur.models.setup(migrations=PROJECT_ROOT)

It writes only what is missing (hand edits survive), is inert on a deployed instance, and puts the first revision in place right away: a fresh database gets upgrade head, one that already has the tables gets stamped with a revision autogenerated against an empty probe database — so nothing is dropped and the history still describes the full schema.

The result lives next to the distribution folder, not inside it — Alembic is build-time tooling and has no business being uploaded with the app:

myproject/
  alembic.ini           # prepend_sys_path = %(here)s/deploy
  migrations/
    env.py              # 4 lines: import_models() + run()
    versions/           # the revisions — commit these, they are code
  deploy/               # what actually gets deployed
alembic revision --autogenerate -m "add slug to entry"
alembic upgrade head
alembic check          # CI gate: do models and schema still agree?

Bone-level changes bring their own data migration. A skeleton project just edits the bone and viur-core coerces on read; in SQL the same change moves data, and Alembic would drop the link table first. Eight transitions are detected and generated — following viur-core's own rules:

def upgrade() -> None:
    op.reduce_languages("post", "title", new_type=sa.String(200),
                        languages=["de", "en"], keep="de")
    op.collapse_multiple("post", link_table="post_tag", target_column="tag_id",
                         link_parent_fk="post_id", link_dest_fk="tag_id",
                         foreign_table="tag", keep="first")   # = loadVal[0]

multiple ↔ single, multilingual ↔ plain, strText, numeric precision, selectbool, new/removed fields and new using payload columns. Only boolselect needs a line from you — viur-core has no rule there, so the generator emits a stub that refuses to run until the mapping is filled. Detection runs on structure snapshots, not on Alembic's DDL diff, which cannot see strText at all.

The module also handles what a stock Alembic scaffold does not: resolving the database URL without booting viur-core, rendering custom TypeDecorator columns (RecordJSON) as their DDL type so revisions stay frozen snapshots, and SQLite's batch mode. Details in docs/migrations.md, design rationale in analysis/03.

Development

Two test layers, run separately:

Unit (fast, mocked) — tests/, 100 % coverage gate:

git clone https://github.com/sprengplatz/viur-models
cd viur-models
pip install --no-deps -e .
pip install pytest pytest-cov 'coverage[toml]' 'spltz-viur-light-mock>=0.3,<1.0' 'spltz-viur-actions>=0.4,<1.0' sqlmodel pydantic-extra-types pycountry email-validator 'alembic>=1.13'
pytest                  # 100% coverage required

viur-light-mock provides the viur.core.* stand-ins so these run without the App Engine stack.

Integration (real core) — integration/, no coverage gate:

pip install "viur-core>=3.8,<4" rsa pytest 'spltz-viur-actions>=0.4,<1.0' sqlmodel pydantic-extra-types pycountry email-validator
pip install --no-deps -e .
python -m pytest -c integration/pytest.ini integration

Runs against the real framework — the layer that catches mock-vs-core drift. See integration/README.md.

Coverage policy. The 100 % gate applies to the unit layer only. The integration layer runs without a coverage requirement — a coverage target there would pressure mocking the very framework it exists to exercise.

Documentation

sprengplatz.github.io/viur-models — available in English and German (/de/).

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spltz_viur_models-0.1.0.tar.gz (118.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spltz_viur_models-0.1.0-py3-none-any.whl (62.2 kB view details)

Uploaded Python 3

File details

Details for the file spltz_viur_models-0.1.0.tar.gz.

File metadata

  • Download URL: spltz_viur_models-0.1.0.tar.gz
  • Upload date:
  • Size: 118.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for spltz_viur_models-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3526860d04361687ca631c76bafee8b89e07bb81664f8407284812b7aab86351
MD5 610f8258a206eb25bab2685ba5528522
BLAKE2b-256 63d8e3f194d2026ed440dcd62a5041395aa5fb78d4825d60f1a191eab693e191

See more details on using hashes here.

Provenance

The following attestation bundles were made for spltz_viur_models-0.1.0.tar.gz:

Publisher: release.yml on sprengplatz/viur-models

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file spltz_viur_models-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for spltz_viur_models-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c08bdb721167535875f4ec50e4327ee4b786587de999a9422228f0364241cb81
MD5 5879a6719c4ff64e9fd57d956c564589
BLAKE2b-256 2db896148bc7b43c78f4805db8be2db81da097304f92f458cf1d1144c4bc7cee

See more details on using hashes here.

Provenance

The following attestation bundles were made for spltz_viur_models-0.1.0-py3-none-any.whl:

Publisher: release.yml on sprengplatz/viur-models

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page