sqlmodel: How to fix NoForeignKeysError: Could not determine join condition between parent/child tables on relationship

Problem

When running your Python script using sqlmodel with model definition code such as

from typing import List, Optional
from sqlmodel import ForeignKey, Relationship, SQLModel, Field
from sqlalchemy import create_engine
from sqlmodel import SQLModel

class Manufacturer(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(description="Manufacturer name")
    products: List["Product"] = Relationship(
        back_populates="manufacturer"
    )

class Product(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    mpn: Optional[str] = Field(description="Manufacturer part number", index=True)
    manufacturer: Optional[Manufacturer] = Relationship(
        back_populates="products"
    )

you see an error message such as

Traceback (most recent call last):
  File "/home/uli/Nextcloud/Experimental/InventreeLCSC/TestModel.py", line 25, in <module>
    product: Product = Product(mpn="123456")
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 4, in __init__
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/state.py", line 566, in _initialize_instance
    manager.dispatch.init(self, args, kwargs)
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/event/attr.py", line 497, in __call__
    fn(*args, **kw)
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/mapper.py", line 4396, in _event_on_init
    instrumenting_mapper._check_configure()
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/mapper.py", line 2388, in _check_configure
    _configure_registries({self.registry}, cascade=True)
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/mapper.py", line 4204, in _configure_registries
    _do_configure_registries(registries, cascade)
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/mapper.py", line 4245, in _do_configure_registries
    mapper._post_configure_properties()
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/mapper.py", line 2405, in _post_configure_properties
    prop.init()
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/interfaces.py", line 584, in init
    self.do_init()
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/relationships.py", line 1644, in do_init
    self._setup_join_conditions()
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/relationships.py", line 1884, in _setup_join_conditions
    self._join_condition = jc = JoinCondition(
                                ^^^^^^^^^^^^^^
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/relationships.py", line 2310, in __init__
    self._determine_joins()
  File "/home/uli/.local/lib/python3.12/site-packages/sqlalchemy/orm/relationships.py", line 2444, in _determine_joins
    raise sa_exc.NoForeignKeysError(
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Manufacturer.products - there are no foreign keys linking these tables.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.

Solution

When you encounter this error, the solution is simple: Explicitly specify the ForeignKey relationship by adding:

sa_relationship=ForeignKey("product.id")

as an argument to both sides of the Relationship field definition.

Full example:

from typing import List, Optional
from sqlmodel import ForeignKey, Relationship, SQLModel, Field
from sqlalchemy import create_engine
from sqlmodel import SQLModel

class Manufacturer(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(description="Manufacturer name")
    products: List["Product"] = Relationship(
        back_populates="manufacturer",
        sa_relationship=ForeignKey("product.id")
    )

class Product(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    mpn: Optional[str] = Field(description="Manufacturer part number", index=True)
    manufacturer: Optional[Manufacturer] = Relationship(
        back_populates="products",
        sa_relationship=ForeignKey("manufacturer.id")
    )