Reputation: 1523
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"
)
Upvotes: 0
Views: 944
Reputation: 994
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:
class Prompt(Base, init=False):
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...
.
Upvotes: 1