45

我正在使用 Sequelize 为用户记录进行数据库查找,并且我希望模型的默认行为返回该password记录的字段。该password字段是一个哈希,但我仍然不想返回它。

我有几个可行的选择,但似乎没有一个特别好:

  1. findWithoutPassword为模型创建一个自定义类方法,User并在该方法User.find中对attributes集合进行操作,如Sequelize 文档中所示

  2. 做一个正常User.find并过滤控制器中的结果(不是首选)

  3. 使用其他一些库来去除不需要的属性

有没有更好的办法?最重要的是,如果有一种方法可以在 Sequelize 模型定义中指定从不返回该password字段,但我还没有找到一种方法来做到这一点。

4

8 回答 8

114

另一种方法是向 User 模型添加默认范围。

将此添加到模型的选项对象中

defaultScope: {
  attributes: { exclude: ['password'] },
}

或者您可以创建一个单独的范围以仅在某些查询中使用它。

将此添加到模型的选项对象中

scopes: {
  withoutPassword: {
    attributes: { exclude: ['password'] },
  }
}

然后你可以在查询中使用它

User.scope('withoutPassword').findAll();
于 2018-01-20T15:45:00.917 回答
77

我建议覆盖该toJSON功能:

sequelize.define('user', attributes, {
  instanceMethods: {
    toJSON: function () {
      var values = Object.assign({}, this.get());

      delete values.password;
      return values;
    }
  }
});

或在续集 v4

const User = sequelize.define('user', attributes, {});

User.prototype.toJSON =  function () {
  var values = Object.assign({}, this.get());

  delete values.password;
  return values;
}

toJSON当数据返回给用户时调用,因此最终用户将看不到密码字段,但它仍然可以在您的代码中使用。

Object.assign克隆返回的对象 - 否则您将从实例中完全删除该属性。

于 2015-01-16T08:15:55.827 回答
34

我喜欢结合使用 Pawan 的两个答案并声明以下内容:

defaultScope: {
    attributes: { exclude: ['password'] },
},
scopes: {
    withPassword: {
        attributes: { },
    }
}

这允许我在默认情况下排除密码,并withPassword在需要时使用范围显式返回密码,例如在运行登录方法时。

userModel.scope('withPassword').findAll()

这样可以确保在通过引用字段包含用户时不返回密码,例如

accountModel.findAll({
    include: [{
        model: userModel,
        as: 'user'
    }]
})
于 2018-08-18T00:12:29.910 回答
30

也许你可以exclude在你的时候添加你的属性find,如下所示:

var User = sequelize.define('user', attributes);

User.findAll({
    attributes: {
        exclude: ['password']
    }
});

阅读文档以获取更多详细信息

于 2016-09-18T14:51:53.647 回答
5

我可以通过在返回的字段中添加一个 getter 来完成这项工作undefined

firstName: {
      type: DataTypes.STRING,
      get() {
        return undefined;
      }
    }

当模型包含在另一个模型中时,接受的答案不起作用。

我有一个虚拟字段,比如说fullName这取决于我想隐藏的字段,比如说firstNamelastName. 在这种情况下,基于的解决方案defaultScope不起作用。

于 2019-12-14T17:20:32.867 回答
4

下面的代码对我有用。我们希望在运行时访问实例属性,但在将数据发送到客户端之前将其删除。

const Sequelize = require('sequelize')

const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname')

const PROTECTED_ATTRIBUTES = ['password', 'token']

const Model = Sequelize.Model

class User extends Model {
  toJSON () {
    // hide protected fields
    let attributes = Object.assign({}, this.get())
    for (let a of PROTECTED_ATTRIBUTES) {
      delete attributes[a]
    }
    return attributes
  }
}

User.init({
  email: {
    type: Sequelize.STRING,
    unique: true,
    allowNull: false,
    validate: {
      isEmail: true
    }
  },
  password: {
    type: Sequelize.STRING,
    allowNull: false
  },
  token: {
    type: Sequelize.STRING(16),
    unique: true,
    allowNull: false
  },
},
{
  sequelize,
  modelName: 'user'
})

module.exports = User

Github 要点

于 2019-05-19T08:25:59.877 回答
0

有一个用于范围属性的插件,如此所述。

我按照接受的答案中提到的那样进行了覆盖,除了我调用了原始文件toJSON而不是api文档getthis.constructor.super_.prototype.toJSON.apply(this, arguments)描述的那样

于 2015-12-05T22:21:25.743 回答
0

您可以通过排除属性以简单的方式执行此操作。请参阅下面的代码(注释行)

    Patient.findByPk(id, {
            attributes: {
              exclude: ['UserId', 'DiseaseId'] // Removing UserId and DiseaseId from Patient response data
            },
            include: [
              { 
                model: models.Disease
              },
              {
                model: models.User,
                attributes: {
                  exclude: ['password'] // Removing password from User response data
                }
              }
           ]
    })
    .then(data => {
        res.status(200).send(data);
    })
    .catch(err => {
        res.status(500).send({
            message: `Error retrieving Patient with id ${id} : ${err}`
        });
    });
于 2020-05-21T06:25:19.440 回答