Flask-SQLAlchemy构造函数困惑

0 投票
3 回答
1603 浏览
提问于 2025-04-18 15:07

这段代码来自Miguel Grindberg的书《Flask Web Development》。在models.py文件里,我们有三个类:一个是角色类(Role),里面有三种角色(用户、版主和管理员);一个是用户类(User),包含了用户的ID、用户名、邮箱、角色ID、密码哈希和确认状态;还有一个是权限类(Permission),代码在下面。在第9章第114页,他给用户类添加了一些代码,用来检查邮箱地址是否属于管理员,如果是,就把这个用户添加到管理员角色里。如果不是,这个用户就会被添加到默认的角色(用户)中……

def __init__(self, **kwargs):
    super(User, self).__init__(**kwargs)
    if self.role is None:
        if self.email == current_app.config['FLASKY_ADMIN']:
            self.role = Role.query.filter_by(permissions=0xff).first()
    if self.role is None:
        self.role = Role.query.filter_by(default=True).first()

我想问的是,为什么这段代码需要一个构造函数?在文件的其他部分并没有使用构造函数(完整代码在下面),那为什么这段代码需要一个呢?我在Stack上看过这个问题(Flask-SQLAlchemy Constructor

class Permission:
    FOLLOW = 0x01
    COMMENT = 0x02
    WRITE_ARTICLES = 0x04
    MODERATE_COMMENTS = 0x08
    ADMINISTER = 0x80


class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    default = db.Column(db.Boolean, default=False, index=True)
    permissions = db.Column(db.Integer)
    users = db.relationship('User', backref='role', lazy='dynamic')

@staticmethod
def insert_roles():
    roles = {
        'User': (Permission.FOLLOW |
                 Permission.COMMENT |
                 Permission.WRITE_ARTICLES, True),
        'Moderator': (Permission.FOLLOW |
                      Permission.COMMENT |
                      Permission.WRITE_ARTICLES |
                      Permission.MODERATE_COMMENTS, False),
        'Administrator': (0xff, False)
    }
    for r in roles:
        role = Role.query.filter_by(name=r).first()
        if role is None:
            role = Role(name=r)
        role.permissions = roles[r][0]
        role.default = roles[r][1]
        db.session.add(role)
    db.session.commit()

def __repr__(self):
    return '<Role %r>' % self.name


class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    password_hash = db.Column(db.String(128))
    confirmed = db.Column(db.Boolean, default=False)

    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        if self.role is None:
            if self.email == current_app.config['FLASKY_ADMIN']:
                self.role = Role.query.filter_by(permissions=0xff).first()
            if self.role is None:
                self.role = Role.query.filter_by(default=True).first()

3 个回答

0

每当创建一个新的用户实例时,构造函数会给这个用户实例分配一个角色。如果没有这个构造函数,那么每次创建用户实例时,你就得在其他地方(比如在路由中)给它分配角色。这种做法假设用户实例只会在你的应用中创建,比如提交表单或调用路由。但这样做可能会很麻烦,可能会导致某些用户的角色是空的。如果你的应用没有处理这种情况,可能会让用户做一些他们不应该做的事情。

默认情况下,Flask-SQLAlchemy使用SQLAlchemy的基类来定义构造函数(具体可以参考Flask-SQLAlchemy构造函数)。这就是为什么你可以在Flask-SQLAlchemy中创建模型而不需要自己写构造函数,因为系统会默认创建一个。

所以,如果你想在创建模型实例时做一些特定的事情(比如根据某些逻辑给某一列赋值),那么你需要自己创建构造函数,但可以使用super(MODEL, self).__init__(**kwargs)来节省输入模型中所有列的时间,因为super会继承基类的功能。

0

在面向对象编程的术语中,Python里更接近“构造函数”的是 __new__ 方法,而不是 __init__。不过老实说,这两个概念在Python中并不是完全对应的:__new__ 是用来创建对象的,而 __init__ 通常是用来初始化对象的。所以可以说这是一种“分开的”构造函数?

不管怎样,.__init__ 是我们在Python中通常用来调整实例属性的地方。因此,上面提到的 super() 方法是用来初始化 User 这个父类的,而子类的 __init__ 则负责初始化子类的部分。你需要做一些事情,比如实现一些依赖于关键字属性的逻辑等等,这些逻辑必须放在某个地方——要么在类外部(这就违反了封装的原则),要么在类或实例内部的某个方法里。__init__ 是很常见的,我真的很惊讶为什么有人会不使用它。

你还提到了一些SQLAlchemy对象,比如带有声明性 __tablename__ 类属性的对象,这些对象不使用 __init__。嗯,zzzeek 就像个黑魔法师,做一些古怪而有争议的事情,所以他已经超越了我们这些普通人每天使用的 __init__ 方法。;-)

0
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
if self.role is None:
    if self.email == current_app.config['FLASKY_ADMIN']:
        self.role = Role.query.filter_by(permissions=0xff).first()
if self.role is None:
    self.role = Role.query.filter_by(default=True).first()

我觉得这里有一些预处理的步骤,而这个父类的作用是为了提前进行检查。所以每次我们查询一个用户的时候,这个检查都会发生。

撰写回答