You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sequelize6关联查询为何需加dataValues才能取别名属性

Sequelize 6.x 嵌套关联查询别名需通过dataValues访问的原因

复现场景:Sequelize 6.x 中执行三层关联查询,关联链路为ForTrade belongsTo Artwork belongsTo Viewer,查询时对Viewer模型的wallet_address字段设置别名trader_address,直接通过实例属性路径访问别名返回undefined,必须追加.dataValues才能获取正确值。

关联查询代码参考:

let _bid = await ForTrade.findOne({
  where: {id:_id},
  include:[{
    model:Artwork,
    include: [{
      model:Viewer,
      attributes:[["wallet_address", "trader_address"]] 
    }]
  }]
});
// 直接访问返回undefined
console.log(_bid.artwork.viewer.trader_address);
// 追加dataValues可以正常取值
console.log(_bid.artwork.viewer.dataValues.trader_address);

关联定义代码参考:

Artwork.belongsTo(Viewer, {foreignKey: 'uploader_id'});
ForTrade.belongsTo(Artwork, {foreignKey: "artwork_id"});

核心原因

Sequelize的Model实例对实例.属性名这种直接访问方式,是基于模型初始化时预定义的字段做的代理映射,只有提前在模型的attributes配置中显式声明的字段,才会被挂载到实例顶层供直接访问。
上述场景中的trader_address是查询阶段临时定义的字段别名,从来没有在Viewer模型的定义中注册过,因此不会被Sequelize的属性代理逻辑识别,直接访问自然返回undefined。

为什么dataValues可以正常取值

dataValues是Sequelize实例上专门存储数据库原始查询结果的内部对象,所有从数据库返回的字段——不管是模型预定义的字段、查询时临时生成的别名、SQL层面的计算字段——都会被完整存入这个对象,不会经过模型属性规则的过滤,因此可以直接取到临时别名对应的值。

更规范的访问方案

不推荐在业务代码中长期直接读取dataValues,可以用两种更稳妥的方式实现无dataValues访问:

  • 转普通对象后访问
    查询完成后调用实例的get({plain: true})方法,把Sequelize实例转为普通JavaScript对象,所有字段(包括临时别名)都会直接挂在对象顶层:
    const _bidPlain = _bid.get({plain: true});
    // 可直接访问,无需dataValues
    console.log(_bidPlain.artwork.viewer.trader_address);
    
  • 模型层预定义虚拟字段
    如果trader_address是业务中高频使用的别名,可以在Viewer模型定义时新增一个VIRTUAL类型的属性,配置getter映射到wallet_address字段,后续所有查询场景都可以直接通过实例属性访问。

注意事项

Sequelize实例的顶层属性和dataValues并不是完全同步的:

  • 模型预定义字段、通过实例setter方法合法赋值的字段,会在顶层属性和dataValues之间同步更新
  • 查询临时别名、原生SQL返回的未声明字段、手动写入dataValues的自定义值,不会自动代理到实例顶层,只能通过dataValues或者转plain对象后访问。

内容的提问来源于stack exchange,提问作者user938363

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:33:24