Repository navigation
Conversation
…#107) Parity with #98/#106 (TypeScript). The Python and Go generators derived the table name as snakeCase(entity.name) and never read the per-entity `table` override, so an entity named `Order` with "table": "order_record" generated the SQL reserved word `order`. - Both generators now emit `tableName = entity.table || snakeCase(name)`. - Python template uses it for `__tablename__`, the self-referencing ManyToMany ForeignKey, join-table names, and index names, so every reference to this entity's own table follows the override consistently. - Go template uses it in `TableName()`. Without an override tableName === snakeCase(name), so output is unchanged for the common case. Added override + fallback tests to both suites. Full suite: 318 passed, 0 failed. fixes #107
cultron
force-pushed
the
fix/107-python-go-table-override
branch
from
August 18, 2026 22:18
d59604c to
1997649
Compare
cultron
added a commit
that referenced
this pull request
Oct 5, 2026
… Python/Go (supersedes #119) (#150) * fix(cli): honor per-entity table override in Python and Go generators (#107) Parity with #98/#106 (TypeScript). The Python and Go generators derived the table name as snakeCase(entity.name) and never read the per-entity `table` override, so an entity named `Order` with "table": "order_record" generated the SQL reserved word `order`. - Both generators now emit `tableName = entity.table || snakeCase(name)`. - Python template uses it for `__tablename__`, the self-referencing ManyToMany ForeignKey, join-table names, and index names, so every reference to this entity's own table follows the override consistently. - Go template uses it in `TableName()`. Without an override tableName === snakeCase(name), so output is unchanged for the common case. Added override + fallback tests to both suites. Full suite: 318 passed, 0 failed. fixes #107 * fix(python): point ManyToOne and join-table FKs at the parent's real table PR #119 moves __tablename__ to the per-entity table override, but child models still wrote ForeignKey("<parent name lowercased>.id"). With an override on the parent, or any multi-word parent (UserProfile -> user_profile, FK said userprofile), the FK named a table that does not exist. Pass every entity's table name into the model template and use it. * feat(test smoke): create, read and delete one row per entity apso test smoke only called GET /health and each list route. It now builds a payload per entity from the .apsorc (required fields only: not nullable, no default), creates entities parents first so required ManyToOne foreign keys (<camelCase(to_name or parent)>Id) can be set from the parent's id, reads each row back by id, and deletes children before parents. Entities it cannot fill (unsupported required types, cycles of required relationships, children of skipped entities) get a skip step with the reason instead of failing. The planner lives in src/lib/verify/crud-plan.ts with unit tests for the payload values and the ordering. * feat(test smoke, migrate): Python and Go support where it is honest apso test smoke for Python and Go projects starts the generated server (uvicorn from .venv, or a go build ./cmd binary so stopping it cannot orphan the server) and runs the same HTTP checks against DATABASE_URL from the environment or .env. It never creates a database or tables; with no DATABASE_URL, or nothing listening at its host and port, the check is skipped with a message and reports "Smoke test skipped.". Routes follow each generator: /api/<plural> (Python) and /api/<kebab-plural> (Go). apso migrate generate and verify fail fast for Python and Go and name the native tool (Alembic, goose). The baseline, run and drift flow needs a scratch database; TypeScript has PGlite, the others do not. scaffold-smoke.sh: the go leg runs apso test smoke when SMOKE_DATABASE_URL points at an empty Postgres (opt in, off in CI).
Contributor
Author
|
Shipped in #150 (cherry-picked with authorship), together with a fix for the Python ForeignKey target that this change alone would have broken. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #107 — Python/Go parity with #98/#106 (already fixed for TypeScript).
Problem
The Python and Go generators derived the table name as
snakeCase(entity.name)and never read the per-entitytableoverride in.apsorc. An entity namedOrderwith"table": "order_record"generated the SQL reserved wordorderin both languages.Fix
tableName = entity.table || snakeCase(name)into the template context (mirrors the TS fix in fix(cli): honor per-entity table override in TS generator (#98) #106).templates/go/models/model.eta):TableName()returnstableName.templates/python/models/model.eta):tableNameis used for__tablename__and for every other place that names this entity's own table — the self-referencing ManyToManyForeignKey("<table>.id"), the derived join-table name, the join-column prefix, and the index name. This matters in SQLAlchemy (unlike TypeORM) because the join-table FK references the owning table by string name; if only__tablename__changed, that FK would point at a non-existent table.Without an override
tableName === snakeCase(name), so generated output is byte-identical for the common (no-override) case. The only behavior change is when an override is set, and then all self-table references follow it consistently.Scope note
Cross-entity FKs to other entities' tables (e.g. Python
ForeignKey("<assoc>.id"), which already usesassoc.name.toLowerCase()rather than the override) are a separate pre-existing gap and are intentionally out of scope here, matching #106's focus.Tests
Added override + fallback tests to both generator suites:
order_record, never the reserved wordorderorderpreservedFull suite: 318 passed / 0 failed, lint 0 errors.
Note
Merging to
mainpublishes@apso/cli(merge = release) — leaving the merge to Matt.🤖 Generated with Claude Code