这个脑筋急转弯我已经玩了一天了,我刚刚跨越了自助的界限,转而寻求他人的帮助。本质上,我想映射某些模型以使用 FastAPI 构建 API(学习管理系统 API)。
型号: 用户(具有某些角色:管理员、学生、教师), 轮廓, 学生、老师。
到目前为止,我已经能够通过这些模型及其关系完成以下任务: (已安装并使用:FastAPI、SqlAlchemy、Pydantic)
class User(Base):
__tablename__= "users"
id: Mapped[uuid_pkg.UUID] = mapped_column(default_factory=uuid_pkg.uuid4, primary_key=True, unique=True, index=True)
email: Mapped[str] = mapped_column(String(), unique=True, index=True, default=None)
hashed_password: Mapped[str] = mapped_column(String(), nullable=False, default=None)
user_role = relationship("UserRole", back_populates="user", uselist=False,)
我可以使用架构和 CRUD 函数基于上述内容创建用户。
class UserRole(Base):
__tablename__ = "user_roles"
user_id = Column(
UUID(as_uuid=True),
ForeignKey("users.id"),
primary_key=True,
nullable=False,
)
role_id = Column(
UUID(as_uuid=True),
ForeignKey("roles.id"),
primary_key=True,
nullable=False,
)
role = relationship("Role")
user = relationship("User", back_populates="user_role", uselist=False) #one-to-one
我可以使用架构和 CRUD 函数基于上述内容创建用户角色。
class Role(Base):
__tablename__ = "roles"
id = Column(
UUID(as_uuid=True), primary_key=True, index=True, default=uuid4
)
name = Column(String(100), index=True) #Admin, Student, Teacher
description = Column(Text) #describes the Role.name column
__table_args__ = (
UniqueConstraint("user_id", "role_id", name="unique_user_role"),
)
我可以使用架构和 CRUD 函数基于上述内容创建角色。
class Profile(Base):
__tablename__ = "profile"
id: Mapped[uuid_pkg.UUID] = mapped_column(default_factory=uuid_pkg.uuid4, primary_key=True, unique=True)
first_name= mapped_column(String(50))
last_name= mapped_column(String(50))
phone: Mapped[str] = mapped_column(String(15), default=None)
avatar: Mapped[str] = mapped_column(String, default="https://something-something.com/xf.jpg")
user_id: Mapped[uuid_pkg.UUID] = mapped_column(ForeignKey("users.id"), default_factory=uuid_pkg.uuid4, nullable=False)
user = relationship("User", back_populates="profile", uselist=False)
class BaseRole(Base):
#holds similar data across role type: Admin, Student, Teacher
__abstract__ = True #abstract so no table gets created
id: Mapped[uuid_pkg.UUID] = mapped_column(default_factory=uuid_pkg.uuid4, primary_key=True, unique=True)
identification_document: Mapped[str] = mapped_column(String(), unique=True, index=True, default=None)
identification_number: Mapped[str] = mapped_column(String(), unique=True, index=True, default=None)
identification_type: Mapped[str] = mapped_column(String(), index=True, default=None)
areas_of_interest: Mapped[List | None] = mapped_column(MutableList.as_mutable(ARRAY(String)))
class Student(BaseRole):
__tablename__ = "student"
class InterestedCourse(enum.Enum): #one course track at any time
SME = "sme"
IT = "it"
NA = "n/a"
course: Mapped[str] = mapped_column(Enum(InterestedCourse), default=InterestedCourse.NA)
@declared_attr
def user_id(cls):
return mapped_column(ForeignKey("users.id"), primary_key=True, index=True, unique=True, nullable=False)
@declared_attr
def user(cls):
return relationship("User", back_populates=Student.__tablename__, uselist=False)
__mapper_args__ = {'polymorphic_identity':'student', 'concrete':True}
class Teacher(BaseRole):
__tablename__ = "teacher"
......
我使用下面的代码在我的路由上设置了权限,这有效:
current_user: models.User = Security(
dependencies.get_current_active_user,
scopes=[Role.ADMIN["name"]],
),
错误(此错误来自 BaseRole 和 Student 和 Teacher 的抽象具体类):
sqlalchemy.exc.InvalidRequestError: Mapper properties (i.e. deferred,column_property(), relationship(), etc.) must be declared as @declared_attr callables on declarative mixin classes. For dataclass field() objects, use a lambda:
注意:user_roles 表中的
user_id
并非故意唯一:用户可以是管理员和教师。
代码结束。
我的问题:
混合!
我几乎只是在我需要的地方使用 BaseRole 作为 Mixin:
class Student(Base, BaseRole):
__tablename__ = "student"
class InterestedCourse(enum.Enum): #one course track at any time
SME = "sme"
IT = "it"
NA = "n/a"
course: Mapped[str] = mapped_column(Enum(InterestedCourse), default=InterestedCourse.NA)
# @declared_attr
# def user_id(cls):
# return mapped_column(ForeignKey("users.id"), primary_key=True, //index=True, unique=True, nullable=False)
# @declared_attr
# def user(cls):
# return relationship("User", back_populates=Student.__tablename__, uselist=False)
# __mapper_args__ = {'polymorphic_identity':'student', 'concrete':True} #could have stayed