dropbox / sqlalchemy-stubs

Mypy plugin and stubs for SQLAlchemy
Apache License 2.0
569 stars 101 forks source link

Can not set nullable relationship to None #166

Open lexicalunit opened 4 years ago

lexicalunit commented 4 years ago

Given the following relationship:

class Game(Base):
    __tablename__ = "games"
    id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
    users = relationship("User", back_populates="game")

class User(Base):
    __tablename__ = "users"
    xid = Column(BigInteger, primary_key=True, nullable=False)
    game_id = Column(Integer, ForeignKey("games.id", ondelete="SET NULL"), nullable=True)
    game = relationship("Game", back_populates="users")

I'm trying to do the following in the application:

user.game = None

This should be fine and it does work as intended in practice, however I get an error from mypy:

error: Incompatible types in assignment (expression has type "None", variable has type "Game")
lexicalunit commented 4 years ago

Note that the workaround in this case would be to use user.game_id = None instead, but I feel like that's not as nice as being able to use the relationship as intended.

killthekitten commented 4 years ago

@lexicalunit a simple but not ideal workaround would be:

game = cast(Optional[Game], relationship("Game", back_populates="users"))

If I understand it correctly, making it work nicer would require creating a plugin. It could be similar to the one supporting Column, which enables support for nullable and some other column parameters.

In that case, the plugin would need to know whether the foreign key column is nullable or not, which could be inferred from the foreign_keys argument, i.e.:

game = relationship("Game", foreign_keys=game_id, back_populates="users")