You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SQLAlchemy与mypy定义的模型初始化时关联参数缺失问题

问题描述

我定义了如下ORM模型:

class Base(MappedAsDataclass, DeclarativeBase):
    """subclasses will be converted to dataclasses"""


class Prompt(Base):
    __tablename__ = "Prompt"

    id = mapped_column(
        "id",
        UUID(as_uuid=True),
        primary_key=True,
        index=True,
        server_default=sa.text("gen_random_uuid()"),
    )
    created_at = mapped_column(
        "created_at", DateTime(timezone=True), server_default=func.now(), nullable=False
    )
    text: Mapped[str] = mapped_column(Text)
    display_name: Mapped[str] = mapped_column("display_name", String)

    # many to one relationship
    owner_id: Mapped[uuid.UUID] = mapped_column(
        "owner_id",
        UUID(as_uuid=True),
        ForeignKey("User.id"),
    )

    owner: Mapped[User] = relationship("User", back_populates="prompts")

    # many-to-many relationship
    transcripts: Mapped[List[Transcript]] = relationship(
        "Transcript",
        secondary=transcript_prompt_association,
        back_populates="prompts",
    )

    deleted: Mapped[bool] = mapped_column("deleted", Boolean, default=False)

当我创建模型实例时:

db_prompt = models.Prompt(text=text, display_name=display_name, owner_id=user_id)

收到mypy报错:

Missing positional arguments "owner", "transcripts" in call to "Prompt"  [call-arg]mypy

我曾尝试将关联字段设为Optional:

owner: Optional[Mapped[User]] = relationship("User", back_populates="prompts")

但仍触发相同报错。我原本认为mypy会自动识别关联字段无需在初始化时传入。

附我的mypy.ini配置:

[mypy]
python_version = 3.11
plugins = pydantic.mypy,sqlalchemy.ext.mypy.plugin
ignore_missing_imports = True
disallow_untyped_defs = True
exclude = (?x)(
    alembic    # files named "one.py"
  )

请问该如何解决此问题?

解决方法

问题根源在于MappedAsDataclass会将所有Mapped字段自动纳入dataclass的构造函数参数,包括SQLAlchemy的关系字段,而这些字段本不需要在实例化时传入。解决方式如下:

1. 给关系字段添加init=False参数

在定义relationship时,通过init=False告诉dataclass不要将该字段加入构造函数的必填参数列表:

修改owner字段:

owner: Mapped[User] = relationship("User", back_populates="prompts", init=False)

修改transcripts字段:

transcripts: Mapped[List[Transcript]] = relationship(
    "Transcript",
    secondary=transcript_prompt_association,
    back_populates="prompts",
    init=False
)

这样修改后,dataclass生成的__init__方法就不会要求传入owner和transcripts,mypy的报错会直接消失。

2. 可选:给关系字段设置默认值(按需使用)

如果需要在某些场景下允许初始化时传入关系字段,可以保留init=True(默认值),但给字段设置合适的默认值:

比如给transcripts设置空列表默认值:

transcripts: Mapped[List[Transcript]] = relationship(
    "Transcript",
    secondary=transcript_prompt_association,
    back_populates="prompts",
    default_factory=list
)

但通常SQLAlchemy关系字段不需要在实例化时手动传入,因此第一种方法更贴合ORM的使用习惯。

为什么Optional无法解决问题?

Optional[Mapped[...]]仅表示字段值可以为None,但dataclass的规则是:没有默认值的字段,即使类型是Optional,依然会被当作构造函数的必填参数。只有通过init=False直接排除字段,或者给字段设置默认值,才能让mypy不再要求传入该参数。

内容的提问来源于stack exchange,提问作者siva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 02:45:31