Sequelize与Apollo一对多关联查询返回null及新增数据重复外键问题排查
问题1:查询羊只品种名称返回null的解决方法
你遇到的第一个问题核心是Sequelize关联的别名和GraphQL TypeDefs里的字段名不匹配。
看你的关联配置:
Sheep.belongsTo(Breed);
Sequelize对于belongsTo关联,如果没有指定as选项,默认的别名是模型名的驼峰式小写(也就是breed),而不是外键字段名breed_id。但你的GraphQL TypeDef里,Sheep类型的关联字段定义的是breed_id: Breed!,这就导致Resolver里查询返回的breed字段(来自include的Breed模型)无法匹配TypeDef里的breed_id字段,所以返回null。
你后来修改include为{model: Breed, as: "breed"}后解决问题,其实更规范的做法是同步调整TypeDef里的字段名,让它更语义化:
type Sheep { # 其他字段... breed: Breed! # 把breed_id改成breed,和Sequelize的别名对应 # 其他字段... }
这样查询的时候就可以直接写:
query Get_all_sheep { get_all_sheep { breed { breed_name } } }
如果不想修改TypeDef的字段名,也可以在关联配置里指定as: "breed_id"来匹配:
Sheep.belongsTo(Breed, { as: "breed_id", foreignKey: "breed_id" });
之后Resolver里include的时候用{model: Breed, as: "breed_id"}即可。
问题2:新增Mutation返回重复外键字段的解决方法
你现在遇到的breed_id和breedId同时存在的问题,是Sequelize的自动驼峰转换特性导致的:Sequelize默认会把数据库里的下划线命名字段(比如breed_id)转换成驼峰式的属性名(breedId)添加到模型实例中,同时保留原始的下划线字段名,所以会出现重复。
这里给你几个常用的解决方案:
方案1:全局开启underscored模式
在Sequelize的初始化配置里,设置define: { underscored: true },这样所有模型都会统一使用下划线命名的字段,不会生成驼峰式的属性:
const sequelize = new Sequelize(database, username, password, { // 其他配置(比如dialect、host等) define: { underscored: true, // 启用下划线命名规则 timestamps: true // 保留createdAt/updatedAt的话需要开启 } });
配置后模型实例里就只会保留breed_id、color_id等下划线字段,不会出现breedId这类驼峰字段。
方案2:单个模型设置underscored
如果不想全局修改,可以在Sheep模型的定义里单独开启该规则:
module.exports = (sequelize, DataTypes) => { const Sheep = sequelize.define('Sheep', { // 你的字段定义... }, { underscored: true // 仅该模型启用下划线规则 }); // 关联配置... return Sheep; };
方案3:在Resolver里手动过滤字段
如果不想修改Sequelize配置,可以在新增Mutation的Resolver里,手动删除不需要的驼峰字段:
// 示例:新增羊只的Mutation resolver create_sheep: async (parent, args) => { const newSheep = await Sheep.create(args); // 删除重复的驼峰字段 delete newSheep.dataValues.breedId; delete newSheep.dataValues.colorId; delete newSheep.dataValues.markingId; return newSheep; }
这种方法适合临时解决,但如果字段较多会比较繁琐。
方案4:用attributes指定返回字段
在创建或查询时,通过attributes选项明确指定要返回的字段,排除驼峰式字段:
const newSheep = await Sheep.create(args, { attributes: [ 'sheep_id', 'tag_id', 'dob', 'sex', 'breed_id', 'color_id', 'marking_id', 'scrapie_id', 'name', 'weight_at_birth', 'date_deceased', 'date_last_bred', 'createdAt', 'updatedAt', 'sire', 'dam' ] });
这样返回的实例就只会包含你指定的下划线字段。
内容的提问来源于stack exchange,提问作者Nika Bo

