Related
Tried to create API for internet store using ExpressJS and sequelize. I have users, basket and devices. To connect basket and choosen by user devices used middle table BasketDevice.
Models:
export const Models = {
User: sequelize.define('user',
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
email: { type: DataTypes.STRING, unique: true },
password: { type: DataTypes.STRING },
role: { type: DataTypes.STRING, defaultValue: 'USER' },
isActivated: { type: DataTypes.BOOLEAN, defaultValue: false},
activationLink: { type: DataTypes.STRING, unique: true }
}
),
Basket: sequelize.define('basket',
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
}
),
BasketDevice: sequelize.define('basketDevice',
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
}
),
Device: sequelize.define('device',
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: { type: DataTypes.STRING, allowNull: false, unique: true },
price: { type: DataTypes.INTEGER, allowNull: false },
img: { type: DataTypes.STRING, allowNull: false },
rating: { type: DataTypes.INTEGER, defaultValue: 0 },
}
}
Assotiations between tables:
Models.User.hasOne(Models.Basket)
Models.Basket.belongsTo(Models.User)
Models.Basket.hasMany(Models.BasketDevice, {as: 'devices'})
Models.BasketDevice.belongsTo(Models.Basket)
Models.Device.hasMany(Models.BasketDevice)
Models.BasketDevice.belongsTo(Models.Device)
Tryed to give in response basket include basket device
Basket service:
class BasketService {
async getBasket(token: string) {
const userData = await TokenService.verifyRefreshToken(token) as any
const basket = await Models.Basket.findOne({
where: { userId: userData.id },
include: [{model: Models.BasketDevice, as: 'devices'}]
})
return basket
}
}
Don't know how create getBasket function that gives added to cart devices and counting of one device
What is the opltimal way to give devices added in basket?
This is my partner model
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = require('../connection');
const PartnerInfo = sequelize.define(
'partnerinfo',
{
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: Sequelize.UUIDV4,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
phone: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
loginSecret: {
type: DataTypes.STRING,
allowNull: true,
},
gender: {
type: DataTypes.INTEGER,
defaultValue: 0, // 0:Female 1:male
},
dob: {
type: DataTypes.STRING,
allowNull: true,
},
image: {
type: DataTypes.STRING,
allowNull: true,
},
type: {
type: DataTypes.INTEGER,
defaultValue: 0, // 0:Value 1:Elite
},
joiningDate: {
type: DataTypes.STRING,
allowNull: false,
},
termination_date: {
type: DataTypes.STRING,
allowNull: true,
},
line1: {
type: DataTypes.STRING,
allowNull: true,
},
line2: {
type: DataTypes.STRING,
allowNull: true,
},
landmark: {
type: DataTypes.STRING,
allowNull: true,
},
city: {
type: DataTypes.STRING,
allowNull: true,
},
state: {
type: DataTypes.STRING,
allowNull: true,
},
country: {
type: DataTypes.STRING,
allowNull: true,
},
pincode: {
type: DataTypes.STRING,
allowNull: true,
},
},
{
timestamps: true,
}
);
module.exports = PartnerInfo;
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = require('../connection');
const PartnerInfo = require('./partnerinfo');
const ServiceArea = require('./servicearea');
const PartnerServiceArea = sequelize.define('partnerservicearea', {
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: Sequelize.UUIDV4,
primaryKey: true,
},
partner_id: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'partnerinfos',
key: 'id',
},
},
servicearea_id: {
type: DataTypes.CHAR,
allowNull: false,
},
});
PartnerServiceArea.belongsTo(PartnerInfo, {
foreignKey: {
name: 'partner_id',
allowNull: false,
},
targetKey: 'id',
});
PartnerInfo.hasMany(PartnerServiceArea, {
foreignKey: {
name: 'partner_id',
allowNull: false,
},
targetKey: 'id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
PartnerServiceArea.belongsTo(ServiceArea, {
foreignKey: {
name: 'servicearea_id',
allowNull: false,
},
targetKey: 'id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
ServiceArea.hasMany(PartnerServiceArea, {
foreignKey: {
name: 'servicearea_id',
allowNull: false,
},
targetKey: 'id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
module.exports = PartnerServiceArea;
These are the two models I am trying to associate and also using default sync.
I need help with the linking and want to use complex query
This is the SQL query
CREATE TABLE IF NOT EXISTS `partnerserviceareas`
(
`id` CHAR(36) BINARY NOT NULL,
`partner_id` CHAR(36) BINARY NOT NULL,
`servicearea_id` CHAR(255) NOT NULL,
`createdAt` DATETIME NOT NULL,
`updatedAt` DATETIME NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`partner_id`) REFERENCES `partnerinfos` (`id`)
ON DELETE NO ACTION ON UPDATE CASCADE,
FOREIGN KEY (`servicearea_id`) REFERENCES `service_areas` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
and the error is
Referencing column 'partner_id' and referenced column 'id' in foreign key constraint 'partnerserviceareas_ibfk_1' are incompatible
Please check "charter set" and "collation" on the both related fields, if data type is "string/char" type.
There is no such problem ("foreign key constraint 'xxx' are incompatible") on integer data type (or at least I have not met such incompatibles).
Sometimes created table has different "charter set/collation" from foreign tables (foreign table created by others, restored from old dump etc).
If create new table (charter/collation to be same as foreign table):
CREATE TABLE IF NOT EXISTS `partnerserviceareas` (
...
)
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
If you have already both tables and would like to change a column:
ALTER TABLE `partnerserviceareas`
CHANGE COLUMN `partner_id` `partner_id` CHAR(36)
CHARACTER SET 'utf8mb4'
COLLATE 'utf8mb4_unicode_ci' NOT NULL ;
It's my first time using Sequelize with MySQL and would like to understand how to insert data into two tables with a foreign key dependency.
Let's say, I have two tables - bookingDescription and bookingSummary. The entities for the same are as follows
//bookingSummary
module.exports = (sequelize, DataTypes) => {
return sequelize.define('bookingSummary', {
headerId: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true,},
titleId: DataTypes.STRING,
abNumber: DataTypes.INTEGER,
userProfileId: DataTypes.INTEGER,
theatreId: DataTypes.STRING,
mFId: DataTypes.STRING,
createdBy: { type: DataTypes.TEXT, allowNull: false },
modifiedBy: { type: DataTypes.TEXT, allowNull: false },
status: DataTypes.STRING
}, {
tableName: 'booking_summary',
underscored: true
})
}
//bookingDescription
module.exports = (sequelize, DataTypes) => {
return sequelize.define('bookingWeek', {
lineId: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true,},
headerId: DataTypes.STRING,
titleId: DataTypes.STRING,
abNumber: DataTypes.INTEGER,
theatreId: DataTypes.STRING,
mFId: DataTypes.STRING,
theatreName: DataTypes.STRING,
city: DataTypes.STRING,
state: DataTypes.STRING,
playStartDate: DataTypes.DATE,
playEndDate: DataTypes.DATE,
preferredFormat: DataTypes.INTEGER,
screensInDay: DataTypes.INTEGER,
featureTypeFlag: DataTypes.TEXT,
cofeatureName: DataTypes.STRING,
exhbtrReqComment: DataTypes.STRING,
createdBy: { type: DataTypes.TEXT, allowNull: false },
modifiedBy: { type: DataTypes.TEXT, allowNull: false },
status: DataTypes.STRING
}, {
tableName: 'booking_description',
underscored: true
})
}
Foreign key references in bookingSummary are as follows
FOREIGN KEY (headerId) REFERENCES bookingSummary(headerId)
FOREIGN KEY (titleId, abNumber, mFId)
REFERENCES bookingSummary(abNumber)
I have an object of data which needs to be inserted into the tables at an instant.
Any idea on how to approach this ?
So, i would recommend you first learning about SQL relations, regardless of Sequelize, and your current project structure, because it seems to me that you still lack the basic understanding of the logic behind these associations, where they should be used and how.
Both your tables have duplicate fields, which makes no sense. Take a look here, for example: https://code.tutsplus.com/articles/sql-for-beginners-part-3-database-relationships--net-8561
As for your current code, i played with it a bit, and this is what i did(will work only if you're using model.sync(), because a column needs to be created automatically. Also use force:true if you already have the tables):
module.exports = (sequelize, DataTypes) => {
const bookingHeader = sequelize.define('bookingHeader', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
abNumber: DataTypes.INTEGER,
userProfileId: DataTypes.INTEGER,
theatreId: DataTypes.STRING,
mFId: DataTypes.STRING,
createdBy: { type: DataTypes.TEXT, allowNull: true },
modifiedBy: { type: DataTypes.TEXT, allowNull: true },
status: DataTypes.STRING
}, {
tableName: 'booking_header',
// underscored: true
})
bookingHeader.associate = function (models) {
bookingHeader.hasOne(models.bookingDescription);
};
return bookingHeader;
}
Booking description:
module.exports = (sequelize, DataTypes) => {
const bookingDescription = sequelize.define('bookingDescription', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, },
abNumber: DataTypes.INTEGER,
theatreId: DataTypes.STRING,
mFId: DataTypes.STRING,
theatreName: DataTypes.STRING,
city: DataTypes.STRING,
state: DataTypes.STRING,
playStartDate: DataTypes.DATE,
playEndDate: DataTypes.DATE,
preferredFormat: DataTypes.INTEGER,
screensInDay: DataTypes.INTEGER,
featureTypeFlag: DataTypes.TEXT,
cofeatureName: DataTypes.STRING,
exhbtrReqComment: DataTypes.STRING,
createdBy: { type: DataTypes.TEXT, allowNull: false },
modifiedBy: { type: DataTypes.TEXT, allowNull: false },
status: DataTypes.STRING
}, {
tableName: 'booking_description',
// underscored: true
})
bookingDescription.associate = function (models) {
bookingDescription.belongsTo(models.bookingHeader);
};
return bookingDescription;
}
In your route, you can do something like this:
const {bookingHeader,bookingDescription} = models;
app.post("/booking", async (req, res, next) => {
try {
const header = await bookingHeader.create(req.body)
const headerId = header.id
await bookingDescription.create({state:'active',createdBy:'somebody',modifiedBy:'somebody',bookingHeaderId:headerId})
res.json(header);
} catch (error) {
console.log(error)
res.status(500)
}
});
Notice that i hardcoded some of the fields for the description, just for example purposes. Also, it's possible that there is a "better" way to do this with Sequelize, but the docs are really poor, so this gets the job done.
I cannot seem to figure out how to seed ARRAY(ENUM) using Sequelize. When I am registering a user via my app, I can create a new user fine, but when I am using the queryInterface.bulkInsert in a seed file, I am getting:
ERROR: column "roles" is of type "enum_Users_roles"[] but expression is of type text[]
here is my code:
return queryInterface.bulkInsert('Users', [
{
email: faker.internet.email(),
roles: ['user'],
password: "hash",
public_id: faker.random.uuid(),
created_at: new Date(),
updated_at: new Date()
}
]);
and here is my migration file for the user:
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
email: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
roles: {
type: Sequelize.ARRAY(Sequelize.ENUM({
values: ['user', 'setter', 'admin']
})),
allowNull: false
},
public_id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
allowNull: false
},
created_at: {
allowNull: false,
type: Sequelize.DATE
},
updated_at: {
allowNull: false,
type: Sequelize.DATE
}
})
I am just assuming that I am doing it wrong, but I cannot find any documentation on how to do it correctly. If anyone can help and explain (teach a man to fish), I would appreciate it.
Someone answered on github here
This is their answer which worked for me (and I greatly appreciate)
You can use this code
class Item extends Sequelize.Model { }
Item.init({
name: { type: DataTypes.STRING },
values: {
type: DataTypes.ARRAY(DataTypes.ENUM({
values: ['a', 'b']
}))
}
}, {
sequelize,
timestamps: true
})
sequelize.sync({ force: true }).then(async () => {
await sequelize.queryInterface.bulkInsert('Items', [
{
name: 'xyz',
values: sequelize.literal(`ARRAY['a']::"enum_Items_values"[]`),
createdAt: new Date(),
updatedAt: new Date()
}
]);
});
Executing (default): INSERT INTO "Items" ("name","values","createdAt","updatedAt") VAL
[1]: https://github.com/sequelize/sequelize/issues/11541#issuecomment-542791562
Obviously you'll want to change the array values and enum values. For example, mine would be
ARRAY['user']::"enum_Users_values"[]
I’m new with sequelize I’m trying to make a request with associate tables
I have a first model called Experience
module.exports = function (sequelize, DataTypes) {
const Experience = sequelize.define('experience', {
internalId: {
type: DataTypes.BIGINT,
unique: true,
allowNull: false,
},
label: {
type: DataTypes.STRING,
unique: false,
allowNull: false,
},
picture: {
type: DataTypes.TEXT,
unique: false,
allowNull: true,
},
type: {
type: DataTypes.STRING,
validate: {
isIn: {
args: [[
'generic',
'specific',
]],
msg: 'Must be a valid type',
},
},
unique: false,
allowNull: true,
},
author: {
type: DataTypes.STRING,
unique: false,
allowNull: true,
defaultValue: 'import',
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
});
Experience.associate = (models) => {
Experience.belongsToMany(models.Tag, {
as: 'Tags',
through: models.ExperienceTag,
});
};
return Experience;
};
a second called Tags
module.exports = function (sequelize, DataTypes) {
const Tag = sequelize.define('tag', {
internalId: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
},
name: {
type: DataTypes.STRING,
unique: false,
allowNull: false,
},
author: {
type: DataTypes.STRING,
unique: false,
allowNull: true,
defaultValue: 'import',
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
});
Tag.associate = (models) => {
Tag.belongsToMany(models.Experience, {
as: 'Experiences',
through: models.ExperienceTag,
});
};
return Tag;
};
The association table name was ExperienceTags
I would like get all the Experiencewho have a tagId = 44
This is my request:
Experience.findAll({
include: [{
model: ExperienceTag,
where: { tagId: 44 },
}],
})
.then((results) => {
winston.warn(JSON.stringify(results, null, 2));
res.status(200)
.send(results);
})
.catch(error => res.status(500)
.send({ error: error.toString() }));
But when I execute it I have an error like:
{
"error": "SequelizeEagerLoadingError: experienceTag is not associated to experience!"
}
I think you like to include Tag rather than ExperienceTag, the following example may help you
Experience.findAll({
include: [{
model: Tag, //model name which you want to include
as: 'Tags', // you have to pass alias as you used while defining
where: { tagId: 44 },
}],
})
I think , you need to add as: 'Experiences' , in your include as you have defined association with alias
Change this
Experience.findAll({
include: [{
model: ExperienceTag,
where: { tagId: 44 },
}],
})
With
Experience.findAll({
include: [{
model: ExperienceTag,
as: 'Experiences', // <---- HERE
where: { tagId: 44 },
}],
})