Skip to content

Type (typehint) error for db.relationship #1318

Open
@cainmagi

Description

@cainmagi

Problem Description

The typehint of

db.relationship("...", secondary=..., back_populates="...")

should be sq_orm.Relationship[...], not sq_orm.RelationshipProperty[...].

The mismatch of the typehint causes the manual annotation supported by sqlalchemy fails:

image

How to fix it

Go here:

def relationship(
self, *args: t.Any, **kwargs: t.Any
) -> sa_orm.RelationshipProperty[t.Any]:
"""A :func:`sqlalchemy.orm.relationship` that applies this extension's
:attr:`Query` class for dynamic relationships and backrefs.
.. versionchanged:: 3.0
The :attr:`Query` class is set on ``backref``.
"""
self._set_rel_query(kwargs)
return sa_orm.relationship(*args, **kwargs)

Make this modification:

    def relationship(
        self, *args: t.Any, **kwargs: t.Any
-   ) -> sa_orm.RelationshipProperty[t.Any]:
+   ) -> sa_orm.Relationship[t.Any]:
        """A :func:`sqlalchemy.orm.relationship` that applies this extension's

Things will get corrected.

It is also recommended to modify this place:

def _relation(
self, *args: t.Any, **kwargs: t.Any
) -> sa_orm.RelationshipProperty[t.Any]:

But the following place should NOT be changed, because it is consistent with sq_orm:

def dynamic_loader(
self, argument: t.Any, **kwargs: t.Any
) -> sa_orm.RelationshipProperty[t.Any]:

Codes with typehint errors when using flask-sqlalchemy

# -*- coding: UTF-8 -*-

try:
    from typing import List
except ImportError:
    from builtins import list as List

from flask_sqlalchemy import SQLAlchemy
import sqlalchemy as sa
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass


class Base(DeclarativeBase, MappedAsDataclass):
    """The base class for creating SQLAlchemy models.

    All mixins are defined in the mro list.

    All metadata of are defined as attributes.
    """


db = SQLAlchemy(model_class=Base)


roles = db.Table(
    "role_users",
    sa.Column("user_id", sa.ForeignKey("user.id"), primary_key=True),
    sa.Column("role_id", sa.ForeignKey("role.id"), primary_key=True),
)


class User(db.Model):
    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    # Expression of type "RelationshipProperty[Any]" cannot be assigned to declared type "Mapped[List[Role]]"
    # "RelationshipProperty[Any]" is incompatible with "Mapped[List[Role]]"Pylance[reportAssignmentType] 
    # (https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportAssignmentType)
    roles: Mapped[List["Role"]] = db.relationship(
        "Role", secondary=roles, back_populates="users", default_factory=list
    )


class Role(db.Model):
    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    # Expression of type "RelationshipProperty[Any]" cannot be assigned to declared type "Mapped[List[User]]"
    #  "RelationshipProperty[Any]" is incompatible with "Mapped[List[User]]"Pylance[reportAssignmentType] 
    # (https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportAssignmentType)
    users: Mapped[List["User"]] = db.relationship(
        "User", secondary=roles, back_populates="roles", default_factory=list
    )

Codes working perfectly if only using sqlalchemy

# -*- coding: UTF-8 -*-

try:
    from typing import List
except ImportError:
    from builtins import list as List

import sqlalchemy as sa
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass


class Base(DeclarativeBase, MappedAsDataclass):
    """The base class for creating SQLAlchemy models.

    All mixins are defined in the mro list.

    All metadata of are defined as attributes.
    """


roles = sa.Table(
    "role_users",
    Base.metadata,
    sa.Column("user_id", sa.ForeignKey("user.id"), primary_key=True),
    sa.Column("role_id", sa.ForeignKey("role.id"), primary_key=True),
)


class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    roles: Mapped[List["Role"]] = relationship(
        "Role", secondary=roles, back_populates="users", default_factory=list
    )


class Role(Base):
    __tablename__ = "roles"
    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    users: Mapped[List["User"]] = relationship(
        "User", secondary=roles, back_populates="roles", default_factory=list
    )

Environment:

  • Python version: 3.10.13
  • Flask-SQLAlchemy version: 3.1.1
  • SQLAlchemy version: 2.0.28

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions