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.2.0.tar.gz (121.7 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.2.0-py3-none-any.whl (63.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: spltz_viur_models-0.2.0.tar.gz
  • Upload date:
  • Size: 121.7 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.2.0.tar.gz
Algorithm Hash digest
SHA256 a997fee1f2b9a4ccc44839702797cfba696f4f97f29a0976c2f638a5fcfbce72
MD5 71b876856579adb8e06ea5c63404f0af
BLAKE2b-256 2f3577b13eb2bed55af42c8efb8295a0e4aebdbcb462c680281d828172262708

See more details on using hashes here.

Provenance

The following attestation bundles were made for spltz_viur_models-0.2.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.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for spltz_viur_models-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c2e50f2cb5eb740eadf399dd506852dbc5ff7e45b5e7a84a0990c923b4fdd343
MD5 1bd4de27787f40e7e57328b0e5fb50eb
BLAKE2b-256 2665e5159459dfd3cbb2d3647da72e3bb57b08f33d1b41e97842a1c1f56c041d

See more details on using hashes here.

Provenance

The following attestation bundles were made for spltz_viur_models-0.2.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

This release

0.2.0 This release

2 files

0.1.0

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