Sailsでは、モデル定義のトップレベルのプロパティをモデル設定と呼びます。これには、属性定義から、モデルが使用するデータベース設定、そしてその他いくつかのオプションが含まれます。
このページの大部分は、Sailsでサポートされているモデル設定の完全な解説に当てられています。しかし、始める前に、Sailsアプリでこれらの設定を実際に適用する方法を見てみましょう。
モデル設定を使用すると、Sailsアプリのモデルの動作をカスタマイズできます。モデル定義のトップレベルのプロパティを設定することで、モデルごとに指定することも、sails.config.models
でアプリ全体のデフォルトとして指定することもできます。
アプリ内のすべてのモデルで共有されるデフォルトのモデル設定を変更するには、config/models.js
を編集します。
たとえば、新しいアプリを生成すると、Sailsはconfig/models.js
ファイルに3つの異なるデフォルト属性(id
、createdAt
、updatedAt
)を自動的に含めます。すべてのモデルで、少し異なるカスタマイズされたid
属性を使用したいとしましょう。そのためには、config/models.js
定義でattributes: { id: {...} }
をオーバーライドするだけです。
特定のモデルの設定をさらにカスタマイズするには、そのモデルの定義ファイル(例:api/models/User.js
)でトップレベルのプロパティとして指定します。これにより、同じ名前のデフォルトのモデル設定がオーバーライドされます。
たとえば、モデル定義の1つ(api/models/UploadedFile.js
)にfetchRecordsOnUpdate: true
を追加すると、そのモデルは更新されたレコードを返すようになります。しかし、他のモデルには影響しません。それらは依然としてデフォルト設定(変更しない限りfetchRecordsOnUpdate: false
)を使用します。
日々の開発では、最も頻繁に操作するモデル設定はattributes
です。属性はほぼすべてのモデル定義で使用され、いくつかのデフォルト属性はconfig/models.js
に含まれています。今後の参考のために、さらにいくつかのヒントを示します。
tableName
を指定する場合は、常にモデルごとに指定する必要があります。(アプリ全体のテーブル名は意味がありません!)datastore
をオーバーライドしたい場合があります。たとえば、デフォルトのデータストアがPostgreSQLだが、Redisに配置したいCachedBloodworkReport
モデルがある場合などです。migrate
とschema
の設定はアプリ全体のデフォルトとしてのみ指定し、モデルごとには決して指定しない方が最善です。モデル設定とは何か、そしてどのように設定するかについてのアイデアができたので、それぞれを見ていきましょう。
モデルの属性定義のセット。
attributes: { /* ... */ }
型 | 例 | デフォルト |
---|---|---|
下記参照。 | {} |
ほとんどの場合、個々のモデル定義(api/models/
内)で属性を定義しますが、config/models.js
でデフォルト属性を指定することもできます。これにより、グローバル属性のセットを1箇所で定義し、Sailsがそれらをすべてのモデルに暗黙的に、繰り返しなく利用できるようにすることができます。デフォルト属性は、関連するモデル定義で同じ名前の置換属性を定義することで、モデルごとにオーバーライドすることもできます。
attributes: {
id: { type: 'number', autoIncrement: true },
createdAt: { type: 'number', autoCreatedAt: true },
updatedAt: { type: 'number', autoUpdatedAt: true },
}
Sailsアプリでのモデル属性の定義と使用方法を含む、モデル属性の完全な紹介については、概念 > ORM > 属性を参照してください。
モデルのレコードをJSONにシリアル化する方法をカスタマイズできる関数。
customToJSON: function() { /*...*/ }
型 | 例 | デフォルト |
---|---|---|
下記参照。 | n/a |
モデルにcustomToJSON
設定を追加すると、モデルのレコードの文字列化方法が変わります。つまり、これらのレコードのいずれかがJSON.stringify()
に渡されるたびに実行されるカスタムロジックを挿入できます。これは、ユーザーパスワードなどの機密データが応答に誤って含まれないようにするためのフェールセーフを実装するために最も一般的に使用されます(res.send()
とactions2は送信前にデータを文字列化する場合があります)。
customToJSON
関数は引数をとりませんが、レコードにthis
変数としてアクセスできます。これにより、機密データを省略し、サニタイズされた結果を返すことができます。これが、JSON文字列を生成する際にJSON.stringify()
が実際に使用するものです。たとえば
customToJSON: function() {
// Return a shallow copy of this record with the password and ssn removed.
return _.omit(this, ['password', 'ssn'])
}
customToJSON
関数は、非同期機能をサポートするように設計されていません。これにより、コア内の同期ビットは同期状態を維持し、システム全体の安定性を向上させることができます。
customToJSON
で使用できるthis
変数は、実際のレコードオブジェクトへの直接参照であることに注意してください。そのため、変更しないように注意してください。つまり、delete this.password
のようなコードを記述しないでください。代わりに、_.omit()
や_.pick()
などのメソッドを使用して、レコードのコピーを取得します。または、新しい辞書を作成して返すだけです(例:return { foo: this.foo }
)。
モデルがレコードを行(/MongoDBドキュメント)として保存および取得するSQLテーブル(/MongoDBコレクション)の名前。
tableName: 'some_preexisting_table'
型 | 例 | デフォルト |
---|---|---|
'some_preexisting_table' |
モデルのIDと同じ。 |
tableName設定を使用すると、特定のモデルが使用する基盤となる物理モデルの名前をカスタマイズできます。つまり、コントローラーアクション/ヘルパーのコードに影響を与えることなく、モデルがデータベース内でレコードを保存および取得する場所を制御できます。
デフォルトでは、Sailsはモデルのidentityを使用してテーブル名を決定します。
await User.find();
// => SELECT * FROM user;
これは推奨される規則であり、ほとんどの場合変更する必要はありません。しかし、PythonやC#などの異なるプラットフォーム用に記述された既存のアプリケーションとレガシーデータベースを共有している場合、またはチームがデータベーステーブルに異なる命名規則を好む場合、このマッピングをカスタマイズすることが役立つ場合があります。
上記の例に戻ると、api/models/User.js
でモデル定義を変更し、tableName: 'foo_bar'
を設定した場合、少し異なる結果が見られます。
await User.find();
// => SELECT * FROM foo_bar;
tableName
とは何か?MySQLやPostgreSQLのようなデータベースでは、設定は文字通りの「テーブル」を指します。MongoDBでは、「コレクション」を指します。これは単なる慣れの問題です。「テーブル」と呼ぶものを、別の言葉で呼んでも、同様にクエリできます。
アプリのロード時にSailsが実行する自動マイグレーション戦略。
migrate: 'alter'
型 | 例 | デフォルト |
---|---|---|
'alter' |
プロンプトが表示されます。 注記:本番環境では、これは常に 'safe' です。 |
migrate
設定は、アプリの自動マイグレーション戦略を制御します。簡単に言うと、これはSailsに、データベース内のテーブル/コレクション/セットなどを自動的に再構築しようとするかどうかを指示します。
アプリの開発過程では、ほとんどの場合、データベースの構造に少なくとも1つか2つの破壊的変更を加える必要があります。「破壊的変更」とは何かは、使用しているデータベースによって異なります。たとえば、モデル定義に新しい属性を追加したとします。そのモデルがMongoDBを使用するように構成されている場合、これは大した問題ではありません。何も起こらなかったかのように開発を続けることができます。しかし、そのモデルがMySQLを使用するように構成されている場合、追加の手順があります。対応するテーブルに列を追加する必要があります(そうでないと、.create()
などのモデルメソッドが機能しなくなります)。そのため、MySQLを使用するモデルの場合、属性を追加することはデータベーススキーマへの破壊的変更です。
すべてのモデルがMongoDBを使用している場合でも、注意すべき破壊的なスキーマ変更がいくつかあります。たとえば、属性に
unique: true
を追加すると、MongoDBに一意のインデックスを作成する必要があります。
Sailsでは、データベースマイグレーションに関して、2つの異なる動作モードがあります。
sails run
を使用して、すべてのデータベースマイグレーションを手動で行うことをお勧めします。本番データベースに破壊的変更を適用する必要がある場合は、手動のデータベースマイグレーションを使用する必要があります。それ以外の場合は、ラップトップで開発したり、自動テストを実行したりする場合、自動マイグレーションにより多くの時間を節約できます。
開発環境でSailsアプリを起動すると(新しいSailsアプリでsails lift
を実行する場合など)、構成された自動マイグレーション戦略が実行されます。migrate: 'safe'
を使用している場合は、追加の処理は行われませんが、drop
またはalter
を使用している場合は、Sailsは開発データベース内のすべてのレコードをメモリにロードし、データの物理レイヤー表現(つまり、テーブル/コレクション/セットなど)を削除して再作成します。これにより、一意性制約の削除など、モデル定義に加えた破壊的変更を、開発データベースに自動的に適用できます。最後に、alter
を使用している場合、Sailsは、以前に保存したレコードを使用して、新しく生成されたテーブル/コレクション/セットにシードしようとします。
自動マイグレーション戦略 | 説明 |
---|---|
safe |
データベースを自動マイグレーションしません。手動で行います。 |
alter |
列/フィールドを自動マイグレーションしますが、既存のデータは保持しようとします(実験的) |
drop |
すべてのデータを消去/削除し、Sailsを起動するたびにモデルを再構築します。 |
alter
またはdrop
戦略を使用する場合は、アプリを最後に起動してからデータベースに加えた手動による変更が失われる可能性があることに注意してください。これには、カスタムインデックス、外部キー制約、列の順序とコメントなどが含まれます。一般的に、自動マイグレーションによって作成されたテーブルは、列名、型(指定されている場合の文字セット/エンコーディングを含む)、一意性以外の物理データベース列の詳細については、一貫性が保証されていません。
Sailsのdrop
およびalter
自動マイグレーション戦略は、開発中や自動テスト実行時の利便性のために存在する機能です。**これらは、重要なデータで使用することを意図したものではありません。**drop
またはalter
を本番データセットで使用しないよう十分注意してください。実際、不注意によるこのような操作を防ぐための安全策として、アプリケーションを本番環境で起動する際は、設定にかかわらず、Sailsは常にmigrate: 'safe'
を使用します。
多くの場合、ホスティングプロバイダーは、Node.jsアプリケーションを検出すると、NODE_ENV
環境変数を自動的に"production"に設定します。それでも、この安全策だけに頼らず、ユーザーのデータを守るための通常の予防措置を講じてください。Sails(またはその他のツールやフレームワーク)を既存の本番データを持つデータベースに接続する際は、特に初めて接続する際には、必ずドライランを実施してください。本番データは機密性が高く、価値があり、多くの場合、代替不可能です。顧客、ユーザー、そして彼らの弁護士は、データが消去されることを良しとしません。
ベストプラクティスとして、本番環境で実行されていることを100%確信していない限り、本番データベースの認証情報を使用してアプリケーションを起動またはデプロイしないようにしてください。組織全体でこれを解決するための一般的なアプローチとして、そもそも本番データベースの認証情報をソースコードリポジトリにプッシュしないことです。代わりに、すべての機密認証情報に環境変数を使用します。(これは、アプリケーションが規制要件の対象となっている場合、または多数の人がコードベースにアクセスできる場合に特に有効です。)
比較的多くの開発/テストデータを使用している場合、alter
自動マイグレーション戦略は、起動時に完了するまでに時間がかかる可能性があります。npm test
、sails console
、またはsails lift
などのコマンドがハングしているように見える場合は、開発データセットのサイズを小さくすることを検討してください。(覚えておいてください:Sailsの自動マイグレーションは、ローカルのラップトップ/デスクトップコンピューターでのみ、そして小規模な開発データセットでのみ使用する必要があります。)
モデルが特定の属性セットに準拠したレコードを期待するかどうか。
schema: true
型 | 例 | デフォルト |
---|---|---|
true |
アダプターによって異なります。 |
schema
設定を使用すると、モデルを「スキーマレス」モードまたは「スキーマフル」モードのどちらかに切り替えることができます。より具体的には、.create()
や.update()
などのメソッドの動作を制御します。通常、使用しているアダプターがサポートしていれば、任意のデータをレコードに保存できます。しかし、schema:true
を有効にすると、モデルのattributes
に対応するプロパティのみが実際に保存されます。
この設定は、MongoDBのようなスキーマレスデータベースを使用するモデルにのみ関連します。MySQLやPostgreSQLのようなリレーショナルデータベースに接続されている場合、モデルは常に事実上
schema:true
になります。これは、基盤となるデータベースでは、事前に設定されたテーブルと列にのみデータを保存できるためです。
レコードの検索、レコードの作成などを行うために、モデルが使用するデータストアの設定の名前。
datastore: 'legacyECommerceDb'
型 | 例 | デフォルト |
---|---|---|
'legacyECommerceDb' |
'default' |
これにより、このモデルがデータの取得と保存を行うデータベースを指定できます。特に指定がない限り、アプリケーション内のすべてのモデルは、「default」という名前の組み込みデータストアを使用します。これは、アプリケーションのプライマリデータベースを簡単に設定しながら、特定のモデルのdatastore
設定を上書きできるようにします。
アプリケーションのデータストアの設定の詳細については、リファレンス > 設定 > データストアを参照してください。
データの復号化に使用されるキーのセット。default
データ暗号化キー(または「DEK」)は、特に設定されていない限り、常に暗号化に使用されます。
dataEncryptionKeys: {
default: 'tVdQbq2JptoPp4oXGT94kKqF72iV0VKY/cnp7SjL7Ik='
}
ユースケースでキーローテーションが必要でない限り、
default
キーだけで十分です。default
以外のデータ暗号化キーは、それらで暗号化された古いデータを復号化するためにのみ存在します。
データ暗号化キーを廃止するには、新しいキーID(2028
など)を割り当て、新しいdefault
キーを作成して新しい暗号化で使用します。たとえば、2028年にSailsアプリをリリースし、キーが毎年ローテーションされる場合、翌年のdataEncryptionKeys
は次のようになります。
dataEncryptionKeys: {
default: 'DZ7MslaooGub3pS/0O734yeyPTAeZtd0Lrgeswwlt0s=',
'2028': 'C5QAkA46HD9pK0m7293V2CzEVlJeSUXgwmxBAQVj+xU='
}
その翌年(2030年1月)にデフォルトキーを交換した後、次のように表示される可能性があります。
dataEncryptionKeys: {
default: 'tVdQbq2JptoPp4oXGT94kKqF72iV0VKY/cnp7SjL7Ik=',
'2029': 'DZ7MslaooGub3pS/0O734yeyPTAeZtd0Lrgeswwlt0s=',
'2028': 'C5QAkA46HD9pK0m7293V2CzEVlJeSUXgwmxBAQVj+xU='
}
このモデルを使用して.destroy()
を呼び出すたびに、常にcascade: true
を設定した場合と同様に動作するかどうか。
cascadeOnDestroy: true
型 | 例 | デフォルト |
---|---|---|
true |
false |
パフォーマンス上の理由から、デフォルトでは無効になっています。このモデル設定で有効にしたり、.meta({cascade: true})
を使用してクエリごとに有効にしたりできます。
この機能は、
sails-mongo
アダプターでのみ使用できます。
true
に設定すると、モデルは自動生成されたMongoDB ObjectIDオブジェクトをプライマリキーとして使用しません。これにより、sails-mongo
アダプターを使用して、長いUUIDのようなものだけでなく、任意の文字列または数値をプライマリキーとするモデルを作成できます。これをtrue
に設定すると、.create()
または.createEach()
を呼び出すたびにid
の値を指定する必要があることに注意してください。
型 | 例 | デフォルト |
---|---|---|
true |
false |
パフォーマンス上の理由から、デフォルトでは無効になっています。このモデル設定で有効にしたり、.meta({dontUseObjectIds: true})
を使用してクエリごとに有効にしたりできます。
以下の低レベルの設定は、完全性を期して含まれていますが、実際には、変更する必要はほとんど(またはまったくない)はずです。
モデルのプライマリキー属性の名前。
この設定を変更する必要はありません。代わりに、カスタムプライマリキーを使用する必要がある場合は、「id」属性にカスタム
columnName
を設定します。
primaryKey: 'id'
型 | 例 | デフォルト |
---|---|---|
'id' |
'id' |
慣例的にこれは"id"であり、Sails v1.0以降に生成された新しいアプリケーションのconfig/models.js
ファイルに自動的に含まれるデフォルトの属性です。モデルのプライマリキーを変更する最良の方法は、そのデフォルト属性のcolumnName
をカスタマイズすることです。
たとえば、既存のMySQLデータベースのテーブルと統合する必要があるUserモデルがあるとします。そのテーブルは、プライマリキーとして"id"以外の名前("email_address"など)の列を持つ可能性があります。モデルにそのプライマリキーを尊重させるには、モデル定義でid
属性のオーバーライドを指定します。次のようになります。
id: {
type: 'string',
columnName: 'email_address',
required: true
}
その後、アプリケーションのコードでプライマリキーを使用してユーザーを検索できるようになり、生成されたすべてのSQLクエリでのemail_address
へのマッピングは自動的に処理されます。
await User.find({ id: req.param('emailAddress' });
すべての注意事項を脇に置いて、MongoDBの熱心なユーザーだとしましょう。新しいSailsアプリでは、
config/models.js
のデフォルトの"id"属性にcolumnName: '_id'
を設定することから始めます。その後、通常どおりSailsとWaterlineを使用でき、すべて正常に動作します。しかし、親しみやすさのために「id」属性自体の名前を変更したいと思うようになったとしたらどうでしょうか?そうすれば、コードで組み込みのモデルメソッドを呼び出すときに、通常の"id"の代わりに、
.destroy({ _id: 'ba8319abd-13810-ab31815' })
のような構文を使用できます。これが、このモデル設定が役立つ可能性のあるところです。
config/models.js
を編集してprimaryKey: '_id'
を含め、デフォルトの"id"属性を"_id"に名前変更するだけです。しかし、この方法を再考する十分な理由があります。
モデルの一意の小文字識別子。
モデルの
identity
は読み取り専用です。自動的に導出され、手動で設定することはできません。
Something.identity;
型 | 例 |
---|---|
'purchase' |
Sailsでは、モデルのidentity
は、ファイル名を小文字にしてファイル拡張子を削除することで自動的に推論されます。たとえば、api/models/Purchase.js
のidentityはpurchase
になります。これはsails.models.purchase
としてアクセスでき、ブループリントルートが有効になっている場合は、GET /purchase
やPATCH /purchase/1
のようなリクエストでアクセスできます。
assert(Purchase.identity === 'purchase');
assert(sails.models.purchase.identity === 'purchase');
assert(Purchase === sails.models.purchase);
モデルの一意のグローバル識別子。これは、対応するグローバル変数の名前も決定します(該当する場合)。
モデルの
globalId
は読み取り専用です。自動的に導出され、手動で設定することはできません。
Something.globalId;
型 | 例 |
---|---|
'Purchase' |
モデルのglobalIdの主な目的は、Sailsがその代わりに自動的に公開するグローバル変数の名前を決定することです。ただし、モデルのグローバル化が無効にされている場合を除きます。Sailsでは、モデルのglobalId
は、ファイル名から自動的に推論されます。たとえば、api/models/Purchase.js
のglobalIdはPurchase
になります。
assert(Purchase.globalId === 'Purchase');
assert(sails.models.purchase.globalId === 'Purchase');
if (sails.config.globals.models) {
assert(sails.models.purchase === Purchase);
}
else {
assert(typeof Purchase === 'undefined');
}