From 42b71dbf779fb87c532929b50541e8c0f114cdb5 Mon Sep 17 00:00:00 2001 From: jaakko Date: Mon, 13 Apr 2026 13:18:11 +0300 Subject: [PATCH] Templatejen laatu: declarative_base, ConfigDict, ForeignKey MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- network-poc/frontend/src/pages/index.astro | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/network-poc/frontend/src/pages/index.astro b/network-poc/frontend/src/pages/index.astro index d38660c..63ebcf9 100644 --- a/network-poc/frontend/src/pages/index.astro +++ b/network-poc/frontend/src/pages/index.astro @@ -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; }