英文:
Model defined with SQLAlchemy and mypy requires a relationship parameter during initialization
问题
以下是代码的翻译部分,不包括代码本身:
我定义了如下模型:
class Base(MappedAsDataclass, DeclarativeBase):
    """子类将被转换为数据类"""
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)
    # 一对多关系
    owner_id: Mapped[uuid.UUID] = mapped_column(
        "owner_id",
        UUID(as_uuid=True),
        ForeignKey("User.id"),
    )
    owner: Mapped[User] = relationship("User", back_populates="prompts")
    # 多对多关系
    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)
我收到以下错误信息:
在调用“Prompt”时缺少位置参数“owner”和“transcripts” [call-arg]mypy
我应该如何修复它?
我已经尝试过:
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    # 文件名为“one.py”
)
英文:
I have a model defined as following:
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)
When I want to create an instance of the model:
db_prompt = models.Prompt(text=text, display_name=display_name, owner_id=user_id)
I receive the following error:
Missing positional arguments "owner", "transcripts" in call to "Prompt"  [call-arg]mypy
How can I fix it?
I already tried to:
owner: Optional[Mapped[User]] = relationship("User", back_populates="prompts")
=> Same error.
I thought mypy understands automatically that a relationship field is not required during init.
EDIT:
My 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"
  )
答案1
得分: 1
由于您的Base扩展了MappedAsDataClass,所以您在这里使用了一个Python数据类。错误消息“缺少位置参数“owner”…”来自于生成的初始化方法中,“owner”是一个必需的字段(mypy只是提醒您这一点)。
您可以通过添加init=False来将owner从必需字段中排除:
owner: Mapped[User] = relationship("User", back_populates="prompts", init=False)
或者,您可以为其提供默认值None:
owner: Mapped[User] = relationship("User", back_populates="prompts", default=None)
然而,如果您给它一个默认值为None,并且仍然使用之前的方法初始化实例:
db_prompt = models.Prompt(text=text, display_name=display_name, owner_id=user_id)
owner字段将由于数据类生成初始化方法的方式而覆盖owner_id为None。
因此,您可以选择:
- 不要使用数据类为此类自动生成初始化方法:
 
class Prompt(Base, init=False):
- 或者,添加一个后初始化方法,如果它是
None,则删除该属性,以便您的owner_id不会被空值覆盖: 
def __post_init__(self):
    if (self.owner is None):
        delattr(self, "owner")
这是一种有点巧妙的方法,不建议使用。在您进行修改后,您可以使用Prompt(owner_id=user_id, ...或Prompt(owner=user...来实例化一个实例。
英文:
Since your Base extends MappedAsDataClass, you are using a Python dataclass
here. The error message "Missing positional arguments "owner", ..." comes from
the fact that in the generated init method, "owner" is a required field (mypy is
just reminding you of that fact).
You can exclude owner from the required field by adding init=False:
owner: Mapped[User] = relationship("User", back_populates="prompts", init=False)
Alternatively, you can give it a default value of None:
owner: Mapped[User] = relationship("User", back_populates="prompts", default=None)
However, if you give it a default value of None and still use your previous
method to initialize the instance:
db_prompt = models.Prompt(text=text, display_name=display_name, owner_id=user_id)
The owner field will overwrite owner_id with None due to how a dataclass
generates the init method.
So, you can either:
- Don't use dataclass to automatically generate the init method for this class:
 
class Prompt(Base, init=False):
- Or, add a post init method and remove the the attribute if it's 
Noneso
yourowner_iddoes not get overwritten by a null value: 
def __post_init__(self):
    if (self.owner is None):
        delattr(self, "owner")
This is kind of a hack and is not recommended. After you made the modification,
you can instantiate an instance with either Prompt(owner_id=user_id, ... or
Prompt(owner=user....
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论