Name:
interface
Value:
Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.
Gen1 DocsLegacy

Page updated Jan 10, 2025

モデル関係の構築

アプリケーションデータをモデル化する際、異なるデータモデル間の関係を確立する必要があることがよくあります。Amplify Data では、データスキーマに 1 対多、1 対 1、および多対多の関係を作成できます。クライアント側では、Amplify Data により関連データの遅延読み込みまたは事前読み込みが可能です。

With Amplify Data Construct @aws-amplify/data-construct@1.8.4, an improvement was made to how relational field data is handled in subscriptions when different authorization rules apply to related models in a schema. The improvement redacts the values for the relational fields, displaying them as null or empty, to prevent unauthorized access to relational data.

This redaction occurs whenever it cannot be determined that the child model will be protected by the same permissions as the parent model.

Because subscriptions are tied to mutations and the selection set provided in the result of a mutation is then passed through to the subscription, relational fields in the result of mutations must be redacted.

If an authorized end-user needs access to the redacted relational fields, they should perform a query to read the relational data.

Additionally, subscriptions will inherit related authorization when relational fields are set as required. To better protect relational data, consider modifying the schema to use optional relational fields.

関係のタイプ

関係コード説明
1 対多a.hasMany(...) & a.belongsTo(...)2 つのモデル間に 1 対多の関係を作成します。Team は複数の Members を持ちます。MemberTeam に属しています。
1 対 1a.hasOne(...) & a.belongsTo(...)2 つのモデル間に 1 対 1 の関係を作成します。Customer は 1 つの Cart を持ちます。Cart は 1 つの Customer に属しています。
多対多結合テーブル上の 2 つの a.hasMany(...) & a.belongsTo(...)結合テーブルで関連モデル間に 2 つの 1 対多の関係を作成します。Post は複数の Tags を持ちます。Tag は複数の Posts を持ちます。

1 対多のモデル関係を構築する

hasMany() メソッドと belongsTo() メソッドを使用して、2 つのモデル間に 1 対多の関係を作成します。以下の例では、Team は複数の Members を持ち、Member はちょうど 1 つの Team に属しています。

  1. Member モデルで teamId という参照フィールドを作成します。この参照フィールドの型は Team の識別子の型と一致する必要があります。この場合、自動生成される id: a.id().required() フィールドです。
  2. teamId フィールドを参照する team という関係フィールドを追加します。これにより、Member モデルからチーム情報を照会できます。
  3. Member モデルの teamId フィールドを参照する members という関係フィールドを追加します。
const schema = a.schema({
Member: a.model({
name: a.string().required(),
// 1. Create a reference field
teamId: a.id(),
// 2. Create a belongsTo relationship with the reference field
team: a.belongsTo('Team', 'teamId'),
}),
Team: a.model({
mantra: a.string().required(),
// 3. Create a hasMany relationship with the reference field
// from the `Member`s model.
members: a.hasMany('Member', 'teamId'),
}),
}).authorization((allow) => allow.publicApiKey());

「Has Many」関係をレコード間で作成する

final team = Team(mantra: "Go Frontend!");
final teamRequest = ModelMutations.create(team);
final teamResponse = await Amplify.API.mutate(request: teamRequest).response;
final member = Member(name: "Tim", team: teamResponse.data);
final memberRequest = ModelMutations.create(member);
final memberResponse = await Amplify.API.mutate(request: memberRequest).response;

「Has Many」関係をレコード間で更新する

final newTeam = Team(mantra: "Go Fullstack!");
final newTeamRequest = ModelMutations.create(team);
final newTeamResponse = await Amplify.API.mutate(request: teamRequest).response;
final memberWithUpdatedTeam = existingMember.copyWith(team: newTeamResponse.data);
final memberUpdateRequest = ModelMutations.update(memberWithUpdatedTeam);
final memberUpdateResponse = await Amplify.API.mutate(request: memberUpdateRequest).response;

「Has Many」関係をレコード間で削除する

参照フィールドが必須でない場合、関係値を null に設定することで、1 対多の関係を「削除」できます。

final memberWithRemovedTeam = existingMember.copyWith(team: null);
final memberRemoveRequest = ModelMutations.update(memberWithRemovedTeam);
final memberRemoveResponse = await Amplify.API.mutate(request: memberRemoveRequest).response;

「Has Many」関係で関連データを読み込む

// Fetch the team with the team id.
final teamRequest = ModelQueries.get<Team>(
Team.classType, TeamModelIdentifier(id: "YOUR_TEAM_ID"));
final teamResult = await Amplify.API.query(request: teamRequest).response;
final team = teamResult.data!;
// Define a limit for your pagination
const limit = 100;
// Do the initial call to get the initial items
final firstRequest = ModelQueries.list<Member>(Member.classType,
limit: limit, where: Member.TEAMID.eq(team.id));
final firstResult = await Amplify.API.query(request: firstRequest).response;
final firstPageData = firstResult.data;
// If there are more than 100 items you can reiterate the following code to get next pages.
if (firstPageData?.hasNextResult ?? false) {
final secondRequest = firstPageData!.requestForNextResult;
final secondResult =
await Amplify.API.query(request: secondRequest!).response;
return secondResult.data?.items ?? <Member?>[];
} else {
// You can return the page data by calling items property.
return firstPageData?.items ?? <Member?>[];
}

「1 対 1」関係をモデル化する

hasOne() メソッドと belongsTo() メソッドを使用して、2 つのモデル間に 1 対 1 の関係を作成します。以下の例では、CustomerCart を持ち、CartCustomer に属しています。

  1. Cart モデルで customerId という参照フィールドを作成します。この参照フィールドの型は Customer の識別子の型と一致する必要があります。この場合、自動生成される id: a.id().required() フィールドです。
  2. customerId フィールドを参照する customer という関係フィールドを追加します。これにより、Cart モデルから顧客情報を照会できます。
  3. Cart モデルの customerId フィールドを参照する activeCart という関係フィールドを追加します。
const schema = a.schema({
Cart: a.model({
items: a.string().required().array(),
// 1. Create reference field
customerId: a.id(),
// 2. Create relationship field with the reference field
customer: a.belongsTo('Customer', 'customerId'),
}),
Customer: a.model({
name: a.string(),
// 3. Create relationship field with the reference field
// from the Cart model
activeCart: a.hasOne('Cart', 'customerId')
}),
}).authorization((allow) => allow.publicApiKey());

「Has One」関係をレコード間で作成する

「has one」関係をレコード間で作成するには、最初に親アイテムを作成し、次に子アイテムを作成して親を割り当てます。

final customer = Customer(name: "Rene");
final customerRequest = ModelMutations.create(customer);
final customerResponse = await Amplify.API.mutate(request: customerRequest).response;
final cart = Cart(items: ["Tomato", "Ice", "Mint"], customer: teamResponse.customer);
final cartRequest = ModelMutations.create(cart);
final cartResponse = await Amplify.API.mutate(request: cartRequest).response;

「Has One」関係をレコード間で更新する

「Has One」関係をレコード間で更新するには、最初に子アイテムを取得し、次に親への参照を別の親に更新します。たとえば、Cart を別の Customer に再割り当てするには:

final newCustomer = Customer(name: "Ian");
final newCustomerRequest = ModelMutations.create(newCustomer);
final newCustomerResponse = await Amplify.API.mutate(request: newCustomerRequest).response;
final cartWithUpdatedCustomer = existingCart.copyWith(customer: newCustomerResponse.data);
final cartUpdateRequest = ModelMutations.update(cartWithUpdatedCustomer);
final cartUpdateResponse = await Amplify.API.mutate(request: cartUpdateRequest).response;

「Has One」関係をレコード間で削除する

final cartWithRemovedCustomer = existingCart.copyWith(customer: null);
final cartRemoveRequest = ModelMutations.update(cartWithRemovedCustomer);
final cartRemoveResponse = await Amplify.API.mutate(request: cartRemoveRequest).response;

「Has One」関係で関連データを読み込む

// Fetch the cart with the cart id.
final cartRequest = ModelQueries.get<Cart>(
Cart.classType, CartModelIdentifier(id: "MY_CART_ID"));
final cartResult = await Amplify.API.query(request: cartRequest).response;
final cart = cartResult.data!;
// Do the customer call to with the id from cart
if (cart.customerId != null) {
final customerRequest = ModelQueries.get<Customer>(
Customer.classType, CustomerModelIdentifier(id: cart.customerId!));
final customerResult =
await Amplify.API.query(request: customerRequest).response;
final customer = customerResult.data!;
}

「多対多」関係をモデル化する

2 つのモデル間に多対多の関係を作成するには、「結合テーブル」として機能するモデルを作成する必要があります。この「結合テーブル」は、2 つの関連エンティティ間に 2 つの 1 対多の関係を含む必要があります。たとえば、Post が多くの Tags を持ち、Tag が多くの Posts を持つという関係をモデル化するには、これら 2 つのエンティティ間の関係を表す新しい PostTag モデルを作成する必要があります。

const schema = a.schema({
PostTag: a.model({
// 1. Create reference fields to both ends of
// the many-to-many relationship
postId: a.id().required(),
tagId: a.id().required(),
// 2. Create relationship fields to both ends of
// the many-to-many relationship using their
// respective reference fields
post: a.belongsTo('Post', 'postId'),
tag: a.belongsTo('Tag', 'tagId'),
}),
Post: a.model({
title: a.string(),
content: a.string(),
// 3. Add relationship field to the join model
// with the reference of `postId`
tags: a.hasMany('PostTag', 'postId'),
}),
Tag: a.model({
name: a.string(),
// 4. Add relationship field to the join model
// with the reference of `tagId`
posts: a.hasMany('PostTag', 'tagId'),
}),
}).authorization((allow) => allow.publicApiKey());

2 つのモデル間の複数の関係をモデル化する

関係は参照フィールドによって一意に定義されます。たとえば、Post は Person モデルとの authoreditor の個別の関係を持つことができます。

const schema = a.schema({
Post: a.model({
title: a.string().required(),
content: a.string().required(),
authorId: a.id(),
author: a.belongsTo('Person', 'authorId'),
editorId: a.id(),
editor: a.belongsTo('Person', 'editorId'),
}),
Person: a.model({
name: a.string(),
editedPosts: a.hasMany('Post', 'editorId'),
authoredPosts: a.hasMany('Post', 'authorId'),
}),
}).authorization((allow) => allow.publicApiKey());

クライアント側では、次のコードで関連データを取得できます:

const client = generateClient<Schema>();
const { data: post } = await client.models.Post.get({ id: "SOME_POST_ID" });
const { data: author } = await post?.author();
const { data: editor } = await post?.editor();

識別子でソートキーを持つモデルのモデル関係

データモデルが識別子でソートキーを使用する場合、関連データモデルにも参照フィールドを追加し、ソートキーフィールドを保存する必要があります:

const schema = a.schema({
Post: a.model({
title: a.string().required(),
content: a.string().required(),
// Reference fields must correspond to identifier fields.
authorName: a.string(),
authorDoB: a.date(),
// Must pass references in the same order as identifiers.
author: a.belongsTo('Person', ['authorName', 'authorDoB']),
}),
Person: a.model({
name: a.string().required(),
dateOfBirth: a.date().required(),
// Must reference all reference fields corresponding to the
// identifier of this model.
authoredPosts: a.hasMany('Post', ['authorName', 'authorDoB']),
}).identifier(['name', 'dateOfBirth']),
}).authorization((allow) => allow.publicApiKey());

関係を必須または任意にする

Amplify Data の関係は参照フィールドを使用して、関係が必須か任意かを判断します。参照フィールドを必須としてマークした場合、2 つのモデル間の関係を「削除」することはできません。代わりに、関連レコード全体を削除する必要があります。

const schema = a.schema({
Post: a.model({
title: a.string().required(),
content: a.string().required(),
// You must supply an author when creating the post
// Author can't be set to `null`.
authorId: a.id().required(),
author: a.belongsTo('Person', 'authorId'),
// You can optionally supply an editor when creating the post.
// Editor can also be set to `null`.
editorId: a.id(),
editor: a.belongsTo('Person', 'editorId'),
}),
Person: a.model({
name: a.string(),
editedPosts: a.hasMany('Post', 'editorId'),
authoredPosts: a.hasMany('Post', 'authorId'),
}),
}).authorization((allow) => allow.publicApiKey());