Flask-SQLAlchemy构造函数困惑
这段代码来自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 个回答
每当创建一个新的用户实例时,构造函数会给这个用户实例分配一个角色。如果没有这个构造函数,那么每次创建用户实例时,你就得在其他地方(比如在路由中)给它分配角色。这种做法假设用户实例只会在你的应用中创建,比如提交表单或调用路由。但这样做可能会很麻烦,可能会导致某些用户的角色是空的。如果你的应用没有处理这种情况,可能会让用户做一些他们不应该做的事情。
默认情况下,Flask-SQLAlchemy使用SQLAlchemy的基类来定义构造函数(具体可以参考Flask-SQLAlchemy构造函数)。这就是为什么你可以在Flask-SQLAlchemy中创建模型而不需要自己写构造函数,因为系统会默认创建一个。
所以,如果你想在创建模型实例时做一些特定的事情(比如根据某些逻辑给某一列赋值),那么你需要自己创建构造函数,但可以使用super(MODEL, self).__init__(**kwargs)
来节省输入模型中所有列的时间,因为super会继承基类的功能。
在面向对象编程的术语中,Python里更接近“构造函数”的是 __new__
方法,而不是 __init__
。不过老实说,这两个概念在Python中并不是完全对应的:__new__
是用来创建对象的,而 __init__
通常是用来初始化对象的。所以可以说这是一种“分开的”构造函数?
不管怎样,.__init__
是我们在Python中通常用来调整实例属性的地方。因此,上面提到的 super()
方法是用来初始化 User
这个父类的,而子类的 __init__
则负责初始化子类的部分。你需要做一些事情,比如实现一些依赖于关键字属性的逻辑等等,这些逻辑必须放在某个地方——要么在类外部(这就违反了封装的原则),要么在类或实例内部的某个方法里。__init__
是很常见的,我真的很惊讶为什么有人会不使用它。
你还提到了一些SQLAlchemy对象,比如带有声明性 __tablename__
类属性的对象,这些对象不使用 __init__
。嗯,zzzeek
就像个黑魔法师,做一些古怪而有争议的事情,所以他已经超越了我们这些普通人每天使用的 __init__
方法。;-)
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()
我觉得这里有一些预处理的步骤,而这个父类的作用是为了提前进行检查。所以每次我们查询一个用户的时候,这个检查都会发生。