@@ -61,6 +61,48 @@ def __str__(self) -> str:
6161 return f"{ self .name } : { self .type } "
6262
6363
64+ @dataclass
65+ class AssociationTable :
66+ """
67+ Represents an association table for many-to-many relationships in SQLAlchemy.
68+ """
69+
70+ name : str
71+ """
72+ The name of the association table.
73+ """
74+
75+ left_table_name : str
76+ """
77+ The name of the left (source) table.
78+ """
79+
80+ left_foreign_key : str
81+ """
82+ The foreign key column name for the left table.
83+ """
84+
85+ left_primary_key : str
86+ """
87+ The full primary key reference for the left table (e.g., 'TableName.primary_key').
88+ """
89+
90+ right_table_name : str
91+ """
92+ The name of the right (target) table.
93+ """
94+
95+ right_foreign_key : str
96+ """
97+ The foreign key column name for the right table.
98+ """
99+
100+ right_primary_key : str
101+ """
102+ The full primary key reference for the right table (e.g., 'TableName.primary_key').
103+ """
104+
105+
64106@dataclass
65107class WrappedTable :
66108 """
@@ -520,32 +562,42 @@ def create_one_to_one_relationship(self, wrapped_field: WrappedField):
520562
521563 def create_one_to_many_relationship (self , wrapped_field : WrappedField ):
522564 """
523- Creates a one-to-many relationship mapping for the given wrapped field.
524- The target side of the wrapped field gets a foreign key to this table with a unique name.
525- This table gets a relationship that joins the target table with the foreign key.
565+ Creates a many-to-many relationship mapping for the given wrapped field using an association table.
566+ This allows multiple instances of the source table to reference the same instances of the target table.
526567
527- :param wrapped_field: The field for the one -to-many relationship.
568+ :param wrapped_field: The field for the many -to-many relationship.
528569 """
529570
530571 # get the target table
531572 target_wrapped_table = self .get_table_of_wrapped_field (wrapped_field )
532573
533- # create a foreign key to this on the remote side
534- fk_name = f"{ self .tablename .lower ()} _{ wrapped_field .field .name } { self .ormatic .foreign_key_postfix } "
535- fk_type = (
536- f"Mapped[{ module_and_class_name (Optional )} [{ module_and_class_name (int )} ]]"
537- )
538- fk_column_constructor = f"mapped_column(ForeignKey('{ self .full_primary_key_name } ', use_alter=True), nullable=True, use_existing_column=True)"
539- target_wrapped_table .foreign_keys .append (
540- ColumnConstructor (fk_name , fk_type , fk_column_constructor )
574+ # create association table name
575+ association_table_name = f"{ self .tablename .lower ()} _{ wrapped_field .field .name } _association"
576+
577+ # create foreign key names for the association table
578+ left_fk_name = f"{ self .tablename .lower ()} { self .ormatic .foreign_key_postfix } "
579+ right_fk_name = f"{ target_wrapped_table .tablename .lower ()} { self .ormatic .foreign_key_postfix } "
580+
581+ # create association table metadata
582+ association_table = AssociationTable (
583+ name = association_table_name ,
584+ left_table_name = self .tablename ,
585+ left_foreign_key = left_fk_name ,
586+ left_primary_key = self .full_primary_key_name ,
587+ right_table_name = target_wrapped_table .tablename ,
588+ right_foreign_key = right_fk_name ,
589+ right_primary_key = target_wrapped_table .full_primary_key_name ,
541590 )
542591
543- # create a relationship with a list to collect the other side
592+ # add association table to ORMatic
593+ self .ormatic .association_tables .append (association_table )
594+
595+ # create a relationship with a list using the association table
544596 rel_name = f"{ wrapped_field .field .name } "
545597 rel_type = (
546598 f"Mapped[{ module_and_class_name (List )} [{ target_wrapped_table .tablename } ]]"
547599 )
548- rel_constructor = f"relationship('{ target_wrapped_table .tablename } ', foreign_keys='[ { target_wrapped_table . tablename } . { fk_name } ] ', post_update=True )"
600+ rel_constructor = f"relationship('{ target_wrapped_table .tablename } ', secondary=' { association_table_name } ', cascade='save-update, merge' )"
549601 self .relationships .append (
550602 ColumnConstructor (rel_name , rel_type , rel_constructor )
551603 )
0 commit comments