diff --git a/MycroForge.CLI/CodeGen/EntityLinker.cs b/MycroForge.CLI/CodeGen/EntityLinker.cs index a200b74..ebf42bd 100644 --- a/MycroForge.CLI/CodeGen/EntityLinker.cs +++ b/MycroForge.CLI/CodeGen/EntityLinker.cs @@ -4,6 +4,23 @@ namespace MycroForge.CLI.CodeGen; public partial class EntityLinker { + #region AssociationTable + + private static readonly string[] AssociationTable = + [ + "from sqlalchemy import Column, ForeignKey, Table", + "from db.entities.entity_base import EntityBase", + "", + "%left_entity%_%right_entity%_mapping = Table(", + "\t\"%left_entity%_%right_entity%_mapping\",", + "\tEntityBase.metadata,", + "\tColumn(\"%left_entity%_id\", ForeignKey(\"%left_table%.id\"), primary_key=True),", + "\tColumn(\"%right_entity%_id\", ForeignKey(\"%right_table%.id\"), primary_key=True),", + ")" + ]; + + #endregion + private readonly ProjectContext _context; private readonly string _left; private readonly string _right; @@ -21,12 +38,12 @@ public partial class EntityLinker var right = await LoadEntity(_right); left.AppendColumn( - $"\t{right.FieldName}: Mapped[\"{right.ClassName}\"] = relationship(back_populates=\"{left.FieldName}\")" + $"\t{right.FieldName}: Mapped[\"{right.ClassName.Pascalize()}\"] = relationship(back_populates=\"{left.FieldName}\")" ); right.AppendColumns([ $"\t{left.FieldName}_id: Mapped[int] = mapped_column(ForeignKey(\"{left.TableName}.id\"))", - $"\t{left.FieldName}: Mapped[\"{left.ClassName}\"] = relationship(back_populates=\"{right.FieldName}\")" + $"\t{left.FieldName}: Mapped[\"{left.ClassName.Pascalize()}\"] = relationship(back_populates=\"{right.FieldName}\")" ]); await _context.WriteFile(left.Path, left.Rewrite()); @@ -39,11 +56,12 @@ public partial class EntityLinker var right = await LoadEntity(_right); left.Import(from: "typing", import: "List"); - left.AppendColumn($"\t{right.FieldName.Pluralize()}: Mapped[List[\"{right.ClassName}\"]] = relationship()"); + left.AppendColumn( + $"\t{right.FieldName.Pluralize()}: Mapped[List[\"{right.ClassName.Pascalize()}\"]] = relationship()"); right.AppendColumns([ $"\t{left.FieldName}_id: Mapped[int] = mapped_column(ForeignKey(\"{left.TableName}.id\"))", - $"\t{left.FieldName}: Mapped[\"{left.ClassName}\"] = relationship(back_populates=\"{right.FieldName}\")" + $"\t{left.FieldName}: Mapped[\"{left.ClassName.Pascalize()}\"] = relationship(back_populates=\"{right.FieldName}\")" ]); await _context.WriteFile(left.Path, left.Rewrite()); @@ -58,13 +76,13 @@ public partial class EntityLinker left.Import(from: "typing", import: "Optional"); left.AppendColumns([ $"\t{right.FieldName}_id: Mapped[Optional[int]] = mapped_column(ForeignKey(\"{right.TableName}.id\"))", - $"\t{right.FieldName}: Mapped[\"{right.ClassName}\"] = relationship(back_populates=\"{left.FieldName.Pluralize()}\")" + $"\t{right.FieldName}: Mapped[\"{right.ClassName.Pascalize()}\"] = relationship(back_populates=\"{left.FieldName.Pluralize()}\")" ]); left.Import(from: "typing", import: "List"); right.AppendColumns([ $"\t{left.FieldName}_id: Mapped[int] = mapped_column(ForeignKey(\"{left.TableName}.id\"))", - $"\t{left.FieldName.Pluralize()}: Mapped[List[\"{left.ClassName}\"]] = relationship(back_populates=\"{right.FieldName}\")" + $"\t{left.FieldName.Pluralize()}: Mapped[List[\"{left.ClassName.Pascalize()}\"]] = relationship(back_populates=\"{right.FieldName}\")" ]); await _context.WriteFile(left.Path, left.Rewrite()); @@ -73,50 +91,60 @@ public partial class EntityLinker public async Task ManyToMany() { - // var left = await LoadEntity(_left); - // var right = await LoadEntity(_right); - // - // left.AppendImportIfNotExists( - // $"db.entities.{right.FieldName}", - // $"{right.ClassName}", - // $"from db.entities.{right.FieldName} import {right.ClassName}" - // ); - // left.AppendImportIfNotExists( - // "typing", - // "Optional", - // "from typing import Optional" - // ); - // left.AppendColumn([ - // $"\t{right.FieldName}_id: Mapped[Optional[int]] = mapped_column(ForeignKey(\"{right.TableName}.id\"))", - // $"\t{right.FieldName}: Mapped[\"{right.ClassName}\"] = relationship(back_populates=\"{left.FieldName.Pluralize()}\")\n" - // ]); - // - // right.AppendImportIfNotExists( - // $"db.entities.{left.FieldName}", - // $"{left.ClassName}", - // $"from db.entities.{left.FieldName} import {left.ClassName}" - // ); - // left.AppendImportIfNotExists( - // "typing", - // "List", - // "from typing import List" - // ); - // right.AppendColumn([ - // $"\t{left.FieldName}_id: Mapped[int] = mapped_column(ForeignKey(\"{left.TableName}.id\"))", - // $"\t{left.FieldName.Pluralize()}: Mapped[List[\"{left.ClassName}\"]] = relationship(back_populates=\"{right.FieldName}\")\n" - // ]); - // - // await _context.WriteFile(left.Path, left.Rewrite()); - // await _context.WriteFile(right.Path, right.Rewrite()); + var left = await LoadEntity(_left); + var right = await LoadEntity(_right); + + var associationTable = string.Join('\n', AssociationTable); + associationTable = associationTable + .Replace("%left_entity%", left.ClassName.Underscore().ToLower()) + .Replace("%right_entity%", right.ClassName.Underscore().ToLower()) + .Replace("%left_table%", left.TableName) + .Replace("%right_table%", right.TableName); + var associationTablePath = + $"{Features.Db.FeatureName}/entities/associations/{left.TableName.Singularize()}_{right.TableName.Singularize()}_mapping.py"; + + await _context.CreateFile(associationTablePath, associationTable); + + var associationTableImport = associationTablePath + .Replace(".py", "") + .Replace('/', '.') + .Replace('\\', '.') + .Split('.') + ; + + var associationTableImportPath = string.Join('.', associationTableImport[..^1]); + var associationTableImportName = associationTableImport[^1]; + + left.Import(from: string.Join('.', associationTableImportPath), import: associationTableImportName); + left.Import(from: "typing", import: "List"); + right.Import(from: string.Join('.', associationTableImportPath), import: associationTableImportName); + right.Import(from: "typing", import: "List"); + + left.AppendColumns([ + $"\t{right.FieldName.Pluralize()}: Mapped[List[\"{right.ClassName.Pascalize()}\"]] = relationship(back_populates=\"{left.FieldName.Pluralize()}\", secondary={associationTableImportName})" + ]); + + right.AppendColumns([ + $"\t{left.FieldName.Pluralize()}: Mapped[List[\"{left.ClassName.Pascalize()}\"]] = relationship(back_populates=\"{right.FieldName.Pluralize()}\", secondary={associationTableImportName})" + ]); + + await _context.WriteFile(left.Path, left.Rewrite()); + await _context.WriteFile(right.Path, right.Rewrite()); } private async Task LoadEntity(string name) { - var path = GetEntityPath(name); + var path = $"{Features.Db.FeatureName}/entities"; + + if (name.Split(':').Select(s => s.Trim()).ToArray() is { Length: 2 } fullName) + { + path = Path.Join(path, fullName[0]); + name = fullName[1]; + } + + path = Path.Join(path, $"{name.Underscore().ToLower()}.py"); var entity = new EntityModel(name, path, await _context.ReadFile(path)); entity.Initialize(); return entity; } - - private string GetEntityPath(string name) => $"{Features.Db.FeatureName}/entities/{name.Underscore().ToLower()}.py"; } \ No newline at end of file diff --git a/MycroForge.CLI/Features/Db.cs b/MycroForge.CLI/Features/Db.cs index b5646ea..5dbf204 100644 --- a/MycroForge.CLI/Features/Db.cs +++ b/MycroForge.CLI/Features/Db.cs @@ -8,11 +8,12 @@ public sealed class Db : IFeature private static readonly string[] Settings = [ - "connectionstring = \"mysql+asyncmy://root:root@localhost:3306/example\"", - "", "class DbSettings:", "\tdef get_connectionstring() -> str:", - "\t\treturn connectionstring" + "\t\t# Example", + "\t\t# connectionstring = \"mysql+asyncmy://root:root@localhost:3306/your_database\"", + "\t\t# return connectionstring", + "\t\traise Exception(\"DbSettings.get_connectionstring was not implemented.\")", ]; private static readonly string[] AsyncSession =