Permalink
Fetching contributors…
Cannot retrieve contributors at this time
138 lines (86 sloc) 7.57 KB

Models

A model represents a set of structured data, called records. Models usually correspond to a table/collection in a database, attributes correspond to columns/fields, and records correspond to rows/documents.

Defining models

By convention, models are defined by creating a file in a Sails app's api/models/ folder:

// 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' },
  },
};

For a complete walkthrough of available options when setting up a model definition, see Model Settings, Attributes, and Associations.

Using models

Once a Sails app is running, its models may be accessed from within controller actions, helpers, tests, and just about anywhere else you normally write backend code. This lets your code call model methods to communicate with your database (or even with multiple databases).

There are many built-in methods available on models, the most important of which are the model methods like .find() and .create(). You can find detailed usage documentation for methods like these in Reference > Waterline (ORM) > Models.

Query methods

Every model in Sails has a set of methods exposed on it to allow you to interact with the database in a normalized fashion. This is the primary way of interacting with your app's data.

Since they usually have to send a query to the database and wait for a response, most model methods are asynchronous. That is, they don't come back with an answer right away. Like other asynchronous logic in JavaScript (setTimeout() for example), that means we need some other way of determining when they've finished executing, whether they were successful, and if not, what kind of error (or other exceptional circumstance) occurred.

In Node.js, Sails, and JavaScript in general, the recommended way to handle this is by using async/await.

For more information about working with queries, see Reference > Waterline (ORM) > Queries.

Resourceful pubsub methods

Sails also provides a few other "resourceful pubsub" (or "RPS") methods, specifically designed for performing simple realtime operations using dynamic rooms. For more information about those methods, see Reference > WebSockets > Resourceful PubSub.

Custom model methods

In addition to the built-in functionality provided by Sails, you can also define your own custom model methods. Custom model methods are most useful for extrapolating controller code that relates to a particular model; i.e. this allows you to pull code out of your controllers and into reusuable functions that can be called from anywhere (i.e. don't depend on req or res.)

This feature takes advantage of the fact that models ignore unrecognized settings, so you do need to be careful about inadvertently overriding built-in methods (don't define methods named "create", etc.)

If you're at all unsure, write a helper instead.

Custom model methods can be synchronous or asynchronous functions, but more often than not, they're asynchronous. By convention, asynchronous model methods should be async functions, which accept a dictionary of options as their argument.

For example:

// 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 });
}

Notice we didn't try/catch any of the code within that function, that's because we intend to leave that responsibility to whoever calls our function (e.g. an action).

Then you can do:

var monkeys = await Monkey.findWithSameNameAsPerson(37);

For more tips, read about the incident involving Timothy the Monkey.

What about instance methods?

As of Sails v1.0, instance methods have been removed from Sails and Waterline. While instance methods like .save() and .destroy() were sometimes convenient in app code, in Node.js at least, many users found that they led to unintended consequences and design pitfalls.

For example, consider an app that manages wedding records. It might seem like a good idea to write an instance method on the Person model to update the spouse attribute on both individuals in the database. This would allow you to write controller code like:

personA.marry(personB, function (err) {
  if (err) { return res.serverError(err); }
  return res.ok();
})

Which looks great...until it comes time to implement a slightly different action with roughly the same logic, but where the only available data is the id of "personA" (not the entire record.) In that case, you're stuck rewriting your instance method as a static method anyway!

A better strategy is to write a custom (static) model method from the get-go. This makes your function more reusable/versatile, since it will be accessible whether or not you have an actual record instance on hand. You might refactor the code from the previous example to look like:

Person.marry(personA.id, personB.id, function (err) {
  if (err) { return res.serverError(err); }
  return res.ok();
})

Case Sensitivity

Queries in Sails v1.0 are no longer forced to be case insensitive regardless of how the database processes the query. This leads to much improved query performance and better index utilization. Most databases are case sensitive by default but in the rare cases where they aren't and you would like to change that behavior you must modify the database to do so.

For example by default MySQL will use a database collation that is case insensitive which is different from sails-disk so you may experience different results from development to production. In order to fix this you can set the tables in your MySQL database to a case sensitive collation such as utf8_bin.