用SQLAlchemy和mypy定义的模型在初始化期间需要一个关系参数。

huangapple go评论58阅读模式
英文:

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_idNone

因此,您可以选择:

  1. 不要使用数据类为此类自动生成初始化方法:
class Prompt(Base, init=False):
  1. 或者,添加一个后初始化方法,如果它是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:

  1. Don't use dataclass to automatically generate the init method for this class:
class Prompt(Base, init=False):
  1. Or, add a post init method and remove the the attribute if it's None so
    your owner_id does 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....

huangapple
  • 本文由 发表于 2023年6月2日 08:13:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76386442.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定