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

Generic类类型提示问题:Mypy报attr-defined错误(Type[ModelType]无id属性)

解决Mypy报错:"Type[ModelType]" has no attribute "id"

问题原因

你定义的ModelType绑定了Base,但Mypy无法确认所有Base的子类都包含id类属性(即模型的表字段)。虽然你的实际业务模型都有id字段,但类型检查器没有这个明确信息,因此抛出attr-defined错误。

解决方案

方案1:给ModelType添加带id的Protocol约束

创建一个Protocol协议,明确要求类必须具备id属性,再让ModelType绑定到这个Protocol:

from typing import TypeVar, Generic, Protocol, Type
from sqlalchemy.ext.declarative import Base
from pydantic import BaseModel
from sqlalchemy.orm import Session

class HasId(Protocol):
    id: int  # 可根据实际场景调整类型,比如SQLAlchemy的`Mapped[int]`

ModelType = TypeVar("ModelType", bound=HasId)  # 替换原有的bound=Base
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)

class BaseOperations(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
    __abstract__ = True

    def __init__(self, model: Type[ModelType], db_session: Session):
        self.model = model
        self.db_session = db_session

    # 其余方法保持不变

方案2:让基类明确声明id属性

如果你的Base是SQLAlchemy的声明式基类,可以创建一个带id的抽象基类,让所有业务模型继承它,同时让ModelType绑定这个新基类:

from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class BaseWithId(Base):
    __abstract__ = True
    id = Column(Integer, primary_key=True)

# 调整ModelType的绑定目标
ModelType = TypeVar("ModelType", bound=BaseWithId)

方案3:类型断言(临时 workaround)

如果不想修改基类或协议,可以在使用self.model.id时用cast强制类型转换,但这会绕过类型检查,仅作为临时解决手段:

from typing import cast

def get_by_id(self, id_: int) -> ModelType | None:
    if hasattr(self, "deleted"):
        return (
            self.db_session.query(self.model)
            .filter(~self.deleted)
            .filter(cast(Type[HasId], self.model).id == id_)
            .first()
        )
    return self.db_session.query(self.model).filter(cast(Type[HasId], self.model).id == id_).first()

额外说明

优先选择方案1或2,它们能让类型系统正确识别代码结构,避免后续类型问题。如果使用SQLAlchemy 2.0+,建议结合DeclarativeBase和Mapped定义模型,类型提示会更精准:

from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class Base(DeclarativeBase):
    pass

class BaseWithId(Base):
    __abstract__ = True
    id: Mapped[int] = mapped_column(primary_key=True)

内容的提问来源于stack exchange,提问作者Asad Amir Khwaja

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:10:09