【问题标题】:Sequelize, convert entity to plain object续集,将实体转换为普通对象
【发布时间】:2014-03-24 14:01:20
【问题描述】:

我对 javascript 不是很熟悉,而且非常棒,因为我无法将新属性添加到使用 ORM 名称 Sequelize.js 从数据库中获取的对象。

为了避免这种情况,我使用了这个技巧:

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
}).success(function (sensors) {
        var nodedata = JSON.parse(JSON.stringify(node)); // this is my trick
        nodedata.sensors = sensors;
        nodesensors.push(nodedata);
        response.json(nodesensors);
});

那么,向对象添加新属性的通常方法是什么。

如果有帮助,我使用 sequelize-postgres 版本 2.0.x。

更新。 console.log(node):

{ dataValues: 
   { nodeid: 'NodeId',
     name: 'NameHere',
     altname: 'Test9',
     longname: '',
     latitude: 30,
     longitude: -10,
     networkid: 'NetworkId',
     farmid: '5',
     lastheard: Mon Dec 09 2013 04:04:40 GMT+0300 (FET),
     id: 9,
     createdAt: Tue Dec 03 2013 01:29:09 GMT+0300 (FET),
     updatedAt: Sun Feb 23 2014 01:07:14 GMT+0300 (FET) },
  __options: 
   { timestamps: true,
     createdAt: 'createdAt',
     updatedAt: 'updatedAt',
     deletedAt: 'deletedAt',
     touchedAt: 'touchedAt',
     instanceMethods: {},
     classMethods: {},
     validate: {},
     freezeTableName: false,
     underscored: false,
     syncOnAssociation: true,
     paranoid: false,
     whereCollection: { farmid: 5, networkid: 'NetworkId' },
     schema: null,
     schemaDelimiter: '',
     language: 'en',
     defaultScope: null,
     scopes: null,
     hooks: { beforeCreate: [], afterCreate: [] },
     omitNull: false,
     hasPrimaryKeys: false },
  hasPrimaryKeys: false,
  selectedValues: 
   { nodeid: 'NodeId',
     name: 'NameHere',
     longname: '',
     latitude: 30,
     longitude: -110,
     networkid: 'NetworkId',
     farmid: '5',
     lastheard: Mon Dec 09 2013 04:04:40 GMT+0300 (FET),
     id: 9,
     createdAt: Tue Dec 03 2013 01:29:09 GMT+0300 (FET),
     updatedAt: Sun Feb 23 2014 01:07:14 GMT+0300 (FET),
     altname: 'Test9' },
  __eagerlyLoadedAssociations: [],
  isDirty: false,
  isNewRecord: false,
  daoFactoryName: 'Nodes',
  daoFactory: 
   { options: 
      { timestamps: true,
        createdAt: 'createdAt',
        updatedAt: 'updatedAt',
        deletedAt: 'deletedAt',
        touchedAt: 'touchedAt',
        instanceMethods: {},
        classMethods: {},
        validate: {},
        freezeTableName: false,
        underscored: false,
        syncOnAssociation: true,
        paranoid: false,
        whereCollection: [Object],
        schema: null,
        schemaDelimiter: '',
        language: 'en',
        defaultScope: null,
        scopes: null,
        hooks: [Object],
        omitNull: false,
        hasPrimaryKeys: false },
     name: 'Nodes',
     tableName: 'Nodes',
     rawAttributes: 
      { nodeid: [Object],
        name: [Object],
        altname: [Object],
        longname: [Object],
        latitude: [Object],
        longitude: [Object],
        networkid: [Object],
        farmid: [Object],
        lastheard: [Object],
        id: [Object],
        createdAt: [Object],
        updatedAt: [Object] },
     daoFactoryManager: { daos: [Object], sequelize: [Object] },
     associations: {},
     scopeObj: {},
     primaryKeys: {},
     primaryKeyCount: 0,
     hasPrimaryKeys: false,
     autoIncrementField: 'id',
     DAO: { [Function] super_: [Function] } } }

我想接下来,你的想法是:“好的,这很简单,只需将你的属性添加到 dataValues 即可。”

node.selectedValues.sensors = sensors;
node.dataValues.sensors = sensors;

我添加了这行,这不起作用

【问题讨论】:

  • node 不能是传统对象。也许它是一个缓冲区? console.log(node); 在你的技巧台词前说什么?
  • 请看帖子更新。

标签: node.js sequelize.js


【解决方案1】:

我使用我自己的模型 mixin 来获得方便的响应

const camelcaseKeys = require('camelcase-keys');

const initMixins = Sequelize => {
    // Convert model to plain object and camelcase keys for response
    Sequelize.Model.prototype.toPlainObject = function({ camelcase = false } = {}) {
        const plain = this.get({ plain: true });
        if (camelcase) {
            return camelcaseKeys(plain, { deep: true });
        } else {
            return plain;
        }
    };
};

module.exports = {
    initMixins,
};
// usage
const userModel = await UserModel.findOne();
console.log(userModel.toPlainObject());

【讨论】:

    【解决方案2】:

    现在回答有点晚了,但万一有人遇到这个问题并需要解决方案......

    (假设这是在 async/await 函数中)

    const sensors = JSON.parse(JSON.stringify(await 
        db.Sensors.findAll({
        where: {
            nodeid: node.nodeid
        }
    })))
    

    您可以查询并返回它是一个普通对象、对象数组等......

    【讨论】:

      【解决方案3】:

      如果您想对所有查询都进行此操作,也可以尝试:

      var sequelize = new Sequelize('database', 'username', 'password', {query:{raw:true}})
      

      访问Setting all queries to raw = true sequelize

      【讨论】:

      • 那么 - 当您进行 upsert 时,这是如何工作的?!
      【解决方案4】:

      您可以使用查询选项{raw: true} 来返回原始结果。您的查询应如下所示:

      db.Sensors.findAll({
        where: {
          nodeid: node.nodeid
        },
        raw: true,
      })
      

      如果您与 include 有关联,则该关联会变平。所以,我们可以使用另一个参数nest:true

      db.Sensors.findAll({
        where: {
          nodeid: node.nodeid
        },
        raw: true,
        nest: true,
      })
      

      【讨论】:

      • "raw: true" 会以某种方式使关联模型注入的数组变平。到目前为止,我发现的唯一解决方法是 configs = JSON.parse(JSON.stringify(configs));
      • 这比公认的答案更简单、更好。谢谢
      • @SoichiHayashi 实际上,当您考虑它时,情况正好相反... ;) 如果没有 raw: true,Sequelize 会以某种方式将结果展开为对象。 sql 查询的结果已经被展平。我发现它有用几次来获得一个平坦的结果......这个答案+1。
      • @SoichiHayashi 获取原始但嵌套的结果,您可以使用 nest: trueraw: true
      • 知道如何将raw:true 作为全局设置传递,因此无需将其传递给每个查询吗?谢谢
      【解决方案5】:

      对于嵌套的 JSON 纯文本

      db.model.findAll({
        raw : true ,
        nest : true
      })
      

      【讨论】:

      • 它不适用于一对多关联,最好使用 .map(obj => obj.get({ plain: true }))
      • 其实我说的是 db.model.findOne 和一对多关联。
      【解决方案6】:

      您可以使用地图功能。这对我有用。

      db.Sensors
          .findAll({
              where: { nodeid: node.nodeid }
           })
          .map(el => el.get({ plain: true }))
          .then((rows)=>{
              response.json( rows )
           });
      

      【讨论】:

      • 谢谢这是我使用演员的最佳解决方案,只是results = results.map(el => el.get({ plain: true }))
      【解决方案7】:

      最好且最简单的做法是:

      只需使用 Sequelize

      的默认方式
      db.Sensors.findAll({
          where: {
              nodeid: node.nodeid
          },
          raw : true // <----------- Magic is here
      }).success(function (sensors) {
              console.log(sensors);
      });
      

      注意:[options.raw]:返回原始结果。有关更多信息,请参见 sequelize.query 信息。


      对于嵌套结果/如果我们有包含模型,在最新版本的 sequlize 中,

      db.Sensors.findAll({
          where: {
              nodeid: node.nodeid
          },
          include : [
              { model : someModel }
          ]
          raw : true , // <----------- Magic is here
          nest : true // <----------- Magic is here
      }).success(function (sensors) {
              console.log(sensors);
      });
      

      【讨论】:

      • 这个答案非常有用,使用 sequleize 提供的本机功能,在我的情况下,当我在套接字中发送多行时,发生堆栈溢出错误。
      • 如果你有一个子数据数组,你会得到错误的结果。因此,如果发布的查询仅返回一个 sensors 和一组子 someModel,您将得到一个 arraysensors,每个 someModel 各有一个。
      • raw 结果也省略了所有 VIRTUAL 属性值。
      【解决方案8】:

      这是我用来从sequelize v4 查询中获取具有非字符串化值和所有嵌套关联的普通响应对象的方法。

      使用 纯 JavaScript (ES2015+):

      const toPlain = response => {
        const flattenDataValues = ({ dataValues }) => {
          const flattenedObject = {};
      
          Object.keys(dataValues).forEach(key => {
            const dataValue = dataValues[key];
      
            if (
              Array.isArray(dataValue) &&
              dataValue[0] &&
              dataValue[0].dataValues &&
              typeof dataValue[0].dataValues === 'object'
            ) {
              flattenedObject[key] = dataValues[key].map(flattenDataValues);
            } else if (dataValue && dataValue.dataValues && typeof dataValue.dataValues === 'object') {
              flattenedObject[key] = flattenDataValues(dataValues[key]);
            } else {
              flattenedObject[key] = dataValues[key];
            }
          });
      
          return flattenedObject;
        };
      
        return Array.isArray(response) ? response.map(flattenDataValues) : flattenDataValues(response);
      };
      

      使用 lodash(更简洁一点):

      const toPlain = response => {
        const flattenDataValues = ({ dataValues }) =>
          _.mapValues(dataValues, value => (
            _.isArray(value) && _.isObject(value[0]) && _.isObject(value[0].dataValues)
              ? _.map(value, flattenDataValues)
              : _.isObject(value) && _.isObject(value.dataValues)
                ? flattenDataValues(value)
                : value
          ));
      
        return _.isArray(response) ? _.map(response, flattenDataValues) : flattenDataValues(response);
      };
      

      用法

      const res = await User.findAll({
        include: [{
          model: Company,
          as: 'companies',
          include: [{
            model: Member,
            as: 'member',
          }],
        }],
      });
      
      const plain = toPlain(res);
      
      // 'plain' now contains simple db object without any getters/setters with following structure:
      // [{
      //   id: 123,
      //   name: 'John',
      //   companies: [{
      //     id: 234,
      //     name: 'Google',
      //     members: [{
      //       id: 345,
      //       name: 'Paul',
      //     }]
      //   }]
      // }]
      

      【讨论】:

      • 谢谢! toPlain 按预期工作,也解决了我的问题。
      • 这很简洁,但是如果您覆盖任何模型的 toJSON 方法,那就不好了。我经常这样做以删除或转换 createdAt/updatedAt 值。到目前为止,唯一对我有用的是 JSON.parse(JSON.stringify(response))。
      • 您的回答促使我深入了解Model 的文档和toJSON。在我的模型定义中修复了自定义 getter 后,toJSON 对我来说效果很好,所以不需要像你这样的自定义实现。
      • 我相信这是唯一可行的解​​决方案。 get({plain:true}) 添加所有未定义的未请求字段。 {raw:true, nest:true} 不能很好地处理 1:n 嵌套关联。
      【解决方案9】:

      我找到了一个解决方案,它适用于使用原生 JavaScript 函数的嵌套模型和数组。

      var results = [{},{},...]; //your result data returned from sequelize query
      var jsonString = JSON.stringify(results); //convert to string to remove the sequelize specific meta data
      
      var obj = JSON.parse(jsonString); //to make plain json
      // do whatever you want to do with obj as plain json
      

      【讨论】:

      • 如此简单,但功能强大并保持模型逻辑。我不知道它的效率如何,但对我来说已经足够了。您可以将其缩写为: let res = JSON.parse(JSON.stringify(result));
      【解决方案10】:

      正如 CharlesA 在他的回答中指出的那样,.values()technically deprecated,尽管 docs 中没有明确指出这一事实。如果您不想在查询中使用{ raw: true },首选方法是在结果上调用.get()

      .get() 但是,是实例的方法,而不是数组的方法。如上面链接问题中所述,Sequelize 返回实例对象的本机数组(维护者不打算更改它),因此您必须自己遍历数组:

      db.Sensors.findAll({
          where: {
              nodeid: node.nodeid
          }
      }).success((sensors) => {
          const nodeData = sensors.map((node) => node.get({ plain: true }));
      });
      

      【讨论】:

      • 这对我有用!虽然我不知道“get”是 JavaScript 还是 Sequelize 函数。请赐教。谢谢
      • @antew 这是从数据库返回的对象上的一个方法 - 它是 sequelize 库的一部分,而不是原生 javascript 函数。
      • 我花了很长时间才完成这项工作!谢谢你。太难了,因为我有很多嵌套的包含,这也是一个错误。所以我必须做多个查询,我不能因为这个!!!
      【解决方案11】:

      对于最近遇到此问题的人,.values 自 Sequelize 3.0.0 起已弃用。改用.get() 来获取普通的javascript 对象。所以上面的代码会变成:

      var nodedata = node.get({ plain: true });
      

      在此处续集docs

      【讨论】:

      • 如果我错了,请纠正我 - 但我认为这不适用于行数组?例如,使用 .findAndcount 您必须对结果行进行 .map 并在每个行上返回 .get 才能获得所需的内容。
      • 运行 JSON.stringify 或 res.json 可能更容易(如果您在 express 中工作)。
      • @backdesk - 没有在阵列上尝试过 - 但从 docs 看来它应该返回相同的东西。
      • 使用.get() 不会转换包含的关联,请改用.get({ plain: true })。感谢您提及文档中的建议。
      • 如果您已经执行了查询,因此也无法发出raw: true
      【解决方案12】:

      如果我猜对了,您希望将 sensors 集合添加到 node。如果您在两个模型之间有映射,则可以使用在每个实例上定义的 include 功能 explained herevalues getter。你可以找到文档for that here

      后者可以这样使用:

      db.Sensors.findAll({
        where: {
          nodeid: node.nodeid
        }
      }).success(function (sensors) {
        var nodedata = node.values;
      
        nodedata.sensors = sensors.map(function(sensor){ return sensor.values });
        // or
        nodedata.sensors = sensors.map(function(sensor){ return sensor.toJSON() });
      
        nodesensors.push(nodedata);
        response.json(nodesensors);
      });
      

      nodedata.sensors = sensors 也有可能也可以工作。

      【讨论】:

      • 谢谢你,我需要的只是:node.values :)
      猜你喜欢
      • 2020-01-14
      • 2012-08-02
      • 2016-02-20
      • 2016-09-14
      • 1970-01-01
      • 1970-01-01
      • 2016-04-14
      • 2016-08-07
      • 2018-02-20
      相关资源
      最近更新 更多