モデルは、レコードと呼ばれる構造化されたデータの集合を表します。モデルは通常、データベース内のテーブル/コレクションに対応し、属性は列/フィールドに対応し、レコードは行/ドキュメントに対応します。
慣例により、モデルはSailsアプリのapi/models/
フォルダにファイルを作成することで定義されます。
// api/models/Product.js
module.exports = {
attributes: {
nameOnMenu: { type: 'string', required: true },
price: { type: 'string', required: true },
percentRealMeat: { type: 'number', defaultsTo: 20, columnType: 'FLOAT' },
numCalories: { type: 'number' },
},
};
モデル定義の設定時のオプションの完全な手順については、モデル設定、属性、および関連付けを参照してください。
Sailsアプリが実行されると、そのモデルはコントローラーアクション、ヘルパー、テスト、および通常バックエンドコードを記述するほぼすべての場所からアクセスできます。これにより、コードのモデルメソッド呼び出しはデータベース(または複数のデータベース)と通信できます。
モデルには多くの組み込みメソッドがありますが、その中でも最も重要なのは、.find()や.create()などのモデルメソッドです。これらのメソッドの詳細な使用方法については、リファレンス > Waterline (ORM) > モデルを参照してください。
Sailsのすべてのモデルには、正規化された方法でデータベースとやり取りできるように公開されたメソッドのセットがあります。これは、アプリのデータとやり取りする主な方法です。
通常はデータベースにクエリを送信して応答を待つ必要があるため、ほとんどのモデルメソッドは非同期です。つまり、すぐに回答が返ってきません。JavaScriptの他の非同期ロジック(たとえば、setTimeout()
)と同様に、実行が完了したかどうか、成功したかどうか、そして成功しなかった場合はどのようなエラー(またはその他の例外的な状況)が発生したかを判断する別の方法が必要です。
Node.js、Sails、および一般的なJavaScriptでは、これを処理するための推奨方法はasync/await
を使用することです。
クエリに関する詳細については、リファレンス > Waterline (ORM) > クエリを参照してください。
Sailsは、動的ルームを使用した簡単なリアルタイム操作を実行するために特別に設計された、他のいくつかの「リソースフルなpubsub」(またはRPS)メソッドも提供します。これらのメソッドの詳細については、リファレンス > WebSockets > リソースフルなPubSubを参照してください。
Sailsによって提供される組み込み機能に加えて、独自のカスタムモデルメソッドを定義することもできます。カスタムモデルメソッドは、特定のモデルに関連するコントローラーコードを展開する場合に最も役立ちます。これにより、コントローラーからコードを取り出して、どこからでも呼び出すことができる再利用可能な関数に挿入できます(req
またはres
とは無関係)。
この機能は、モデルが認識されない設定を無視するという事実を利用しているため、意図せず組み込みメソッドを上書きすることに注意する必要があります(たとえば、「create」という名前のメソッドを定義しないでください)。
少しでも不安な場合は、代わりにヘルパーを作成してください。
カスタムモデルメソッドは、同期関数または非同期関数にすることができますが、ほとんどの場合非同期です。慣例により、非同期モデルメソッドはoptions
の辞書を引数として受け取るasync
関数である必要があります。
例:
// in api/models/Monkey.js...
// Find monkeys with the same name as the specified person
findWithSameNameAsPerson: async function (opts) {
var person = await Person.findOne({ id: opts.id });
if (!person) {
throw require('flaverr')({
message: `Cannot find monkeys with the same name as the person w/ id=${opts.id} because that person does not exist.`,
code: 'E_UNKNOWN_PERSON'
});
}
return await Monkey.find({ name: person.name });
}
この関数内のコードで
try/catch
を使用していません。これは、その責任を関数を呼び出す人に委ねようとしているためです。
次に、次のように実行できます。
var monkeys = await Monkey.findWithSameNameAsPerson({id:37});
詳細については、ティモシーザモンキーに関する事件について読んでください。
Sails v1.0以降、インスタンスメソッドはSailsとWaterlineから削除されました。.save()
や.destroy()
などのインスタンスメソッドは、アプリコードでは便利な場合もありましたが、少なくともNode.jsでは、多くのユーザーが意図しない結果と設計上の落とし穴につながることを発見しました。
たとえば、結婚式記録を管理するアプリを考えてみましょう。データベースの両方の個人のspouse
属性を更新するPersonモデルにインスタンスメソッドを作成するのは良い考えのように思えるかもしれません。これにより、次のようなコントローラーコードを書くことができます。
personA.marry(personB, function (err) {
if (err) { return res.serverError(err); }
return res.ok();
})
これは素晴らしいように見えます...「personA」のID(レコード全体ではない)だけが利用可能なデータである、ほぼ同じロジックを持つ少し異なるアクションを実装する時までです。その場合、インスタンスメソッドを静的メソッドとして書き直すしかありません!
より良い戦略は、最初からカスタム(静的)モデルメソッドを作成することです。これにより、実際のレコードインスタンスがあるかどうかに関係なくアクセスできるため、関数がより再利用可能/多用途になります。前の例のコードを次のようにリファクタリングできます。
Person.marry(personA.id, personB.id, function (err) {
if (err) { return res.serverError(err); }
return res.ok();
})
Sails v1.0のクエリは、データベースがクエリを処理する方法に関係なく、大文字と小文字を区別しないように強制されなくなりました。これにより、クエリの性能が大幅に向上し、インデックスの利用率が向上します。ほとんどのデータベースはデフォルトで大文字と小文字を区別しますが、まれに大文字と小文字を区別しない場合があり、その動作を変更したい場合は、データベースを修正する必要があります。
たとえば、MySQLはデフォルトで大文字と小文字を区別しないデータベース照合を使用します。これはsails-diskとは異なるため、開発から本番環境まで異なる結果が発生する可能性があります。これを修正するには、MySQLデータベースのテーブルをutf8_bin
などの大文字と小文字を区別する照合に設定できます。