Zum Hauptinhalt springen

Ihr eigenes Modell, aus dem Nichts

Etwa 45 Minuten. Die erste Einführung hat eine fertige Demo installiert und verändert. Diese hier baut alles aus einem leeren Modell: Sie definieren die Typen, ergänzen die benutzerdefinierten Eigenschaften, legen eine Ansicht an, modellieren ein paar Entitäten und schreiben dann ein kleines eigenes Template-Paket, das daraus Dateien macht.

Das Ziel ist zu sehen, woher jedes Stück kommt. In der Demo wurden Ihnen die Typen und das Paket in die Hand gegeben. Hier machen Sie sie, und am Ende haben Sie eine Datei aus einem Modell und einer Vorlage erzeugt, die Sie selbst geschrieben haben.

Das Beispiel ist eine winzige Bibliothek: Bücher mit einem Titel, ein paar Angaben und einem Regalplatz. Bewusst klein – ein Entitätstyp, zwei Attributtypen und eine benutzerdefinierte Eigenschaft genügen, um jeden Gedanken zu zeigen.

Zwei Hälften

Die Schritte 1–6 passieren vollständig im Browser. Schritt 7 – das Schreiben der Vorlage – ist Code und ein einzeiliger Upload aus einem Terminal, weil ITB keinen eingebauten Editor für Paketcode hat. Der Bruch wird an der Stelle benannt.


1 · Mit einem leeren Modell beginnen​

Haben Sie ein Modell offen, das Sie behalten wollen, sichern Sie es zuerst mit Modellaktionen ▸ Download Model in der Kopfleiste. Um sauber zu starten, öffnen Sie dann entweder einen frischen Arbeitsbereich oder leeren den aktuellen.

Ein leeres Modell generiert nichts, weil nichts installiert und nichts typisiert ist. Das ist der Ausgangspunkt: jede Datei, die Sie am Ende erzeugen, existiert, weil Sie den Typ, die Entität und die Regel hinzugefügt haben, die sie hervorbringen.


2 · Die Typen definieren​

Öffnen Sie die Einstellungen (Zahnradsymbol in der linken Seitenleiste) und gehen Sie auf den Reiter Typen. Vier Kategorien links: Entität, Attribut, Beziehung, Ansicht. Ein Typ ist ein Stück Vokabular – ein Name, den Ihr Modell benutzen und auf den ein Paket passen kann.

Legen Sie einen Entitätstyp an:

FeldWert
NameBook
Schlüssellib_book

Dann zwei Attributtypen:

NameSchlüssel
Titlelib_title
Detaillib_detail

Ein Entitätstyp: der Name, den Sie sehen, und der Schlüssel, auf den ein Paket passt.

Die Attributtypen haben ihre eigene Kategorie im selben Reiter.

Der Schlüssel ist der Teil, auf den es ankommt, und man versteht ihn besser jetzt als später. Der Name ist, was Sie sehen; der Schlüssel ist, worauf ein Paket passt. Später wird das Template-Paket sagen „erzeuge eine Karte für jedes lib_book" – es nennt den Schlüssel, nie den Anzeigenamen. Sie können Book in diesem Dialog also in Volume umbenennen, ohne dass etwas kaputtgeht, weil lib_book sich nicht geändert hat.

Einen Beziehungstyp legen wir nicht an – das Bibliotheksmodell braucht noch keinen. Ein Entitätstyp und zwei Attributtypen sind ein vollständiges Vokabular für Schritt 7.


3 · Eine benutzerdefinierte Eigenschaft anlegen​

Bleiben Sie in den Einstellungen und wechseln Sie auf den Reiter Benutzerdefinierte Eigenschaften. Legen Sie eine an, mit Geltungsbereich Entitäten:

FeldWert
NameShelf
Schlüssellib_shelf
Datentypstring

Benutzerdefinierte Eigenschaften haben einen eigenen Reiter, mit Kategorien für Entität, Attribut, Beziehung und Modell.

Eine benutzerdefinierte Eigenschaft ist eine Aussage über eine Entität und kein Feld davon. Der Titel eines Buches gehört zum Buch; wo es steht, ist eine Tatsache, die Sie darüber festhalten. Dieser Unterschied ist der ganze Grund, warum es benutzerdefinierte Eigenschaften gibt, und in Schritt 7 sehen Sie den Regalplatz auf der erzeugten Karte landen, ohne je ein „Feld" des Buches gewesen zu sein.


4 · Eine Ansicht anlegen​

Schliessen Sie die Einstellungen. Klicken Sie in der linken Seitenleiste unter Viewpoints auf + und nennen Sie sie Library. Öffnen Sie sie.

Ein Name genügt, um eine anzulegen.

Eine Ansicht ist eine Zeichenfläche – ein Blick auf das Modell. Entitäten leben im Modell; eine Ansicht entscheidet, welche davon Sie ansehen und wo sie liegen. Sie brauchen eine geöffnete, bevor Sie etwas platzieren können – deshalb stand auf der Zeichenfläche bisher No viewpoint open.


5 · Ein Buch modellieren​

Klicken Sie in der linken Seitenleiste auf + neben Entities und wählen Sie Add Entity. Ein Dialog fragt drei Dinge:

  1. Name: Dune.
  2. Beschreibung leer lassen.
  3. Typ auf Book setzen – den Entitätstyp aus Schritt 2. Dann Create Entity.

Wählen Sie nun Dune im Baum, damit die rechte Leiste seinen Bereich zeigt, und öffnen Sie den Reiter Attributes. Legen Sie je Zeile ein Attribut an und geben Sie jedem einen Typ:

AttributAttributtyp
titleTitle
authorDetail
yearDetail

Name und der Typ aus Schritt 2. Der Typ entscheidet, ob ein Paket je eine Datei dafür erzeugt.

Das erste Attribut, auf dem Reiter Attributes. Jedes trägt einen eigenen Typ – dieser Typ ist die Rolle, die es in der Ausgabe spielt. Die anderen beiden entstehen genauso.

Der Typ des Attributs ist das, was das Paket lesen wird – title trägt lib_title, die anderen beiden tragen lib_detail. Ein Attribut ohne Typ ist kein Fehler, aber das Paket aus Schritt 7 überspringt es: es hat keine Rolle, also gibt es nichts, was dafür gerendert würde.

Legen Sie auf demselben Weg ein zweites Buch an – Foundation, Typ Book, ein title und ein author –, damit die Generierung später mehr als eine Datei erzeugt.

hinweis

Die Zeichenfläche sagt weiterhin, die Ansicht sei leer, und das stimmt: eine Entität gehört zum Modell, und eine Ansicht zeigt nur die, die Sie daraufgelegt haben. Ziehen Sie ein Buch aus dem Baum auf die Zeichenfläche, wenn Sie es dort sehen wollen. Die Generierung liest das Modell, ihr ist es also gleich.


6 · Den Eigenschaftswert setzen​

Wählen Sie Dune und bleiben Sie auf dem Reiter Overview der rechten Leiste. Klicken Sie unter Properties auf Add Property, wählen Sie Shelf und geben Sie den Wert SF-3. Lassen Sie den Regalplatz von Foundation leer.

Die Eigenschaft zu definieren (Schritt 3) und ihr einen Wert zu geben, sind zwei verschiedene Handlungen an zwei verschiedenen Orten.

Der leere ist Absicht. In Schritt 7 zeigt die Vorlage den Regalplatz nur, wenn er gesetzt ist; die Karte von Dune trägt also eine Regalzeile und die von Foundation nicht – dieselbe Vorlage, zwei verschiedene Ausgaben, entschieden durch die Daten.

Sie haben nun ein vollständiges, typisiertes Modell. Generiert wurde noch nichts, weil nichts weiss, wie aus einem Book eine Datei wird. Das ist das Paket, und es ist der Rest dieser Einführung.


7 · Ein Template-Paket schreiben​

Das ist der Schritt, der den Browser verlässt. Ein Template-Paket sind drei Dateien in einem Ordner:

library_cards/
config/config.json the vocabulary and the rule
__init__.py the builder (Python)
templates/book_card.md.jinja2 the template

Die vollständige Anatomie steht unter Ein Template-Paket schreiben; hier bauen wir das kleinste, das funktioniert.

Die Konfiguration​

config/config.json deklariert dieselben Typen, die Sie in der Oberfläche angelegt haben – damit Paket und Modell dasselbe Vokabular sprechen – und eine Definition, die Regel:

{
"version": 1,
"metadata": { "name": "library_cards", "version": "1.0.0", "display_name": "Library Cards" },
"requirements": {
"entity_types": [ { "name": "Book", "key": "lib_book", "match_by": "key" } ],
"attribute_types": [ { "name": "Title", "key": "lib_title", "match_by": "key" },
{ "name": "Detail", "key": "lib_detail", "match_by": "key" } ],
"entity_custom_properties": [ { "name": "Shelf", "key": "lib_shelf", "data_type": "string" } ]
},
"definitions": [
{
"name": "book_card",
"type_key": "lib_book",
"template": "book_card.md.jinja2",
"context_builder": "build_book_card_context",
"name_pattern": "{name}.card.md"
}
]
}

Die Definition liest sich: für jede Entität vom Typ lib_book rufe build_book_card_context, rendere book_card.md.jinja2 und benenne die Datei nach der Entität. type_key ist lib_book – der Schlüssel aus Schritt 2, nicht das Wort „Book".

Der Builder​

__init__.py macht aus einem Buch die Daten, die die Vorlage braucht. Es muss create_pack exportieren und eine Funktion je Definition:

import os
from pydantic import BaseModel
from template_engine_api import TemplatePack, TransformHelpers

CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config", "config.json")
TEMPLATES_DIR = os.path.join(os.path.dirname(__file__), "templates")


class BookCardContext(BaseModel):
title: str
details: list[dict]
shelf: str


class LibraryCardBuilders(TransformHelpers):
def build_book_card_context(self, entity_id: str) -> BookCardContext:
entity = self._find(entity_id)
title = ""
details = []
for attr in self._attrs(entity_id):
role = self.imh.type_key(attr.id) if attr.type else None
if role == "lib_title":
title = attr.name
elif role == "lib_detail":
details.append({"name": attr.name, "value_type": attr.datatype or "string"})
return BookCardContext(
title=title or entity.name,
details=details,
shelf=self._cp(entity_id, "lib_shelf", ""),
)


def create_pack(data_model, config=None) -> TemplatePack:
builders = LibraryCardBuilders(data_model, config=config)
return TemplatePack(
context_builders={"build_book_card_context": builders.build_book_card_context},
template_config=CONFIG_PATH,
template_loader_path=TEMPLATES_DIR,
)

Lesen Sie die Schleife: self.imh.type_key(attr.id) fragt jedes Attribut, welche Rolle es spielt, und bekommt den Schlüssel zurück – lib_title oder lib_detail. Der Titel wird zur Überschrift, die Angaben werden zu Zeilen. Ein Attribut ohne Typ liefert None und wird übersprungen. Der Regalplatz wird mit _cp gelesen – eine benutzerdefinierte Eigenschaft, kein Attribut – mit leerem Vorgabewert.

vorsicht

Schreiben Sie kein from __future__ import annotations. Es macht aus den Annotationen Zeichenketten, und Pydantic kann BookCardContext dann beim Laden des Pakets nicht auflösen; es scheitert mit „…Context is not fully defined".

Die Vorlage​

templates/book_card.md.jinja2 macht aus diesem Kontext eine Datei. Der Rückgabewert des Builders kommt als ctx an:

# {@ ctx.title @}

{= if ctx.details =}
| Detail | Type |
| --- | --- |
{= for d in ctx.details =}
| {@ d.name @} | {@ d.value_type @} |
{= endfor =}
{= else =}
_No details recorded yet._
{= endif =}

{= if ctx.shelf =}
Shelf: **{@ ctx.shelf @}**
{= endif =}

Die Trennzeichen sind {@ … @} für einen Ausdruck und {= … =} für einen Block – nicht die üblichen {{ }} und {% %}. Wenn Sie aus Gewohnheit {{ ctx.title }} schreiben, gibt es keinen Fehler; es wird leer gerendert, und der Irrtum sieht so aus, als hätte der Builder keinen Titel geliefert.

Hochladen​

Packen Sie den Ordner in ein Zip und schicken Sie es an Ihre Instanz. Es wird nur für Ihren Benutzer installiert:

cd library_cards && zip -r ../library_cards.zip .
curl -X POST "$ITB_API/api/v1/template/packs" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@../library_cards.zip"

($ITB_API ist die Basis-URL Ihrer Instanz, $TOKEN ein Bearer-Token Ihrer Sitzung.)


8 · Generieren und das Ergebnis prüfen​

Zurück im Browser:

  1. Klicken Sie Generate All und warten Sie auf den grünen Haken.
  2. Download Artifacts, dann entpacken.

Zwei Dateien, eine je Buch:

Dune.card.md
Foundation.card.md

Öffnen Sie Dune.card.md: der Titel als Überschrift, author und year als Zeilen und eine Zeile Shelf: SF-3. Öffnen Sie Foundation.card.md: seine Zeilen, und keine Regalzeile – weil Sie diese Eigenschaft leer gelassen haben und die Vorlage sie nur zeigt, wenn sie gesetzt ist.

Vor dem Generieren hätten Sie auch fragen können: Rechtsklick auf die Titelleiste von Dune auf der Zeichenfläche, dann Show templates. Es listet Ihre Definition book_card, das Artefakt, das sie erzeugen würde, und das Paket, aus dem sie stammt – derselbe Bereich Show Templates aus der ersten Einführung, nur auf eine Regel gerichtet, die Sie geschrieben haben.


Was Sie gerade gebaut haben​

Sie haben gemachtUnd es entschied
Den Entitätstyp lib_bookFür welche Entitäten das Paket eine Datei erzeugt
Die Attributtypen lib_title / lib_detailWas jedes Attribut auf der Karte wird
Die benutzerdefinierte Eigenschaft lib_shelfEine Tatsache über das Buch, die in die Ausgabe gelangt, ohne ein Feld davon zu sein
Die DefinitionDie Regel, die einen Typ an eine Vorlage bindet
Den Builder und die VorlageWie aus einem Buch eine Karte wird

Das ist ein ganzes Paket – dieselbe Form wie die Demo-Pakete, nur kleiner. Von hier aus behandelt Paketkonfiguration die Hebel in der Tiefe, und Ein Template-Paket schreiben die Anatomie, die Scopes und wie man ein Paket testet, bevor man es ausliefert.