【问题标题】:Flask-SQLAlchemy Constructor ConfusionFlask-SQLAlchemy 构造函数混淆
【发布时间】:2014-09-20 06:23:58
【问题描述】:

此代码再次来自 Miguel Grindberg 的书“Flask Web Development”。在 models.py 中,我们有 3 个类,一个 Role 类,它有 3 个角色(User、Moderator、Administrator)、一个 User 类(id、username、email、role_id、password_hash、confirmed)和一个 Permsission 类(代码如下)。在 chp 9 第 114 页中,他向 User 类添加了一些代码,以检查电子邮件地址是否属于管理员,如果是,则将其添加到角色中。如果不是,则将用户添加到默认角色(用户)。 . .

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()

【问题讨论】:

    标签: python python-2.7 flask sqlalchemy flask-sqlalchemy


    【解决方案1】:

    每当创建新的用户实例时,构造函数都会为该用户实例分配一个角色。如果未创建此构造函数,则每次创建 User 实例时,您都必须在其他地方(例如在路由中)为其分配一个角色。但这假设只有在提交表单和/或调用路由时才会在您的应用程序中创建用户实例。这可能会造成混乱,并为某些用户留下 None 角色的漏洞,如果您的应用不期望这样,可能会让用户在网站上执行他们不允许执行的操作。

    默认情况下 Flask-SQLAlchemy 使用 SQLAlchemy 的基类定义构造函数(根据Flask-SQLAlchemy Constructor)。这就是为什么您可以在 Flask-SQLAlchemy 中创建模型而无需创建构造函数的原因,因为它是默认创建的。

    因此,如果您想在创建模型实例时做一些特定的事情(例如根据某些逻辑为列分配值),那么您需要自己创建构造函数但可以使用super(MODEL, self).__init__(**kwargs) 来保存您键入该模型中的 N 列的时间,因为 super 继承了基类。

    【讨论】:

      【解决方案2】:
      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()
      

      我认为有某种预处理,超类化用于更早的检查。所以每次我们查询用户时都会发生这种检查。

      【讨论】:

      • 我明白了,感谢您的回答cyberra。虽然我仍然不确定为什么我们需要一个构造函数来添加这段代码。
      • 然后尝试在不使用此构造函数的情况下编写代码,看看 - 它的外观和维护代码的难度。
      【解决方案3】:

      在典型的 OO 术语中,Python 中更接近的“构造函数”等价物是 __new__ 方法,而不是 __init__。虽然坦率地说,这并不能很好地映射到 Python 概念:您有 __new__ 用于创建对象的方法,而 __init__ 通常用于初始化。那么也许这是一个“拆分”构造函数?

      无论如何,.__init__ 是 Python 中我们通常调整实例属性的地方,因此上面的 super() 方法对 User 超类进行初始化,而子类的其余部分 __init__ 对子类进行初始化。你必须做一些事情,比如在某处实现一些依赖关键字属性的逻辑等——要么在类之外执行(这违反了封装原则),要么在类或实例内部的某个方法中执行。 __init__ 很典型,我很惊讶为什么有人不使用它。

      您还列出了一些 SQLAlchemy 对象,其中包含不使用 __init__ 的声明性 __tablename__ 类属性。好吧,zzzeek 是一个黑魔法师,做着邪恶和道德上有争议的事情,所以他已经超越了我们普通人每天使用的 peon 的__init__ 方法。 ;-)

      【讨论】:

      • 感谢您的回答,虽然我还是有点困惑。所以,你是说他应该为课程的其余部分使用构造函数,即def __init__(self): 然后self.email=email 等等?我还在 Flask-SQLAlchemy 文档快速入门中注意到,他们使用这样的构造函数,但他们不调用基类构造函数,这也让我感到困惑。我认为在子类中使用自己的构造函数时调用基类构造函数是“强制性的”?
      猜你喜欢
      • 2013-12-25
      • 1970-01-01
      • 2018-04-01
      • 1970-01-01
      • 2017-03-18
      • 1970-01-01
      • 2020-01-12
      • 2015-02-14
      • 2014-03-22
      相关资源
      最近更新 更多