diff --git a/README.rst b/README.rst index cd94205e..a91bec14 100644 --- a/README.rst +++ b/README.rst @@ -103,6 +103,8 @@ values must be delimited by commas, e.g. ``--options noconstraints,nobidi``): * ``noconstraints``: ignore constraints (foreign key, unique etc.) * ``nocomments``: ignore table/column comments * ``noindexes``: ignore indexes + * ``noidsuffix``: prevent the special naming logic for single column many-to-one + and one-to-one relationships (see `Relationship naming logic`_ for details) * ``declarative`` diff --git a/src/sqlacodegen/generators.py b/src/sqlacodegen/generators.py index e9a08cde..b580840e 100644 --- a/src/sqlacodegen/generators.py +++ b/src/sqlacodegen/generators.py @@ -748,6 +748,7 @@ class DeclarativeGenerator(TablesGenerator): "use_inflect", "nojoined", "nobidi", + "noidsuffix", } def __init__( @@ -1084,7 +1085,7 @@ def generate_relationship_name( # If there's a constraint with a single column that ends with "_id", use the # preceding part as the relationship name - if relationship.constraint: + if relationship.constraint and "noidsuffix" not in self.options: is_source = relationship.source.table is relationship.constraint.table if is_source or relationship.type not in ( RelationshipType.ONE_TO_ONE, diff --git a/tests/test_generator_declarative.py b/tests/test_generator_declarative.py index 2d30782c..5c31ec41 100644 --- a/tests/test_generator_declarative.py +++ b/tests/test_generator_declarative.py @@ -1532,6 +1532,62 @@ class SimpleItems(Base): ) +@pytest.mark.parametrize("generator", [["noidsuffix"]], indirect=True) +def test_named_foreign_key_constraints_with_noidsuffix( + generator: CodeGenerator, +) -> None: + Table( + "simple_items", + generator.metadata, + Column("id", INTEGER, primary_key=True), + Column("container_id", INTEGER), + ForeignKeyConstraint( + ["container_id"], ["simple_containers.id"], name="foreignkeytest" + ), + ) + Table( + "simple_containers", + generator.metadata, + Column("id", INTEGER, primary_key=True), + ) + + validate_code( + generator.generate(), + """\ +from typing import List, Optional + +from sqlalchemy import ForeignKeyConstraint, Integer +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship + +class Base(DeclarativeBase): + pass + + +class SimpleContainers(Base): + __tablename__ = 'simple_containers' + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + + simple_items: Mapped[List['SimpleItems']] = relationship('SimpleItems', \ +back_populates='simple_containers') + + +class SimpleItems(Base): + __tablename__ = 'simple_items' + __table_args__ = ( + ForeignKeyConstraint(['container_id'], ['simple_containers.id'], \ +name='foreignkeytest'), + ) + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + container_id: Mapped[Optional[int]] = mapped_column(Integer) + + simple_containers: Mapped['SimpleContainers'] = relationship('SimpleContainers', \ +back_populates='simple_items') +""", + ) + + # @pytest.mark.xfail(strict=True) def test_colname_import_conflict(generator: CodeGenerator) -> None: Table(