【问题标题】:Can I have 2 user_loader methods in my Flask web application我的 Flask Web 应用程序中可以有 2 个 user_loader 方法吗
【发布时间】:2020-05-16 09:32:09
【问题描述】:

在我的 Web 应用程序中,用户和工作人员可以登录,我有 2 种不同的模型供他们使用。但是,当我尝试为工作模型创建 user_loader 方法时,我收到错误

这是我的代码

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


@login_manager.worker_loader
def load_worker(worker_id):
    return Worker.query.get(int(user_id))


class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    fname = db.Column(db.String(30), nullable=False)
    lname = db.Column(db.String(30), nullable=False)
    phone = db.Column(db.String(11), unique=True, nullable=False)
    email = db.Column(db.String(25), unique=True, nullable=False)
    location = db.Column(db.String(15), nullable=False)
    password = db.Column(db.String(60), nullable=False)
    mode = db.Column(db.String(10), nullable=False, default='User')
    address = db.relationship('Address', backref='user_address', lazy=True)
    workorder = db.relationship('JobLog', backref='order', lazy=True)


class Worker(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    fname = db.Column(db.String(30), nullable=False)
    lname = db.Column(db.String(30), nullable=False)
    phone = db.Column(db.String(11), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    location = db.Column(db.String(15), nullable=False)
    job = db.Column(db.String(20), nullable=False)
    description = db.Column(db.Text, nullable=False,
                            default='Insert your personal description here')
    image = db.Column(db.String(20), nullable=False, default='default.jpg')
    password = db.Column(db.String(60), nullable=False)
    mode = db.Column(db.String(10), nullable=False, default='Worker')
    active = db.Column(db.Boolean, nullable=False, default=False)
    workrequest = db.relationship('JobLog', backref='request', lazy=True)

【问题讨论】:

    标签: python flask-sqlalchemy flask-login


    【解决方案1】:

    取而代之的是,考虑应用模型继承模式。有一些关于如何做到这一点的好文档here。这将有助于减少代码重复并简化用户加载逻辑。

    来自文档:

    当映射器配置为继承关系时,SQLAlchemy 具有多态加载元素的能力,这意味着 单个查询可以返回多种类型的对象。

    这将允许,如果您的继承模式是 Person -> UserPerson -> Worker,那么您的用户加载器可能如下所示:

    @login_manager.user_loader
    def user_loader(id):
        return Person.query.get(id)
    

    然后,该加载器将返回层次结构中的任何类型,具体取决于 id 关联的类型。

    示例模式可能如下所示:

    class Person(db.Model, UserMixin):
    
        id = db.Column(db.Integer, primary_key=True)
        fname = db.Column(db.String(30), nullable=False)
        lname = db.Column(db.String(30), nullable=False)
        phone = db.Column(db.String(11), unique=True, nullable=False)
        email = db.Column(db.String(25), unique=True, nullable=False)
        location = db.Column(db.String(15), nullable=False)
        password = db.Column(db.String(60), nullable=False)
        mode = db.Column(db.String(10), nullable=False, default='User')
    
        type = Column(String(20))  # this is the discriminator column
    
        __mapper_args__ = {
            'polymorphic_on':type,
        }
    
    class User(Person):
        id = db.Column(db.Integer, db.ForeignKey('person.id'), primary_key=True)
        address = db.relationship('Address', backref='user_address', lazy=True)
        workorder = db.relationship('JobLog', backref='order', lazy=True)
    
        __mapper_args__ = {
            'polymorphic_identity':'user'
        }
    
    class Engineer(Person):
        id = db.Column(db.Integer, db.ForeignKey('person.id'), primary_key=True)
        job = db.Column(db.String(20), nullable=False)
        description = db.Column(db.Text, nullable=False,
                                default='Insert your personal description here')
        image = db.Column(db.String(20), nullable=False, default='default.jpg')
        active = db.Column(db.Boolean, nullable=False, default=False)
        workrequest = db.relationship('JobLog', backref='request', lazy=True)
    
        __mapper_args__ = {
            'polymorphic_identity':'worker'
        }
    

    在该代码中,每个模型都由其自己的表表示,该表表示其自身与基本人员模型之间的差异。当您加载Person 的实例时,查询将返回UserWorker,并表现得好像它由单个表支持。

    我没有测试代码,所以如果有任何错误,请随时编辑。

    【讨论】:

      【解决方案2】:

      @login_manager.user_loader 是存在于login_manager 类中的装饰器——它不代表不同类型的用户(即userworker)——所以你必须将它称为user_loader在这两种情况下。

      但问题是——你不知道你是否应该仅从用户 ID 中检索工作人员或用户。由于工作人员实际上是用户,因此没有两个模型 - 将公共属性合并到一个模型中,并将工作人员相关数据保存在一个单独的模型中,并在它们之间建立关系(或者使它们在用户模型上可以为空以使其更简单)。

      您已经有一个字段告诉您用户是普通用户还是工作人员,所以我会将其合并到一个模型中。您可以通过与今天相同的机制保持工作人员和工作之间的引用(如果他们是用户则设置工作订单,如果他们是工作人员则设置工作请求),或者您可以在 JobLog 之间创建多对多关系和 User 表 - 并使用 User 类型来确定他们是主管还是工人。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-08
        • 2012-04-14
        • 2013-12-20
        • 2020-10-08
        • 1970-01-01
        • 2019-12-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多