AWS Amplify创建Items关联ItemCategory报错的解决咨询
问题背景
我的schema.graphql定义如下:
type Items @model { id: ID! ItemName: String! ItemDescription: String ItemCategory: Categories ItemEvents: [Events] ItemAttributes: [Attributes] createdAt: AWSDateTime updatedAt:AWSDateTime } type Categories @model { id: ID! CategoryName: String! CategoryItems: [Items] createdAt: AWSDateTime updatedAt:AWSDateTime }
AWS Amplify自动生成的mutation.js内容:
export const createItems = /* GraphQL */ ` mutation CreateItems( $input: CreateItemsInput! $condition: ModelItemsConditionInput ) { createItems(input: $input, condition: $condition) { id ItemName ItemDescription ItemCategory { id CategoryName CategoryItems { id ItemName ItemDescription createdAt updatedAt } createdAt updatedAt } createdAt updatedAt } } `;
前端App.js中通过表单创建Item的代码:
{ Categories.map(Category => ( // 遍历分类列表 <form onSubmit={(e) => createItemInput(e, Category)}> <div className='item_input'> <input type="text" name="itemName" placeholder="New Item"/> </div> <button type="submit" id = "newItemButton"> <svg width="30" height="30" viewBox="0 0 120 120" version="1.1" xmlns="http://www.w3.org/2000/svg"> <line x1='0' y1='60' x2='120' y2='60' stroke='black' /> <line x1='60' y1='0' x2='60' y2='120' stroke='black' /> </svg> </button> </form> )) }
相关状态和函数:
const initialFormState = { ItemName: "", ItemDescription: "", ItemCategory: {}}; // 表单输入初始状态 const [Items, setItems] = useState([]); // Item数组 const [formData, setFormData] = useState(initialFormState); // 表单数据 async function createItem() { // 创建Item的函数 if (!formData.ItemName) return; await API.graphql({ query: mutations.createItems, variables: { input: formData } }) } function createItemInput(e, currentCategory) { e.preventDefault(); var iname = e.target.itemName.value; setFormData({ ...formData, 'ItemName': iname, 'ItemCategory': currentCategory}); createItem(); }
问题现象
点击创建按钮时出现错误:"The variables input contains a field name 'ItemCategory' that is not defined for input object type 'CreateItemsInput'"。虽然mutation返回字段包含ItemCategory,但调用时无法传入关联的Category对象。直接在DynamoDB手动创建带ItemCategory映射的Item可行,但代码调用不被接受。移除ItemCategory相关代码后可正常创建Item,但需要保留分类关联功能。
尝试过的操作
手动在GraphQL中添加输入类型定义:
input CreateItemsInput { ItemName: String! ItemDescription: String ItemCategory: Categories }
执行amplify push时更新失败,移除ItemCategory: Categories后推送成功,但无法实现关联需求。
解决方案
1. 修正GraphQL Schema,使用@connection定义关联
Amplify需要通过@connection指令明确建立模型间的关联关系,自动生成正确的输入类型和外键字段。修改后的schema如下:
type Items @model { id: ID! ItemName: String! ItemDescription: String ItemCategory: Categories @connection(fields: ["categoryId"]) categoryId: ID # 新增外键字段,用于关联Categories的id ItemEvents: [Events] ItemAttributes: [Attributes] createdAt: AWSDateTime updatedAt:AWSDateTime } type Categories @model { id: ID! CategoryName: String! CategoryItems: [Items] @connection(keyName: "byCategory", fields: ["id"]) createdAt: AWSDateTime updatedAt:AWSDateTime }
@connection(fields: ["categoryId"])指定Items通过categoryId字段关联到Categories- Categories中的
@connection(keyName: "byCategory", fields: ["id"])建立反向关联,方便通过分类查询所属Item
2. 更新前端代码,传入外键而非完整Category对象
Amplify生成的CreateItemsInput会包含categoryId字段,而非直接接受Category对象。修改前端状态和函数:
// 调整初始表单状态 const initialFormState = { ItemName: "", ItemDescription: "", categoryId: "" }; // 修改创建Item的输入处理函数 function createItemInput(e, currentCategory) { e.preventDefault(); var iname = e.target.itemName.value; // 传入分类的id到categoryId字段,而非完整对象 setFormData({ ...formData, ItemName: iname, categoryId: currentCategory.id }); createItem(); }
3. 重新生成Amplify资源
执行amplify push,Amplify会根据修正后的schema自动更新mutation、输入类型等资源,此时CreateItemsInput会包含categoryId字段,调用时即可正常关联分类。
原理说明
DynamoDB是NoSQL数据库,Amplify通过外键字段而非嵌入对象来建立模型关联。手动定义输入类型会与Amplify自动生成的内容冲突,导致push失败。使用@connection指令后,Amplify会自动处理关联逻辑,生成正确的输入和查询类型。
内容的提问来源于stack exchange,提问作者MizzyV

