I have two models Role and Permission. Upon creating Role record I need to create Permission record as well. My object structure is as follows
const rolesWithPermissions = {
name: 'role_name',
description: 'role_description',
permissions: [{
name: 'permission_name'
}]
}|
id in roles will be created by the database while inserting the record since its auto_increment.
role_id is the foreignKey in Permission model for Role'id column.
I have created Sequelize models for both Role and Permission and have setup the associations as well.
Now the problem is while creating roles I will be passing the permissions data as well.
It is successful but it is not populating the role_id in the Permission model.
Now how do to make this creation successful, I will not be able to pass in the role_id while creating the permissions object since I want to create both like the below.
Role.bulkCreate(rolesWithPermissions, {
include: {
model: Permission,
as: 'permissions',
},
});
This is creating Permission record but role_id in Permission record is null always.
Related
i am working on MERN STACK app..
i want to create 3 users schema i,e Admin employee and manager pease help me out how create that 3 users schema
Actually you can do this with only 1 schema. You can define field named "role" in your user schema and everytime user login you can validate that role and check if the role is "admin" ,"employee" or "manager".
You can use this schema on your auth schema.
role: {
type: String,
default: "user",
enum: [
"admin",
"employee",
"manager"
]
}
So I have user model and admin model and they're associated as user n:1 admin. The code defining the user model as follows:
// users.model.ts
const users = sequelize.define('users', {
...
adminId: {
field: 'admin_id',
type: DataTypes.BIGINT,
allowNull: true
},
...
});
(users as any).associate = function associate(models: any) {
models.users.belongsTo(models.admins);
};
return users;
and the admin model:
// admins.model.ts
const admins = sequelizeClient.define('admins', {
...
});
(admins as any).associate = function associate(models: any) {
models.admins.hasOne(models.users);
};
return admins;
Is it possible to implement some rule in the association, or some Sequelize hook f.e. afterGet that will automatically fetch the referenced record?
I would like to get the admin object as a property of the user object when I query just the User model, f.e. when I call User.findOne(123) it will have the object of the referenced admin record included. Basically telling Sequelize to always do the JOIN when getting the user record. Is that possible in Sequelize or I'll have to write logic separately?
Eventually I figured out that this is done through scopes (docs here and here).
I added this to the users.model.ts:
/*
* When called as `model.scope('<scope_name>').<method>` it will override the default behaviour of Sequelize and
* will add to the query whatever is requested in the scope definition.
*/
users.addScope('includeAdmin', {
include: [{
attributes: ['id', 'name'],
model: sequelize.models.admins,
as: 'admin'
}]
});
Eventually, I will make the following call: User.scope('includeAdmin').findOne(123), at which point Sequelize will automatically JOIN the admins model.
By default the admin entity's properties will be returned as such in the user object:
{
"admin.id": ...,
"admin.name": ...
}
So, if you want to have them as a nested admin object, then you must add nest: true property in the call, as follows: User.scope('includeAdmin').findOne(123, {nest: true})
If I want to make this behaviour default and not call .scope('...'), then when you declare the scope in the .addScope() function, call it 'defaultScope'.
That's why associations are used, to get things. To get the relation as attribute you can use eager loading:
const awesomeCaptain = await Captain.findOne({
where: {
name: "Jack Sparrow"
},
include: Ship
});
// Now the ship comes with it
console.log('Name:', awesomeCaptain.name);
console.log('Skill Level:', awesomeCaptain.skillLevel);
console.log('Ship Name:', awesomeCaptain.ship.name);
console.log('Amount of Sails:', awesomeCaptain.ship.amountOfSails);
I am configuring Mongoose to work on an existing MongoDB, that has these two collections:
Users - with fields:
_id: ObjectId
name: String
org_id: ObjectId
Organizations - with fields:
_id: ObjectId
name: String
I want to be able to populate a User document by Organization data.
So I've created these two Models:
const userSchema = new Schema({
name: String,
org_id: {
type: Schema.Types.ObjectId,
ref: 'Organization',
},
});
const User = mongoose.model('User', userSchema);
const organizationSchema = new Schema({
name: String,
code: String,
});
const Organization = mongoose.model('Organization', organizationSchema);
Since historically the ref field from User to Organization is called org_id (instead of just organization) the population of a user by the organization code is:
const user = await User.findById('5b213a69acef4ac0f886cdbc')
.populate('org_id')
.exec();
where user.org_id will be populated by Organization data. Of course I would be happier to have organization instead of org_id in both - populate method and the path (i.e. user.organizationd).
What is the proper way to achieve it without changing the existing documents?
I could create my Schema methods (instead of populate) and aliases, but I am looking for a more generic and elegant solution.
I understood that you don't want to change the existent documents, but for me, if this name of field doesn't make more sense you need to refactor.
Change the name of the field, organization instead of org_id.
For this you can use the $rename command: MongoDB $rename
db.getCollection('users').updateMany({},{$rename: { "org_id": "organization" }});
After this you will can call .populate('organization').
If it is impossible, I believe that you will not find a solution better than aliases.
Mongoose Documentation: Aliases
I will follow along your code.looks like you applied this: mongoose.Schema=Schema
you embedded Organization model into User. first lets extract organization details for each user.
//import User and Organization models
const main=async ()=>{
const user=await User.findById("placeUserId")//we get the user
const populated=await user.populate("org_id").execPopulate()//we populated organization with all properties
console.log(populated.org_id) }
in the above code, org_id was already referenced in the userSchema. we just reached org_id property and extracted. this was simple. next without changing any code in userSchema and organizationSchema i will find which user is in which organization with virtual property.
virtual property allows us to create virtual fields in the database. it is called virtual because we do not change anything. it is just a way that to see how two models are related.
for this we are gonna add a little code on the page where you defined you defined your organizationSchema file which i assume in models/organization.js. this code will describe the virtual field. it is kinda schema of the virtual field.
//models/organization.js
organizationSchema.virtual('anyNameForField',{
ref:"User", //Organization is in relation with User
localField:"_id"//field that Organization holds as proof of relation
foreignField:"org_id"//field that User holds as proof of relation
})
now time to write the function to find the user inside the organization.
const reverse=async ()=>{
const organization=await Organization.findById("")
const populated=await organization.populate("anyNameForField").execPopulate()
console.log(populated.anyNameForField) //i gave a stupid name to bring your attention.
}
very simple and elegant!
I am upgrading an Express application to Feathersjs application. I have the following model definition for belongs_to_many associations:
Clase A (usergroup):
userGroup.associate = function (models) {
models.user_group.belongsToMany(models.permission, {through: 'user_group_permission'});
};
Class B (permission):
permission.associate = function (models) {
models.permission.belongsToMany(models.user_group, {through: 'user_group_permission'});
};
With that definition, we sould be able to create a group and use the addPermission/addPermissions method to associate permissions to a group.
The following code intends to associate permissions with a group
newGroup = await Model.create({
groupName: data.groupName
});
newGroup.addPermissions(plist);
where Model is the variable for the UserGroup model class, and plist is a list of Permisssions.
This code is written in a service class generated by the feathers generate service command. Following this suggest, I overwrote the create method. The goal is to work with the permission list provided in create group endpoint.
The result is this one:
In the following image we can see the cause of the exception
What is wrong with models definition if with Express application the same code works fine?
In a sails project, considering a model User and a model Role, with a relationship between User and Role :
// `User.js
module.exports = {
attributes: {
...
roles: {
collection: 'role',
dominant: true
},
...
}
}
For the the database representation, sails/waterline will create following tables :
table user,
table role,
table like user_roles__role_roles_role to represent the collection
I know we can force the name for the models USER and ROLE
(with the property 'tablename' : http://sailsjs.com/documentation/concepts/models-and-orm/attributes).
But how can we force the name the relationship table ? (Especially this name is quite long and tends to exceed limit).
Assuming this is a two-way relationship, and the Role model has a users collection, Sails will expect a table named role_users__user_roles, which has the role id first, user id second.
Your example table name would require User to be dominant and would require the Role model to have an attribute named roles_role that is a User collection.
To create your own join table, you can use the through association method and add a new model that represents the relationship, perhaps UsersRoles, and specify the tableName in that model definition.
Examples of the through association:
sails
docs
similar question
gist from comments in that question