Skip to content

Commit 52e3b66

Browse files
committed
docs: Django 백엔드 반영과 changepack
1 parent de9d8a4 commit 52e3b66

7 files changed

Lines changed: 101 additions & 27 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes": {"crates/vespertide-cli/Cargo.toml": "Minor", "crates/vespertide-config/Cargo.toml": "Minor", "crates/vespertide-core/Cargo.toml": "Minor", "crates/vespertide-exporter/Cargo.toml": "Minor", "crates/vespertide-loader/Cargo.toml": "Minor", "crates/vespertide-lsp/Cargo.toml": "Minor", "crates/vespertide-macro/Cargo.toml": "Minor", "crates/vespertide-naming/Cargo.toml": "Minor", "crates/vespertide-planner/Cargo.toml": "Minor", "crates/vespertide-query/Cargo.toml": "Minor", "crates/vespertide/Cargo.toml": "Minor"}, "note": "Django(Python) 익스포터를 8번째 ORM 백엔드로 추가. `Orm`이 exhaustive pub enum이라 `Orm::Django` 추가가 0.x 기준 breaking이고, vespertide-config에 `django` 설정 섹션(`appLabel`)이, vespertide-cli에 `export --orm django` 경로가 함께 들어간다(스키마 전체를 `models.py` 한 파일로 쓰고, 모델은 `managed = False`로 나간다). 기존 파이썬 백엔드(SQLAlchemy·SQLModel)도 파이썬 키워드 컬럼명을 이스케이프한다. published 크레이트를 전부 같은 Minor로 올리는 이유는 #185·#186과 동일하다: [workspace.dependencies]의 `=` 핀으로 물려 있어 일부만 올리면 핀과 크레이트 버전이 어긋나 resolve가 깨진다.", "date": "2026-09-20T09:00:00.0000000Z"}

‎AGENTS.md‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ vespertide/
1818
│ ├── vespertide-planner/ # Schema diffing, baseline reconstruction, validation
1919
│ ├── vespertide-query/ # SQL generation (Postgres/MySQL/SQLite)
2020
│ ├── vespertide-cli/ # CLI commands: init, diff, sql, revision, export
21-
│ ├── vespertide-exporter/ # ORM codegen: SeaORM, SQLAlchemy, SQLModel, JPA, Prisma, Drizzle, GORM
21+
│ ├── vespertide-exporter/ # ORM codegen: SeaORM, SQLAlchemy, SQLModel, JPA, Prisma, Drizzle, GORM, Django
2222
│ ├── vespertide-loader/ # Filesystem loading of models/migrations
2323
│ ├── vespertide-config/ # vespertide.json configuration
2424
│ ├── vespertide-lsp/ # Language server: 13 LSP capabilities + HS-7~11 caching
@@ -48,7 +48,7 @@ vespertide/
4848
| Schema diffing | `vespertide-planner/src/diff/` | topological sort for FK deps |
4949
| SQL generation | `vespertide-query/src/sql/` | One file per action type |
5050
| CLI commands | `vespertide-cli/src/commands/` | `cmd_*` functions |
51-
| ORM export | `vespertide-exporter/src/{seaorm,sqlalchemy,sqlmodel,jpa,prisma,drizzle,gorm}/` | Backend-specific generators |
51+
| ORM export | `vespertide-exporter/src/{seaorm,sqlalchemy,sqlmodel,jpa,prisma,drizzle,gorm,django}/` | Backend-specific generators |
5252
| Compile-time macro | `vespertide-macro/src/lib.rs` | `vespertide_migration!` proc macro |
5353
| **LSP RingCache (HS-7~11)** | `vespertide-lsp/src/cache.rs` | Generic ring-buffer LRU shared across symbols/diagnostics/drift/semantic-token caches |
5454
| **LSP drift cache** | `vespertide-lsp/src/drift/cache.rs` | HS-10 drift cache implementation |
@@ -103,7 +103,7 @@ When constructing struct literals (e.g. `TableDef { name: ... }`), prefer `.into
103103
from string literals over the explicit constructor for terseness.
104104

105105
### `#[non_exhaustive]` Structs (0.2.0+)
106-
`VespertideConfig`, `SeaOrmConfig`, `MigrationOptions` are `#[non_exhaustive]`:
106+
`VespertideConfig`, `SeaOrmConfig`, `DjangoConfig`, `MigrationOptions` are `#[non_exhaustive]`:
107107
external callers MUST construct via `..Default::default()` or the provided
108108
constructor.
109109

@@ -170,7 +170,7 @@ See `docs/clippy-allow-audit.md` for the full audit history.
170170
| `QueryError::Other(...)` in new code | Emits deprecation warning. Use `SchemaError` / `InvalidColumnType` / `BackendError` / `UnsupportedAction` |
171171
| Exhaustive struct literal for `MigrationOptions` / `VespertideConfig` | `#[non_exhaustive]` — use `..Default::default()` |
172172
| Comparing newtype with `String::eq(&name.to_string(), "user")` | `TableName: PartialEq<&str>` — use `name == "user"` directly |
173-
| Per-ORM exporter snapshot test (single ORM) | Use the 7-ORM `orm_cases!` macro; snapshots must cross-compare all ORMs |
173+
| Per-ORM exporter snapshot test (single ORM) | Use the 8-ORM `orm_cases!` macro; snapshots must cross-compare all ORMs |
174174

175175
## COMMANDS
176176

@@ -235,7 +235,7 @@ Files near the ceiling (next split candidates — line counts as of the
235235
| `query/src/sql/delete_column/mod.rs` | 1138 | prod+inline-tests (≤1200) | DROP COLUMN with SQLite rebuild |
236236
| `query/src/sql/add_constraint/mod.rs` | 1138 | prod+inline-tests (≤1200) | ADD CONSTRAINT |
237237
| `core/src/schema/table/tests/mod.rs` | 1137 | test-file (≤1200) | Table normalization tests |
238-
| `exporter/src/tests/fixtures/mod.rs` | 1168 | test-file (≤1200) | Shared 7-ORM fixture schemas |
238+
| `exporter/src/tests/fixtures/mod.rs` | 1173 | test-file (≤1200) | Shared 8-ORM fixture schemas |
239239
| `planner/src/validate/check_strengthening.rs` | 1121 | prod+inline-tests (≤1200) | CHECK strengthening analysis |
240240
| `query/src/sql/helpers.rs` | 1109 | prod+inline-tests (≤1200) | Identifier quoting / type-cast helpers |
241241
| `lsp/src/code_actions.rs` | 1107 | prod+inline-tests (≤1200) | LSP code actions (incl. CHECK BETWEEN-swap) |
@@ -369,7 +369,7 @@ alongside what the dialect emits *in place of* the missing construct.
369369
**How many cases:**
370370

371371
Where the axis has a documented matrix — `vespertide-query`'s
372-
`{PG, MySQL, SQLite}` triple and the exporter's seven-ORM `orm_cases!` — fan out
372+
`{PG, MySQL, SQLite}` triple and the exporter's eight-ORM `orm_cases!` — fan out
373373
**always**, even when every case renders the same bytes: identity across the
374374
matrix is itself the assertion (`uniform_sql_is_emitted_byte_for_byte`), and a
375375
lone single-backend snapshot is a fault (`vespertide-query/AGENTS.md`).
@@ -397,14 +397,14 @@ fn create_table_snapshot(#[case] backend: DatabaseBackend) {
397397
```
398398

399399
This is the same pattern used by `vespertide-query` (3 backends, 564 snapshots)
400-
and `vespertide-exporter` (7 ORMs via `Orm` enum, 525 cross-ORM snapshots). When
400+
and `vespertide-exporter` (8 ORMs via `Orm` enum, 632 cross-ORM snapshots). When
401401
adding a new backend / ORM / format, the change is **one `#[case::name(Value)]`
402402
line**.
403403

404404
### Exporter snapshots MUST cover ALL ORMs (no per-ORM snapshots)
405-
Every `vespertide-exporter` snapshot test MUST be written through the shared `orm_cases!` rstest macro in `crates/vespertide-exporter/src/tests/mod.rs`, which renders each fixture for **all seven ORMs** (`Orm::SeaOrm`, `Orm::SqlAlchemy`, `Orm::SqlModel`, `Orm::Jpa`, `Orm::Prisma`, `Orm::Drizzle`, `Orm::Gorm`). A new export scenario = ONE fixture + ONE `orm_cases!(...)` line, producing exactly seven snapshots (one per ORM) in the single shared `crates/vespertide-exporter/src/tests/snapshots/` directory.
405+
Every `vespertide-exporter` snapshot test MUST be written through the shared `orm_cases!` rstest macro in `crates/vespertide-exporter/src/tests/mod.rs`, which renders each fixture for **all eight ORMs** (`Orm::SeaOrm`, `Orm::SqlAlchemy`, `Orm::SqlModel`, `Orm::Jpa`, `Orm::Prisma`, `Orm::Drizzle`, `Orm::Gorm`, `Orm::Django`). A new export scenario = ONE fixture + ONE `orm_cases!(...)` line, producing exactly eight snapshots (one per ORM) in the single shared `crates/vespertide-exporter/src/tests/snapshots/` directory.
406406

407-
FORBIDDEN: per-ORM `#[test]` snapshot functions inside `src/seaorm/`, `src/sqlalchemy/`, `src/sqlmodel/`, `src/jpa/`, `src/prisma/`, `src/drizzle/`, `src/gorm/`, or any `snapshots/` directory other than `src/tests/snapshots/`. A scenario snapshotted for only one ORM is a defect — ORM output must always be cross-compared across all seven. When adding a new ORM the change is a single `#[case::<orm>(Orm::<Variant>)]` line in the macro, never a new per-ORM test.
407+
FORBIDDEN: per-ORM `#[test]` snapshot functions inside `src/seaorm/`, `src/sqlalchemy/`, `src/sqlmodel/`, `src/jpa/`, `src/prisma/`, `src/drizzle/`, `src/gorm/`, `src/django/`, or any `snapshots/` directory other than `src/tests/snapshots/`. A scenario snapshotted for only one ORM is a defect — ORM output must always be cross-compared across all eight. When adding a new ORM the change is a single `#[case::<orm>(Orm::<Variant>)]` line in the macro, never a new per-ORM test.
408408

409409
Exception: an entry point that exists in only one backend (Prisma's single-file `render_schema`, which deduplicates enums globally; Drizzle's dialect-aware `render_schema`, whose axis is the SQL dialect rather than the ORM) is not a cross-ORM scenario, so its snapshot tests live as inline tests of that module — with the snapshot files still written to the shared `src/tests/snapshots/` via `with_settings!(snapshot_path => ...)`.
410410

‎README.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ Declarative database schema management. Define your schemas in JSON, and Vespert
1616
- **Enum Types**: Native string enums and integer enums (no migration needed for new values)
1717
- **Zero-Runtime Migrations**: Compile-time macro generates database-specific SQL
1818
- **JSON Schema Validation**: Ships with JSON Schemas for IDE autocompletion and validation
19-
- **ORM Export**: Export schemas to SeaORM, SQLAlchemy, SQLModel, JPA, Prisma, Drizzle, GORM
19+
- **ORM Export**: Export schemas to SeaORM, SQLAlchemy, SQLModel, JPA, Prisma, Drizzle, GORM, Django
2020
- **Language Server**: First-class editor support via the bundled `vespertide-lsp` — see [LSP Features](#lsp-features) below
2121

2222
## What's new in 0.2.0
2323

2424
API stability pass with a byte-identical JSON wire format — existing models and migration files load unchanged.
2525

2626
- **Newtype identifiers**: `TableName`, `ColumnName`, `IndexName` in `vespertide-core` (`crates/vespertide-core/src/schema/names.rs`). `#[serde(transparent)]` keeps JSON identical; `Deref<Target = str>` means most call sites need no edit.
27-
- **`#[non_exhaustive]` configs**: `VespertideConfig`, `SeaOrmConfig`, and `MigrationOptions` must be built with `..Default::default()` (or `MigrationOptions::new()`), so future fields don't break semver.
27+
- **`#[non_exhaustive]` configs**: `VespertideConfig`, `SeaOrmConfig`, `DjangoConfig`, and `MigrationOptions` must be built with `..Default::default()` (or `MigrationOptions::new()`), so future fields don't break semver.
2828
- **Decomposed `QueryError`**: new `InvalidColumnType`, `SchemaError`, `BackendError`, and `UnsupportedAction` variants. `QueryError::Other(String)` is `#[deprecated]` but still compiles.
2929
- **Cloneable `MigrationError`**: backed by `Arc<dyn Error>`, so retry loops can re-emit errors without re-running the planner.
3030
- **Faster LSP**: every editor hot path (diagnostics, symbols, drift) is now `RingCache`-backed in `vespertide-lsp`. No API change; -99% latency on the synthetic `tools/lsp-profile/` workload.
@@ -246,6 +246,7 @@ vespertide export --orm jpa # Java - JPA/Hibernate entities
246246
vespertide export --orm prisma # Prisma - schema.prisma models
247247
vespertide export --orm drizzle # TypeScript - Drizzle ORM (pg/mysql/sqlite files)
248248
vespertide export --orm gorm # Go - GORM models (models.go)
249+
vespertide export --orm django # Python - Django models (models.py)
249250
```
250251

251252
## Runtime Migrations (Macro)

‎bridge/node/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Declarative database schema management: define tables in JSON or YAML, diff
44
them against the migration history, and generate SQL for PostgreSQL, MySQL
5-
and SQLite plus ORM code (SeaORM, SQLAlchemy, SQLModel, JPA, Prisma, Drizzle, GORM).
5+
and SQLite plus ORM code (SeaORM, SQLAlchemy, SQLModel, JPA, Prisma, Drizzle, GORM, Django).
66

77
This package is the `vespertide` command-line tool as a native Node addon,
88
so no Rust toolchain is needed.

‎crates/vespertide-cli/AGENTS.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ src/
2121
│ # choices_and_apply/), tests/
2222
├── status.rs # Show config and sync status
2323
├── log.rs # List applied migrations with SQL
24-
├── export/ # Export to ORM code (SeaORM/SQLAlchemy/SQLModel/JPA/Prisma/Drizzle/GORM) —
25-
│ # mod.rs + tests/ (mod.rs, prisma.rs, drizzle.rs, gorm.rs,
24+
├── export/ # Export to ORM code (SeaORM/SQLAlchemy/SQLModel/JPA/Prisma/Drizzle/GORM/Django) —
25+
│ # mod.rs + tests/ (mod.rs, prisma.rs, drizzle.rs, gorm.rs, django.rs,
2626
│ # models_file.rs)
2727
└── erd/ # ERD diagram export — mod.rs, mermaid.rs, dot.rs, svg/ (style, model,
2828
# layout, edges, render, util), tests/
@@ -39,7 +39,7 @@ src/
3939
| `revision -m` | `cmd_revision(msg, fill_with)` | Interactive prompts via `dialoguer::Input` |
4040
| `status` | `cmd_status()` | Display config paths and migration count |
4141
| `log` | `cmd_log(backend)` | Iterate applied migrations, print SQL |
42-
| `export --orm` | `cmd_export(orm, dir)` | per-table `render_entity_with_schema()` + mod.rs wiring; Prisma/Drizzle/GORM render the whole schema into fixed file names |
42+
| `export --orm` | `cmd_export(orm, dir)` | per-table `render_entity_with_schema()` + mod.rs wiring; Prisma/Drizzle/GORM/Django render the whole schema into fixed file names |
4343
| `erd -f svg\|mermaid\|dot` | `cmd_erd_with_filters(format, output, include, exclude, depth)` | FK-graph filtered ERD rendering |
4444

4545
## WHERE TO LOOK
@@ -56,7 +56,7 @@ src/
5656
## NOTES
5757

5858
- **revision/**: Most complex command — handles interactive `--fill-with` prompts for NOT NULL columns without defaults; long ago split from a single 3064-line file into `revision/{mod,parse,emit,write,timezones}.rs` + `prompts/` + `tests/`
59-
- **export/**: Generates the `mod.rs` chain for SeaORM exports; Python/Java ORMs skip it. Prisma, Drizzle and GORM take separate single-file paths rather than one file per model — Prisma writes one `models.prisma`, Drizzle one file per dialect (`models.pg.ts` / `models.mysql.ts` / `models.sqlite.ts`), GORM `models.go`. The last one skips the extension sweep like Drizzle, and refuse to overwrite a `models.*` that does not open with the `Code generated by vespertide. DO NOT EDIT.` line
59+
- **export/**: Generates the `mod.rs` chain for SeaORM exports; Python/Java ORMs skip it. Prisma, Drizzle, GORM and Django take separate single-file paths rather than one file per model — Prisma writes one `models.prisma`, Drizzle one file per dialect (`models.pg.ts` / `models.mysql.ts` / `models.sqlite.ts`), GORM `models.go` and Django `models.py`. The last two skip the extension sweep like Drizzle, and refuse to overwrite a `models.*` that does not open with the `Code generated by vespertide. DO NOT EDIT.` line
6060
- All commands use `load_config()`, `load_models()`, `load_migrations()` from `vespertide_loader`
6161
- YAML and JSON are both fully supported for models and migrations; `new <name> -f yaml` creates YAML templates.
6262
- Prefer typed `MigrationAction` enums; `RawSql` exists as a documented emergency escape hatch, but is not recommended for normal use.

0 commit comments

Comments
 (0)