I have the following two tables in Sequelize
const Tokens = sequelize.define("Tokens", {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
active: {
type: DataTypes.BOOLEAN
}
});
and
const User = sequelize.define("Users", {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
first_name: {
type: DataTypes.STRING
}
});
User.associate = models => {
models["Users"].hasMany(models["Tokens"], {foreignKey: 'userID', as: 'tokens_userid'});
};
I'm trying to run the following query in Sequelize.
const token = await db.Tokens.findOne({
where: {
id,
active: true
},
include: ["tokens_userid"]
});
But I'm getting the following error.
Error: Association with alias "tokens_userid" does not exists
My main goal is to get the user based on a Token ID. Now I would just move that association to the User table, but the problem with that later on I will want to get all the tokens for a given User ID. So I will run into this problem either way.
I tried adding the following line, but it was complaining about circular relations or something like that.
models["Tokens"].hasOne(models["User"], {foreignKey: 'userID', as: 'tokens_userid'});
How can I query either the Users or Tokens table and have it populate correctly with the relation?
I was able to solve this by adding the following line to my table.
models["Tokens"].belongsTo(models["User"], {foreignKey: 'userID', as: 'tokens_userid_from_token'});
Basically what I tried before but changed hasOne to belongsTo.
Hopefully this helps someone else.
Related
My idea is to have the ability to add multiple strings at keyword, I've been told to use foreign keys and so am I. But I can't figure out why it's not working properly. Any ideas?
My code is as follows
user.js
var User = sequelize.define('user', {
username: Sequelize.STRING,
myhash: Sequelize.TEXT,
mysalt: Sequelize.STRING,
keyword: Sequelize.STRING
});
var keywordData = sequelize.define('keywordData', {
keywords: Sequelize.STRING
})
User.hasMany(keywordData)
keywordData.belongsTo(User)
User.js
router.post("/newkeyword", function(req, res) {
keywordData.update(
{keywordData: req.body.newkeyword},
{where: {id: req.user['id']}}
)
})
You forgot to indicate a foreignKey option in the second association definition as well as create an auto-increment id for keywordData model.
var keywordData = sequelize.define('keywordData', {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, },
keywords: { Sequelize.STRING }
})
Hence, your association should be like this:
User.hasMany(keywordData)
keywordData.belongsTo(User, {foreignKey: 'keyword', targetKey: 'keywords' })
Also you an refer this document for more details.
I want to build a sequelize relationship that represents : An item is composed by a specific amount of others items.
Database tables
Item (itemId, name)
Ingredient (ingredientId, itemParentId, itemChildrenId, amount)
Sequelize models
// Item.js
class Item extends Sequelize.Model { }
Item.init({
itemId: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: Sequelize.STRING,
}, {
sequelize: db,
})
// Ingredient.js
class Ingredient extends Sequelize.Model { }
Ingredient.init({
ingredientId: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
amount: Sequelize.INTEGER,
}, {
sequelize: db,
})
And I am just trying to write the correct sequelize association to match my database logic, so I tried :
// Association.js
Item.belongsToMany(Item, { through: Ingredient, as: 'ingredients', foreignKey: 'itemParentId' })
But I'm having this error Unknown column 'ingredients->ingredient.ingredientItemId' in 'field list', which is true but I do not know how to specify the right keys/columns.
Any help, please!
I see few problems: First, you're performing Item.init within the Ingredient model. Probably a mistake on your part. Change it to Ingredient.init.(personally i never used this "init" api, i define models differently, so i'm not sure how it works)
Second, change the primary keys of both Ingredient and Item to be just "id", like:
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
}
Also, your association isn't correct, it needs to be through a junction table:
Item.belongsToMany(Ingredient, { through: "ItemIngredient"})
Note that "through" referes here to a table name, that Sequelize will create automatically if you're using model.sync(), and if not- you will need to create it yourself(or with a migration, which i recommend), with columns: itemId, ingredientId.
You also need to add the "reverse" association, like that:
Ingredient.belongsToMany(Item, { through: "ItemIngredient"})
I am getting started with Sequelize. I am following the documentation they are providing on their website :http://docs.sequelizejs.com/manual/installation/getting-started.html
const Sequelize = require('sequelize');
const sequelize = new Sequelize('haha', 'postgres', 'postgres', {
host: 'localhost',
dialect: 'postgres',
operatorsAliases: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
// SQLite only
storage: 'path/to/database.sqlite'
});
sequelize
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
const User = sequelize.define('user', {
firstName: {
type: Sequelize.STRING
},
lastName: {
type: Sequelize.STRING
}
});
// force: true will drop the table if it already exists
User.sync({force: true}).then(() => {
// Table created
return User.create({
firstName: 'John',
lastName: 'Hancock'
});
});
Up until here, everything works perfectly. And the table "user" is correctly built and populated. (Although I do not understand Sequelize appends an "s" automatically to "user", any explanation.)
However when I add the following portion of code:
User.findAll().then(users => {
console.log(users)
})
I get this error :
Unhandled rejection SequelizeDatabaseError: relation "users" does not
exist
So my questions are:
Why does Sequelize add an "s" to user. (I know it makes sense but shouldn't the developer decide that)
What is causing that error? I followed the documentation but it still didn't work?
When you are defining your model you can add configurations, in this case the option that you must add is freezeTableName prevents the names from being plural.
const User = sequelize.define('user', {
firstName: {
type: Sequelize.STRING
},
lastName: {
type: Sequelize.STRING
}
}, {
// disable the modification of table names; By default, sequelize will automatically
// transform all passed model names (first parameter of define) into plural.
// if you don't want that, set the following
freezeTableName: true,
});
There is another interesting way you can avoid this. But you need to really focus on this way of implementation.
const User = sequelize.define("user", {
firstname: {
type: Sequelize.STRING
},
lastname: {
type: Sequelize.STRING
}
});
you intentionally put user here and use users in other places of coding(Assume sequelize will automatically transform all passed model names (first parameter of define) into plural) . This way of coding will simplify your code.
This problem occurs because creating a table is an asynchronous function. The problem is, the findAll() function can get executed while the table has not been created.
to solve this, you can use:
(async ()=>{
await User.sync({force: true});
// Table created
const users=await User.findAll();
console.log(users);
})();
The problem, in my case, was that the table users was not created. You can create the table manually with CREATE TABLE IF NOT EXISTS (SQL) or add the tableName = "users" in the options object:
export const User = db.define('user',
{
id: {
type: DataTypes.UUIDV4,
autoIncrement: true,
primaryKey: true,
},
name: {
type: new DataTypes.STRING(128),
allowNull: false,
},
email: {
type: new DataTypes.STRING(128),
allowNull: true,
},
password: {
type: new DataTypes.STRING(128),
allowNull: true,
},
},
{
freezeTableName: true,
tableName: "users"
}
);
Run that code twice.
Before running the second time, comment out the following code,
// force: true will drop the table if it already exists
User.sync({force: true}).then(() => {
// Table created
return User.create({
firstName: 'John',
lastName: 'Hancock'
});
});
Maybe answer is not entirely connected with you question but I want to describe my experience with this error
Error: relation "users" does not exist.
It appears Sequelize make migrations based on migrations file names and it alphabetical order. My problem was my files naming was not sorted in order to create proper connections.
If you face with this problem make sure yours migration files are fired in proper (in alphabetically) order.
The proper order is to first migrate table without connections (eg. table_A) and then tables with connections to table_A.
As I said this may not be answer for your particular order but I want to share my experiences because I didn't find this information on the internet when I was looking for this error.
Simply append tableName: "Users" to your model configuration.
The easiest way I found to solve, is to explicitly set the tableName on the model. As others have mentioned, sequelize defaults to the plural form of a model as the table name. For instance User, becomes Users.
When you query, sequelize looks after a table with the same name as your model User. By defining the tableName in the model, sequelize should search the correct table. Append tableName: "Users" to your model configuration i.e:
User.init(
{
email: DataTypes.STRING,
password: DataTypes.STRING,
role: DataTypes.INTEGER,
},
{
sequelize,
modelName: 'User',
tableName: 'Users',
}
);
If you want Sequelize to use a singular word ('info') for a model and that same singular word for the table ('info'), you can name the model 'info' and also add tablename: 'info' to the definition of your model.
This is a way to control Sequelize's default behavior of pluralizing model names, or not, on a table-by-table basis.
info.js
module.exports = (sequelize, DataTypes) => {
const info = sequelize.define('info', {
firstname: DataTypes.STRING,
email: DataTypes.STRING,
phone: DataTypes.STRING,
}, {
tableName: 'info'
});
return info;
};
I have 2 models Prophet and Task and they have a m:n relationship:
Prophets
const prophet = sequelize.define('prophets', {
name: {
type: Sequelize.STRING,
primaryKey: true,
unique: true
}
});
prophet.relationship = function(task) {
prophet.belongsToMany(task, {
through: 'prophetTasks',
as: 'tasks',
foreignKey: 'prophetName'
});
};
Tasks
const task = sequelize.define('tasks', {
name: {
type: Sequelize.STRING,
primaryKey: true,
unique: true
}
});
task.relationship = function(prophet) {
task.belongsToMany(prophet, {
through: 'prophetTasks',
as: 'prophets',
foreignKey: 'taskName'
});
};
EDITED:
my problem is sometimes I have to update a prophet which might remove some relationships with tasks, but I cant figure out how to delete the tasks that have no more relationship with any prophets.
I believe I should find all tasks that doesnt belong in prophetTasks table anymore, but I dont know how to query that with sequelize
You can use ON DELETE CASCADE
So whenever the row is deleted, data containing it as a foreign key will be deleted.
I have two models Brand and Campaign.
A Brand can have many Campaigns
export default(sequelize, DataTypes)=> {
const Brand = sequelize.define('Brand', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
})
Brand.associate = models=> {
Brand.belongsToMany(models.Campaign, {
through: models.CampaignBrand,
foreignKey: 'brand',
})
}
return Brand
}
A Campaign can also have many Brand
export default(sequelize, DataTypes)=> {
const Campaign = sequelize.define('Campaign', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
})
Campaign.associate = models=> {
Campaign.belongsToMany(models.Brand, {
through: models.CampaignBrand,
foreignKey: 'campaign',
})
}
return Campaign
}
And here is through model:
export default(sequelize, DataTypes)=> {
const CampaignBrand = sequelize.define('CampaignBrand', {
// see enums
status: {
type: DataTypes.INTEGER,
allowNull: false,
},
roleText: {
type: DataTypes.STRING,
},
})
CampaignBrand.associate = models=> {
CampaignBrand.belongsTo(models.Campaign, {
foreignKey: 'campaign',
targetKey: 'id',
onDelete: 'CASCADE',
})
}
return CampaignBrand
}
In case I want to get Campaigns by brand. What should I do?
I have tried query likes document mentioned but it does not work for me
With Belongs-To-Many you can query based on through relation and select specific attributes. For example using findAll with through
User.findAll({
include: [{
model: Project,
through: {
attributes: ['createdAt', 'startedAt', 'finishedAt'],
where: {completed: true}
}
}]
});
I have found some ways to work around, but it is not what I am looking for:
SOLUTION 1:
Update belongsToMany Brand to hasMany CampaignBrand and the query by CampaignBrand.brand
SOLUTION 2:
Get Campaign by querying Brand
Any other advices?
Dialect: postgres
Database version: 9.4
Sequelize version: 4.2.1
I think you don't need this association in the the through model:
CampaignBrand.associate = models=> {
CampaignBrand.belongsTo(models.Campaign, {
foreignKey: 'campaign',
targetKey: 'id',
onDelete: 'CASCADE',
})
}
You already have the belongsToMany association in the definitions of Brand and Campaign, so I think you just need to create the CampaignBrand model with your status and roleText attributes.
As I understand it, then you can query brands through campaigns and it should return each brand element and its associated campaigns,
Brand.findAll({
include: [{
model: Campaign
}]
});
This answer is kinda old, but with belongstomany associations, you can use mixins.
https://sequelize.org/api/v6/class/src/associations/belongs-to-many.js~belongstomany
const campaigns = await CampaignModel.findAll();
const campaignsBrandsModels = await campaigns.getBrandsModels({
limit:1,
through: {where:{pivot_field:'pivot_value'} } // << You want this, i think!
});
Now, remember that, the mixins (get, set, count...) set their name with inflection library, so, a way to set the names without error (or without spending a lot of time searching the name in in the console) is setting an 'as' alias in the Association
CampaignBrand.belongsToMany(models.Campaign, {
through: models.CampaignBrand,
foreignKey: 'brand',
as: 'BrandsModels' //<<<< This is Super important! at least for easy usage
})
You can achieve this with an include in the findAll method too, BUT!
the 'limit: #' part will not work! (Will give you the error of "This only works with hasMany because separate: true")
I really hope this can help anyone, seya!