This is my College model
module.exports = {
attributes: {
name:{
type:'string',
required:true
},
location:{
type:'string',
required:true
},
faculties:{
collection:'faculty',
via:'college'
}
}
};
This is my Faculty model
module.exports = {
attributes: {
name:{
type:'string',
required:true
},
description:{
type:'string'
},
college:{
model:'college',
required:true
}
,
years:{
collection:'year',
via:'faculty'
}
}
};
My problem is that I can add new Faculty with any value in college attribute. If I don't have college with 3000 id, I can still add it but college attribute won't show up when I list all faculties. How can I prevent it from adding a faculty with invalid college id?
Currently waterline does not create foreign key constraints in the manner you describe. It only creates the associated field.
You can use a different library instead of Waterline such as Sequelize.js here is a link about how to go about doing that
https://groups.google.com/forum/#!topic/sailsjs/ALMxbKfnCIo
If your are using a SQL database you can manally create the foreign key constraint yourself.
Or you can validate the value of college before being set in your faculty model by checking in beforeValidate() or afterValidate() on your faculty model.
Related
I'm new to TypeORM and I'm stuck in a chicken/egg scenario. I have the following entity:
#Index("institutes_pkey", ["idInstitute"], { unique: true })
#Entity("institutes", { schema: "public" })
export class Institutes {
#PrimaryGeneratedColumn({ type: "bigint", name: "id_institute" })
id_institute: string;
#Column("text", { name: "name" })
name: string;
#Column("text", { name: "description" })
description: string;
#ManyToOne(() => Institutes, (institutes) => institutes.institutes)
#JoinColumn([{ name: "id_grupo", referencedColumnName: "idInstitute" }])
id_group: Institutes;
#OneToMany(() => Institutes, (institutes) => institutes.id_group)
institutes: Institutes[];
}
child_institutes can be grouped under the umbrella of a bigger father_institute.
In that case the the id groups would look like this:
father_institute.id_group = father_institute.id_institute
child_institutes.id_group = father_institute.id_institute.
If a institute won't be part of any group, it's id_group equals it's id_institute (same as father case). And here lies my issue, when I need to save a father_institute, it's FK references the very same object I'm trying to save. What can I do in that case?
I imagined a workaround, create a sequence in PSQL and set id_institute with that sequence then set the default value of id_group to the current value of this sequence. But is there any better solution for this?
I have doctor and specialization table, and have doctor_specialization_pivot table. In my pivot table I have the following columns:
| doctor_id | additional_data | specialization_id |
additional_data is from the doctor model along with the doctor_id.
In my doctor model file, I have this relationship:
public $belongsToMany = [
'specialization' => [
'path\to\specialization\model',
'table' => 'doctor_specialization_pivot',
'parentKey' => 'doctor_id',
'otherKey' => 'specialization_id',
]
];
Now during submit of form, I'm getting this error:
SQLSTATE[HY000]: General error: 1364 Field 'additional_data' doesn't have a default value (SQL: insert into doctor_specialization_pivot (doctor_id, specializations_id) values (1, 3))"
I tried adding to my relationship 'pivot' => ['additional_data']. But still getting the same error.
I checked the submitted data and additional_data is not empty. I checked from OctoberCMS forums but not getting straight forward answers such as this and this.
Okay. I found the answer to my own question.
I'll answer in detail to help everyone. After digging and blind shooting. According to this documentation here, we can use the method attach() to attach a role to a user by inserting a record in the intermediate table that joins the models.
What confuse me in the documentation is that it uses a $roleId variable and I didn't understand where the $roleId came from. If it's the id of the parent table or the id of other table.
Sample from the link:
$user = User::find(1);
$user->roles()->attach($roleId);
So what I did in my doctor model, I hook to the event beforeSave, use the relationship ($this->specialization) as the first parameter instead of the id in the docs. The $this->specialization() is the relationship too defined in belongsToMany.
Answer:
public function beforeSave()
{
$this->specialization()->attach($this->specialization,['additional_data' => 'additional data from doctor table']);
}
The implementation is pretty much like this video from Watch Learn (Ivan). You can learn a lot about OctoberCMS just by watching his guide on it. Here is the documentation on it as well. This is the example info that I have done.
WARNING Another known flaw is you can't apply this to a model record that isn't created yet. Unlike the standard relation widget which waits until it is saved before attaching records this attaches records in a separate overlay form.
Here is my model.php relationship:
public $belongsToMany = [
'equipments' => [
'Brandon\Pixelrpg\Models\Equipments',
'table' => 'brandon_pixelrpg_equipment_inventory',
'key' => 'inventory',
'otherKey' => 'equipment',
'pivot' => ['quantity']
]
];
Here is my controller.php:
public $implement = [
'Backend\Behaviors\ListController',
'Backend\Behaviors\FormController',
'Backend\Behaviors\ReorderController',
'Backend\Behaviors\RelationController'
];
public $listConfig = 'config_list.yaml';
public $formConfig = 'config_form.yaml';
public $reorderConfig = 'config_reorder.yaml';
public $relationConfig = 'config_relation.yaml';
Here is my config_relation.yaml:
equipments:
label: Equipments
view:
list:
columns:
id:
label: ID
type: number
searchable: true
sortable: true
name:
label: Name
type: text
searchable: true
sortable: true
value:
label: Value
type: number
searchable: true
sortable: true
updated_at:
label: Updated
type: datetime
searchable: true
sortable: true
pivot[quantity]:
label: Quantity
type: number
pivot:
form:
fields:
pivot[quantity]:
label: Quantity
type: number
default: 0
I am just going to make a new answer and assume is what you need because you have yet to show any code on how your form works. This is how I would update the pivot information from a frontend form.
Relationship in model.php:
public $belongsToMany = [
'specialization' => [
'path\to\specialization\model',
'table' => 'doctor_specialization_pivot',
'parentKey' => 'doctor_id',
'otherKey' => 'specialization_id',
'pivot' => ['additional_data'] //This is required
]
];
Then in some php code lets call it onAddSpecialization():
public function onAddSpecialization() {
//Calling a function to get the doctor id maybe from the signed in user
$doctor = Doctors::find($this->$doctorId());
//We get our Specialization from an input
$specialization = Specialization::find(Input::get('specialization_id'));
//We get our additional data from an input
$additional_data = Input::get('additional_data');
//Now we are going to attach the information
$doctor->specialization()->attach($specialization, ['additional_data' => $additional_data]);
}
Now an example of updating our additional data:
public function onUpdateAdditionalData() {
//Calling a function to get the doctor id maybe from the signed in user
$doctor = Doctors::find($this->$doctorId());
//If you get specialization by id from an input. I believe you need to go through the relationship in order to access the correct pivot information.
$specialization = $doctor->specialization->where('id', Input::get('specialization_id'))->first();
//Insert the new pivot information
$specialization->pivot->additional_data = $new_additional_data;
//Save
$specialization->pivot->save();
}
I'm using Sequelize in my project. These are the two models:
const User = db.define('user', {
name: Sequelize.STRING,
password: Sequelize.STRING
})
const Product = db.define('product', {
name: Sequelize.STRING,
price: Sequelize.INTEGER
})
Now users can purchase products and I have associations setup like below:
Product.belongsToMany(User, {through: 'UserProducts'})
User.belongsToMany(Product, {through: 'UserProducts'})
I also have this UserProducts table with an additional column.
const UserProducts = db.define('UserProducts', {
status: Sequelize.STRING
})
Sequelize creates a composite key with combination of userId and productId and will only allow one record with a combination of userId and productId. So, for example, userId 2 and productId 14.
This is a problem for me because sometimes people want to purchase multiple times. I need one of the following scenarios to work:
Don't use the composite key and instead have a completely new auto-increment column used as key in UserProducts.
Instead of making key with userId and productId alone, allow me to add one more column into the key such as the status so that unique key is a combination of the three.
I do want to use the associations as they provide many powerful methods, but want to alter the unique key to work in such a way that I can add multiple rows with the same combination of user id and product id.
And since my models/database is already running, I will need to make use of migrations to make this change.
Any help around this is highly appreciated.
If anyone else is having problems in v5 of Sequelize, it is not enough to specify a primary key on the 'through' model.
You have to explicitly set the unique property on the through model.
User.belongsToMany(Product, { through: { model: UserProducts, unique: false } });
Product.belongsToMany(User, { through: { model: UserProducts, unique: false } });
Belongs-To-Many creates a unique key when the primary key is not present on through model.
Since you also have additional property in your UserProducts table, you need to define the model for UserProducts and then tell the sequelize to use that model instead of creating its own
class User extends Model {}
User.init({
name: Sequelize.STRING,
password: Sequelize.STRING
}, { sequelize })
class Product extends Model {}
ProjProductect.init({
name: Sequelize.STRING,
price: Sequelize.INTEGER
}, { sequelize })
class UserProducts extends Model {}
UserProducts.init({
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
status: DataTypes.STRING
}, { sequelize })
User.belongsToMany(Project, { through: UserProducts })
Product.belongsToMany(User, { through: UserProducts })
refer: Sequelize v4 belongsToMany
UPDATE
since you are using v3 and already have a model created for your UserProducts table use following snippet
UserProducts = db.define('UserProducts', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
status: DataTypes.STRING
})
Since setting explicitly the unique property on the through model is not working in v6, the only solution i found is to define the 3 parts of the association this way :
User.hasMany(UserProducts);
UserProducts.belongsTo(User);
Product.hasMany(UserProducts);
UserProducts.belongsTo(Product);
You can then create your models and associations :
const user = await User.create(user_data);
const product = await Product.create(product_data);
const up = await UserProduct.create(up_data);
await up.setUser(user);
await up.setProduct(product);
If anyone has a better solution, I would be happy to know it
I have a SQL server database with a table which auto inserts a guid in each row when a record is added. This column is the primary key for the table and never needs a value to be supplied to it as it's automatic.
The trouble is that Sequelize is sending NULL for this column every time I do a .create({emailAddress:"test#test.com"}) call which is causing an error in the database (because nulls aren't allowed, obviously).
I've tried adding omitNull at the top level and at the "call" level and neither of them work , unless I remove the primary key and then it doesn't send a NULL. So it seems that Sequelize thinks that if something is the primary key then it must send a value, not understanding that the SQL SERVER database is going to handle insertion of that value.
Anybody know a workaround?
// Model
module.exports = function (sequelize, DataTypes) {
const Player = sequelize.define('Player', {
playerId: {
primaryKey: true,
type: DataTypes.STRING
},
emailAddress: {
type: DataTypes.STRING
}
}, {
timestamps: false
});
return Player;
};
// Create a row
let newPlayer = {
emailAddress:'test#test.com'
}
Player.create(newPlayer, {omitNull:true}).then(function(player){
console.log("player", player)
}).catch(function(error){
console.log("error", error)
})
Adding defaultValue and allowNull should do the job
playerId: {
primaryKey: true,
defaultValue: '',
allowNull: false,
}
Does anyone know how I can use compound keys to relate to a table using Sequelize and their built in relationships?
I have tried useing primaryKey: true and unique: with no avail.
Here is the example , how you can define composite key in sequlize
sequelize.define('item_likes',{
item_id:{
type: db.Sequelize.INTEGER,
allowNull: false,
unique:"liked_user" // this will create composite key
},
user_id:{
type: db.Sequelize.INTEGER,
allowNull: false,
unique:"liked_user" // this will create composite key
},
anonymously:{
type : db.Sequelize.BOOLEAN,
defaultValue : false,
comment : "( True : user wants to be anonymous )"
}
}
All you need to do is use unique:"liked_user" , where liked_user is name of key , just for identification , and that should be same for the fields you want to make composite.
Association :
ItemLikes.belongsTo(Item,{foreignKey: 'item_id'});
Item.hasMany(ItemLikes,{foreignKey:'item_id'});
ItemLikes.belongsTo(User,{ foreignKey : 'user_id'});
User.hasMany(ItemLikes, { foreignKey: 'user_id'});