updates 33.1 84
This commit is contained in:
@@ -1,54 +1,111 @@
|
||||
# Modelgen
|
||||
|
||||
Generates platform-specific models from JSON Schema. Reads schema once, writes models for multiple targets.
|
||||
Multi-source, multi-target model generator. Reads a schema from wherever it
|
||||
already lives, and writes it out for every stack that needs it.
|
||||
|
||||
**Status:** dev
|
||||
**Status:** live
|
||||
|
||||
---
|
||||
|
||||
## What It Does
|
||||
|
||||
Modelgen takes a JSON Schema definition and produces model code for different platforms:
|
||||
Everything passes through one intermediate representation — `ModelDefinition`,
|
||||
`FieldDefinition`, `EnumDefinition`. **Loaders** fill it, **generators** emit
|
||||
from it, and the two sides do not know about each other. Adding an input means
|
||||
one extractor and every output comes with it; adding an output means one
|
||||
generator and every input already feeds it.
|
||||
|
||||
- **Pydantic** -- Python data validation models
|
||||
- **Django ORM** -- Django model classes
|
||||
- **Prisma** -- Prisma schema definitions
|
||||
```
|
||||
dataclasses ─┐ ┌─ pydantic
|
||||
Django │ ├─ django
|
||||
SQLAlchemy ├──▶ ModelDefinition ──▶├─ sqlmodel
|
||||
a live DB │ FieldDefinition ├─ typescript
|
||||
OpenAPI │ EnumDefinition ├─ protobuf
|
||||
CSV/ODS ─┘ ├─ prisma
|
||||
├─ strawberry
|
||||
├─ schema (graphgen)
|
||||
└─ datagen
|
||||
```
|
||||
|
||||
One schema, multiple outputs.
|
||||
Core is **pure standard library**. It is published as `soleprint-modelgen` and
|
||||
installs with no dependencies; live-database extraction is an extra
|
||||
(`pip install "soleprint-modelgen[db]"`), and YAML specs need PyYAML.
|
||||
|
||||
## Extractors
|
||||
## Sources
|
||||
|
||||
Modelgen also works in reverse. Extractors read existing codebases and produce a normalized schema representation:
|
||||
| Command | Reads |
|
||||
| --- | --- |
|
||||
| `from-schema` | Python dataclasses in a `schema/` folder |
|
||||
| `from-config` | a room's `config.json` |
|
||||
| `extract` | a Django or SQLAlchemy codebase (`--framework auto` detects) |
|
||||
| `from-db` | a live database, any SQLAlchemy dialect |
|
||||
| `from-openapi` | an OpenAPI 3.x / Swagger 2.0 document |
|
||||
| `from-tabular` | a directory of `.csv` / `.tsv` / `.ods` spreadsheets |
|
||||
|
||||
- **Django extractor** -- reads Django model files
|
||||
- **SQLAlchemy extractor** -- reads SQLAlchemy model files
|
||||
- **Prisma extractor** -- reads Prisma schema files
|
||||
```bash
|
||||
python -m station.tools.modelgen from-openapi -s api.yaml -o out/ -t pydantic,typescript,schema
|
||||
python -m station.tools.modelgen from-tabular -s ./sheets -o out/ -t pydantic,datagen
|
||||
python -m station.tools.modelgen extract -s /path/to/django -o out/ -t prisma
|
||||
python -m station.tools.modelgen from-db -u postgresql://… -o out/ -t typescript
|
||||
python -m station.tools.modelgen list-formats
|
||||
```
|
||||
|
||||
Extractors feed into graphgen for visualization.
|
||||
### From a spec
|
||||
|
||||
## Output
|
||||
`components.schemas` (or Swagger's `definitions`) become models. `$ref` chains
|
||||
and `allOf` are resolved, enums are materialised as real `Enum` classes so every
|
||||
target names them properly, and a referenced object becomes a relation rather
|
||||
than a nested type — the same call the database extractor makes, and what keeps
|
||||
the generated code valid for every target.
|
||||
|
||||
Generated models are written to `gen/<room>/models/`.
|
||||
The parse also yields the *operations*, which is what
|
||||
[shuntgen](#station-shuntgen) turns into routes.
|
||||
|
||||
```
|
||||
gen/<room>/models/
|
||||
├── pydantic/
|
||||
├── django/
|
||||
└── prisma/
|
||||
```
|
||||
### From spreadsheets
|
||||
|
||||
## CLI
|
||||
One model per CSV file, one per sheet in an ODS workbook. Column types are
|
||||
inferred from the values actually present, and a blank cell makes the column
|
||||
optional. Keys and relations are inferred by name and then confirmed against the
|
||||
data: an `id` column that is not unique is not treated as a key, and
|
||||
`customer_id` is only a foreign key if a `customers` sheet came with it.
|
||||
|
||||
```bash
|
||||
python -m modelgen
|
||||
```
|
||||
The rows are kept, not just the shape — which is what lets the `datagen` target
|
||||
sample real values instead of inventing them.
|
||||
|
||||
Reads from `schema.json` (the project source of truth) and writes to the configured output directory.
|
||||
ODS is read with `zipfile` and `ElementTree`. No odfpy, no pandas: the
|
||||
dependency-free promise is what makes this package publishable on its own.
|
||||
|
||||
## Shared Distribution
|
||||
## Targets
|
||||
|
||||
Modelgen is also distributed as a shared component via `ctrl/spr.py`. This allows other projects to use model generation without running full soleprint.
|
||||
`pydantic`, `django`, `sqlmodel`, `typescript` (`ts`), `protobuf` (`proto`),
|
||||
`prisma`, `strawberry`, `schema` (`jsonschema`), `datagen`.
|
||||
|
||||
## Schema Source
|
||||
Two are worth calling out:
|
||||
|
||||
- **`schema`** writes the graphgen-compatible `schema.json` — the portable
|
||||
artifact [graphgen](#station-graphgen) and databrowse read directly.
|
||||
Relations come out as `FK:<Model>` and `M2M:<Model>`.
|
||||
- **`datagen`** writes a `BaseDataGenerator` subclass for
|
||||
[datagen](#station-datagen), including its `schema()` override. Given
|
||||
spreadsheet rows it samples them; otherwise it synthesises from the types.
|
||||
|
||||
Multiple targets in one run get one file each, named `models_<target><ext>`.
|
||||
|
||||
## In a build
|
||||
|
||||
`build.py` calls modelgen during every room build, writing
|
||||
`gen/<room>/models/pydantic/__init__.py` from the room's `config.json`. See
|
||||
[Export / Compile](#export).
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
cd soleprint/station/tools
|
||||
python -m unittest modelgen.tests.test_extractors
|
||||
```
|
||||
|
||||
The source of truth is `schema.json` at the project root. All model generation starts from this file. Room-specific schema extensions live in `cfg/<room>/models/`.
|
||||
stdlib `unittest`, no pytest, and every input is built in a temp directory — the
|
||||
tests have to pass with nothing installed and nothing else in the tree. Run them
|
||||
from `station/tools/`, not from inside `modelgen/`: the package ships a
|
||||
`types.py`, and putting its own directory on `sys.path` shadows the standard
|
||||
library module of that name.
|
||||
|
||||
Reference in New Issue
Block a user