Templatejen laatu: declarative_base, ConfigDict, ForeignKey

- models.py: sqlalchemy.ext.declarative → sqlalchemy.orm (poistaa
  MovedIn20Warning-varoituksen)
- schemas.py: class Config → model_config = ConfigDict() (poistaa
  PydanticDeprecatedSince20-varoituksen)
- models.py: _id-kentät saavat ForeignKey("taulu.id") kun speksissä
  on relationship-merkintä

Testattu: 10 erilaista projektia, 78/78 testiä läpi, 0 varoitusta.
This commit is contained in:
2026-04-13 13:18:11 +03:00
parent b88a741f85
commit 42b71dbf77

View File

@@ -1202,18 +1202,27 @@ Blog → Author: name,email,bio(Text|None) / Post: title, content(Text), author_
}
function tmplModels(spec) {
// Kerää tarvittavat SA-tyypit + ForeignKey jos relaatioita
const saTypes = new Set(['Integer']);
for (const e of spec.entities) for (const f of e.fields) saTypes.add(f.sa_type.match(/^(\w+)/)[1]);
const relMap = {};
for (const r of (spec.relationships || [])) {
const target = spec.entities.find(e => e.name === r.to);
if (target) relMap[`${r.from}.${r.field}`] = target.table_name;
}
const hasFk = Object.keys(relMap).length > 0;
if (hasFk) saTypes.add('ForeignKey');
const imports = [...saTypes].sort().join(', ');
let code = `from sqlalchemy import create_engine, Column, ${imports}\n`;
code += `from sqlalchemy.ext.declarative import declarative_base\nfrom sqlalchemy.orm import sessionmaker\n\n`;
code += `from sqlalchemy.orm import declarative_base, sessionmaker\n\n`;
code += `DATABASE_URL = "sqlite:///./app.db"\n`;
code += `engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})\n`;
code += `SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)\nBase = declarative_base()\n\n`;
for (const e of spec.entities) {
code += `class ${e.name}(Base):\n __tablename__ = "${e.table_name}"\n id = Column(Integer, primary_key=True, index=True)\n`;
for (const f of e.fields) {
let parts = [`Column(${f.sa_type}`];
const fkTarget = relMap[`${e.name}.${f.name}`];
let parts = fkTarget ? [`Column(${f.sa_type}, ForeignKey("${fkTarget}.id")` ] : [`Column(${f.sa_type}`];
if (!f.nullable) parts.push('nullable=False');
if (f.default !== null && f.default !== undefined) parts.push(`default=${pyLiteral(f.default)}`);
code += ` ${f.name} = ${parts.join(', ')})\n`;
@@ -1232,7 +1241,7 @@ Blog → Author: name,email,bio(Text|None) / Post: title, content(Text), author_
if (/\bdatetime\b/i.test(f.py_type)) dtTypes.add('datetime');
}
let code = 'from pydantic import BaseModel\n';
let code = 'from pydantic import BaseModel, ConfigDict\n';
if (dtTypes.size > 0) code += `from datetime import ${[...dtTypes].sort().join(', ')}\n`;
// extra_imports: suodata pois pelkät nimet kuten "datetime" (jo käsitelty yllä)
@@ -1249,7 +1258,7 @@ Blog → Author: name,email,bio(Text|None) / Post: title, content(Text), author_
else if (f.nullable && f.py_type.includes('None')) code += ` ${f.name}: ${f.py_type} = None\n`;
else code += ` ${f.name}: ${f.py_type}\n`;
}
code += `\nclass ${e.name}Response(${e.name}Create):\n id: int\n\n class Config:\n from_attributes = True\n\n`;
code += `\nclass ${e.name}Response(${e.name}Create):\n id: int\n model_config = ConfigDict(from_attributes=True)\n\n`;
}
return code;
}