Zum Inhalt

Bone-Übersicht

Kompakte Referenz, wie jede viur-core-Bone als viur-models-Feld definiert wird. Grundregel: Der Python-Typ bestimmt die Bone — nie ein String-Parameter. Alles, was Pydantic/SQL schon ausdrückt (max_length, ge/le, Optionalität, Defaults), wird abgeleitet; nur ViUR-Spezifisches (descr, visible, params, …) geht durch Field. Optionalität wie üblich per | None + default=None. Alle Ableitungsregeln im Detail: Bone-Referenz; lauffähiges Beispiel: deploy/models/example.py.

Gesamtübersicht

Skalare

Bone Type-String Feld-Definition
StringBone str name: str = Field(descr="Name", max_length=100)
NumericBone (int) numeric rating: int \| None = Field(default=None, ge=1, le=5)
NumericBone (float/Decimal) numeric price: Decimal \| None = Field(default=None, decimal_places=2)
BooleanBone bool active: bool = Field(default=True)
DateBone date due: datetime \| None · day: date \| None · slot: time \| None

Selects

Bone Type-String Feld-Definition
SelectBone (Enum) select kind: EntryKind = Field(descr="Art")
SelectBone (Literal) select status: Literal["new","done"] \| None = Field(values={…}, sa_type=String)
SelectCountryBone select.country country: Country \| None = Field(default=None)

String-Verfeinerungen (der Typ trägt die Bone)

Bone Type-String Feld-Definition
TextBone text message: Text = Field(default="", required=False)
EmailBone str.email mail: Email \| None = Field(default=None)
PhoneBone str.phone phone: Phone \| None = Field(default=None, max_length=15)
UriBone uri site: Uri \| None = Field(default=None)
ColorBone color tint: Color \| None = Field(default=None)
RawBone raw blob: Raw \| None = Field(default=None)
CodeBone/Jinja/Logics/Python raw.code template: Code \| None = Field(default=None)
JsonBone raw.json data: Json \| None = Field(default=None, sa_type=JSON)
UidBone uid uid: Uid \| None = Field(default=None) — ⚠️ Wert-Generierung im Hook
SortIndexBone numeric.sortindex sortindex: SortIndex \| None = Field(default=None)
CredentialBone str.credential secret: Credential \| None = Field(default=None, visible=False) — write-only
PasswordBone password pwd: Password \| None = Field(default=None) — write-only, ⚠️ Hashing im Hook
SpatialBone spatial pos: Spatial(bounds_lat=…, bounds_lng=…) \| None = Field(sa_type=JSON)

Mehrsprachigkeit

Bone Feld-Definition
StringBone(languages=…) title: Language[str] = Field(languages=("de","en"), sa_type=JSON)
TextBone(languages=…) body: Language[Text] \| None = Field(default=None, sa_type=JSON)

Sprachliste per languages= oder projektweit set_default_languages("de", "en"). Input dotted (title.de=…) und als Dict; Wert ist ein {lang: value}-Dict.

Relationen (SQL → SQL)

Bone Feld-Definition
RelationalBone FK-Feld (trägt die Bone-Parameter) + Relationship():
category_id: int \| None = Field(default=None, foreign_key="example_category.id", descr="Kategorie")
category: ExampleCategory \| None = Relationship()
RelationalBone(multiple=True) tags: list[ExampleTag] = Relationship(link_model=EntryTagLink)
RelationalBone(using=RelSkel) Association-Object — die Payload-Spalten der Link-Table SIND das using-RelSkel:
class EntryTagLink(RelationLink, table=True): … tag: ExampleTag = Relationship(); weight: int = Field(ge=0, le=10)
tags: list[EntryTagLink] = Relationship(sa_relationship_kwargs={"cascade": "all, delete-orphan"})
RecordBone / AddressBone pures Pydantic-Nesting: class Address(Record): …
address: Address \| None = Field(default=None, sa_type=RecordJSON(Address), format="$(street)")
multiple: stops: list[Address] = Field(default_factory=list, sa_type=RecordJSON(Address))

required leitet sich aus der FK-Nullability ab; viur_ref_keys = ("name",) am Ziel bestimmt relskel/dest. Bei multiple/using ist cascade="all, delete-orphan" Pflicht.

Cross-Store (SQL → Datastore-Skeleton)

Gespeichert wird der dest-Snapshot (Key + ref_keys-Werte) als JSON; der Input-Read baut den Snapshot neu und ist der Existenz-Check. Gelöschte Ziele blockieren Edits nicht (der alte Snapshot bleibt); Änderungen an referenzierten Skeletons propagieren automatisch: einmal viur.models.install_refresh_hooks() beim Boot (wrappt die Skeleton-postSaved/postDeleted-Handler und deferred den gezielten refresh_for_target über die Index-Tabelle viur_models_relations — das viur-relations-Äquivalent; Löschungen defaulten auf set_null). Flächig gibt es refresh_crossstore(Model, missing=…) als Cron-Sicherheitsnetz.

Bone Feld-Definition
UserBone owner: UserRef() \| None = Field(default=None, sa_type=JSON)
FileBone/ImageBone upload: FileRef() \| None = Field(default=None, sa_type=JSON) — Referenz, kein Upload
TreeLeafBone/TreeNodeBone node: SkeletonRef(kind, type_suffix="tree.node") \| None
beliebiges Skeleton ref: SkeletonRef("feedback", ref_keys=("subject",)) \| None = Field(sa_type=JSON)
multiple (JSON-Array) refs: SkeletonRef("feedback", multiple=True) \| None = Field(sa_type=JSON)
multiple (Link-Table) class FbLink(SkeletonLink, table=True): viur_kind = "feedback"; …
history: list[FbLink] = Relationship(sa_relationship_kwargs={"cascade": "all, delete-orphan"})

System & Sonstiges

Bone Feld-Definition
KeyBone / creationdate / changedate automatisch aus der Model-Basis — nichts deklarieren
BaseBone („hidden") Hidden = Annotated[str, BoneType("hidden", replace=True)]
CaptchaBone / SpamBone Request-Zeit-Prüfung, keine Persistenz — nicht anwendbar
RandomSliceBone Query-Verhalten, kein Feld: def sqlFilter(self, stmt): return stmt.order_by(func.random())

Gemeinsame Bone-Parameter

Bone-Parameter Feld-Äquivalent
descr Field(descr=…) — Default: title-cased Feldname
required abgeleitet (nicht-Optional ohne Default); Override per required=
defaultValue default= / default_factory=
visible / readOnly visible=False / readonly=True (erzwingt required: false)
params params={…}
languages Language[X] + languages=(…) oder set_default_languages()
multiple-Constraints viur_relation_meta = {"rel": {"multiple": {"min": …, "max": …, "duplicates": …}}}
format format="$(dest.name)" — Records & Relationen
refKeys viur_ref_keys = ("name",) am Ziel-Model (key/shortkey immer dabei)
unique / indexed unique=True / index=… — SQL erzwingt

Eigene Typen & pydantic-Ökosystem

# Bone-Typ ohne Python-Pendant: eine Annotated-Zeile
Slug = Annotated[str, BoneType("str.slug")]                # verfeinert str-Extras
Hidden = Annotated[str, BoneType("hidden", replace=True)]  # definiert sie allein

# Ökosystem-Typen sind registriert — der validierte Typ IST die Deklaration:
mail: EmailStr | None = Field(default=None)                # str.email
site: AnyUrl | None = Field(default=None, sa_type=String)  # uri
short: constr(max_length=12) | None = Field(default=None)  # str, maxlength 12

register_bone_type(MeinTyp, BoneType("select.something"))      # MRO-Lookup, Subklassen erben

Funktioniert außerdem nativ: Strict*, PositiveInt/conint(gt=…) (exklusive Grenzen werden für ints exakt zu min/max), ByteSize, AwareDatetime/PastDate, extra-types Epoch.Integer sowie alle str-Subclass-Extra-Typen (MacAddress, ISBN, TimeZoneName, ISO4217, …) als validierte str-Bones. Nicht verwenden: SecretStr (maskiert Dumps — Password/Credential nehmen) und pydantic.Json (validiert JSON-Stringsviur.models.Json nehmen). Normalisierungen beachten: AnyUrl hängt einen Slash an, PhoneNumber speichert RFC3966 (tel:+49…).