This is the 23rd day of my participation in the More text Challenge. For more details, see more text Challenge

Overloading instance

If you need to synchronize your instances, you can use the Reload method. It retrieves the current data from the database and overrides the properties of the model that called the method.

Person.findOne({ where: { name: 'john' } }).then(person= > {
  person.name = 'jane'
  console.log(person.name) // 'jane'

  person.reload().then(() = > {
    console.log(person.name) // 'john'})})Copy the code

Model customization

/ / model
// Model custom methods
topic_user.ceshi = (param) = > {
    console.log('Model custom methods');
    console.log(param);
    return param;
}

/ / controller
await this.ctx.model.TopicUser.ceshi(123);
Copy the code

Scopes – Scopes (emphasis)

Scopes allow you to define commonly used queries for easy use later. Scopes can include all the same attributes as the regular finder where, include, limit, and so on.

define

Scope is defined in the model definition and can be a Finder object or a function that returns a Finder object, except for the default scope, which can only be a single object:

class Project extends Model {}
Project.init({
  / / property
}, {
  defaultScope: {
    where: {
      active: true}},scopes: {
    deleted: {
      where: {
        deleted: true}},activeUsers: {
      include: [{model: User, where: { active: true }}
      ]
    },
    random () {
      return {
        where: {
          someNumber: Math.random()
        }
      }
    },
    accessLevel (value) {
      return {
        where: {
          accessLevel: {
            [Op.gte]: value
          }
        }
      }
    }
    sequelize,
    modelName: 'project'}});Copy the code

Once the model is defined by calling addScope, you can also add scopes. This is particularly useful for scopes with include, where models in include may not be defined when other models are defined.

Always apply the default scope. This means that, with the model definition above, project.findall () will create the following query:

SELECT * FROM projects WHERE active = true
Copy the code

The default scope can be removed by calling.unscoped(),.scope(null), or by calling another scope:

Project.scope('deleted').findAll(); // Delete the default scope
SELECT * FROM projects WHERE deleted = true
Copy the code

You can also include the scope model in the scope definition. This lets you avoid repeating include,attributes, or WHERE definitions.

Use the example above and invoke the active scope in the included user model (instead of specifying the condition directly in the include object):

activeUsers: {
  include: [{model: User.scope('active')}}]Copy the code

use

By calls on the model definition, the scope to apply scope, pass one or more of the scope of the name. The scope to return to a fully functional model as an example, it has all the normal methods:. The.findall,. The update,, count, destroy and so on. You can save this model instance and use it again later:

const DeletedProjects = Project.scope('deleted');

DeletedProjects.findAll();
// After a while

// Let's look for the deleted item again!
DeletedProjects.findAll();
Copy the code

Scope applies to.find,.findAll,.count,.update,.increment, and.destroy.

Scopes can be called as functions in two ways. If the scope does not have any arguments, it can be called normally. If the scope takes arguments, an object is passed:

Project.scope('random', { method: ['accessLevel'.19]}).findAll();
SELECT * FROM projects WHERE someNumber = 42 AND accessLevel >= 19
Copy the code

merge

You can apply more than one scope at a time by passing an array of scopes to.scope or by passing scopes as continuous arguments.

// These two are equivalent
Project.scope('deleted'.'activeUsers').findAll();
Project.scope(['deleted'.'activeUsers']).findAll();
SELECT * FROM projects
INNER JOIN users ON projects.userId = users.id
WHERE projects.deleted = true
AND users.active = true
Copy the code

To apply other scopes with the defaultScope, pass the key defaultScope to.scope:

Project.scope('defaultScope'.'deleted').findAll();
SELECT * FROM projects WHERE active = true AND deleted = true
Copy the code

When more than one scope is called, the keys of the subsequent scope override the previous scope (similar to object.assign), except where and include, which are merged. Consider two scopes:

{
  scope1: {
    where: {
      firstName: 'bob'.age: {
        [Op.gt]: 20}},limit: 2
  },
  scope2: {
    where: {
      age: {
        [Op.gt]: 30}},limit: 10}}Copy the code

Calling.scope(‘scope1’, ‘scope2’) will produce the following query

WHERE firstName = 'bob' AND age > 30 LIMIT 10
Copy the code

Note scope2 will cover limit and the age, and preserved firstName. Limit, offset, the order, the paranoid, lock and raw fields are covered, and the where merged by shallow (means that the same key will be overwritten). Include The merge strategy for the

Note that the attributes key for multiple application scopes is merged in such a way that attributes. Exclude is always retained. This allows multiple scopes to be merged and never leaks sensitive fields in the final scope.

The same merge logic applies when passing find objects directly to findAll(and similar finders) on the scope model:

Project.scope('deleted').findAll({
  where: {
    firstName: 'john'
  }
})
WHERE deleted = true AND firstName = 'john'
Copy the code

The deleted scope here is merged with the Finder. If we were to pass WHERE: {firstName: ‘John ‘, deleted: false} to the Finder, the deleted scope would be overwritten.

Merge the include

Include is recursively merged based on the included model. This is a very powerful combination, added on V5, and better understood with examples.

Consider four models :Foo,Bar,Baz, and Qux, with the following associations:

class Foo extends Model {}
class Bar extends Model {}
class Baz extends Model {}
class Qux extends Model {}
Foo.init({ name: Sequelize.STRING }, { sequelize });
Bar.init({ name: Sequelize.STRING }, { sequelize });
Baz.init({ name: Sequelize.STRING }, { sequelize });
Qux.init({ name: Sequelize.STRING }, { sequelize });
Foo.hasMany(Bar, { foreignKey: 'fooId' });
Bar.hasMany(Baz, { foreignKey: 'barId' });
Baz.hasMany(Qux, { foreignKey: 'bazId' });
Copy the code

Now, consider the following four scopes defined on Foo:

{
  includeEverything: {
    include: {
      model: this.Bar,
      include: [{
        model: this.Baz,
        include: this.Qux
      }]
    }
  },
  limitedBars: {
    include: [{
      model: this.Bar,
      limit: 2}},limitedBazs: {
    include: [{
      model: this.Bar,
      include: [{
        model: this.Baz,
        limit: 2}}}]],excludeBazName: {
    include: [{
      model: this.Bar,
      include: [{
        model: this.Baz,
        attributes: {
          exclude: ['name']}}]}}Copy the code

These four scopes can easily be deeply merged, for example by calling foo.scope (‘includeEverything’, ‘limitedBars’, ‘limitedBazs’, ‘excludeBazName’).findAll(), which is exactly equivalent to calling the following:

Foo.findAll({
  include: {
    model: this.Bar,
    limit: 2.include: [{
      model: this.Baz,
      limit: 2.attributes: {
        exclude: ['name']},include: this.Qux
    }]
  }
});
Copy the code

Observe how the four scopes merge into one. Merges the include of scopes according to the included models. If one scope includes model A and the other includes model B, the result of the merges will include models A and B. On the other hand, if two scopes include the same model A, but have different parameters (such as nested include or other attributes), these are merged recursively, as shown above.

The merges described above work in exactly the same way regardless of the order in which they are applied to the scope. If an argument is set by two different scopes, only that order is different – which is not the case in the above example, because each scope does something different.

This merge policy works in exactly the same way as the arguments passed to.findAll,.findone, etc.

associated

Sequelize and correlation have two different but related scoping concepts. The differences are subtle but important:

  • Association scopeAllows you to specify default properties when getting and setting associations – useful when implementing polymorphic associations. When usingget.set.addandcreateThis scope is called only on an association between two models when the model function is associated with it
  • The scope on the association model allows you to apply the default and other scopes when you get the association, and allows you to pass the scope model when you create the association. These scopes are applicable to both regular lookups on the model and lookups by association.

For example, consider the models Post and comment.comment are associated with several other models (images, videos, etc.), and the association between Comment and other models is polymorphic, which means that in addition to the foreign key commentable_ID, comments store a Commentable column.

You can use the Association Scope to implement polymorphic associations:

this.Post.hasMany(this.Comment, {
  foreignKey: 'commentable_id'.scope: {
    commentable: 'post'}});Copy the code

When post.getComments() is called, this automatically adds WHERE Commentable = ‘post’. Similarly, when a new comment is added to a post,commentable is automatically set to ‘post’. The association scope is meant to live in the background and no programmer needs to worry – it can’t be disabled. For a more complete example of polymorphism, see Association Scope

So consider that the default scope for that Post only shows active posts :where: {active: true}. The scope exists on the associated model (Post), not on the association as the Commentable scope does. Just as the default scope is applied when post.findall () is called, it will also be applied when user.getPosts () is called – this will only return active posts for that User.

To disable the default scope, pass scope: null to the getter: user.getposts ({scope: null}). Also, if you want to apply another scope, do something like this:

User.getPosts({ scope: ['scope1'.'scope2']});
Copy the code

If you want to create a shortcut to a scope on the association model, you can pass the scope model to the association. Consider a shortcut to get all of a user’s posts that have been deleted:

class Post extends Model {}
Post.init(attributes, {
  defaultScope: {
    where: {
      active: true}},scopes: {
    deleted: {
      where: {
        deleted: true
      }
    }
  },
  sequelize,
});

User.hasMany(Post); // Regular getPosts association
User.hasMany(Post.scope('deleted'), { as: 'deletedPosts' });
User.getPosts(); // WHERE active = true
User.getDeletedPosts(); // WHERE deleted = true
Copy the code