不同用户角色的模型关系:我错过了什么?

问题描述 投票:0回答:1

这个脑筋急转弯我已经玩了一天了,我刚刚跨越了自助的界限,转而寻求他人的帮助。本质上,我想映射某些模型以使用 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
并非故意唯一:用户可以是管理员和教师。

代码结束。

我的问题:

  1. 模型关系是理想的方式(就建立用户角色、用户配置文件和角色模型而言)还是我过度扭曲了关系并需要回到我的绘图板?
  2. 我继承抽象类的原因是我不必重写学生和教师的公共字段,我该如何解决错误?我错过了什么?
python sqlalchemy fastapi pydantic user-roles
1个回答
0
投票

混合!

我几乎只是在我需要的地方使用 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
© www.soinside.com 2019 - 2024. All rights reserved.