Is there a possibility to set a default value for an attribute after the model entity was created?
The attribute didn't exist before.
Use defaultsTo.
When a record is created, if no value was supplied, the record will be
created with the specified defaultsTo value. The supplied value can
also be a function that waterline will run while creating the record.
attributes: {
phoneNumber: {
type: 'string',
defaultsTo: '111-222-3333'
},
orderNumber: {
type: 'text',
defaultsTo: function() {
return uuid.v4();
}
}
}
You also may find Lifecycle callbacks useful depending on your use-case.
Lifecycle callbacks are functions that are automagically called before
or after certain model actions. For example, we sometimes use
lifecycle callbacks to automatically hash a password before creating
or updating an Account model.
Related
I am having a hard time wrapping my head around associations with sailsjs.
I have 2 models
Services
attributes: {
status: {
defaultsTo: 'inactive'
},
userId:{
model: 'users',
via: 'id',
},
},
Users
attributes: {
email: {
type: 'string',
required: true,
unique: true
},
password: {
type: 'string'
}
},
So, a service is tied to a user (matching the id of the user).
I used to do a call like http://localhost:1337/Services?userId=userId
Now I would like to transition to associations using the above model attributes.
This works by calling the ID of the service just fine (it includes the users data as well), however if all i have is the user, how could I get the service
Doing the same call (http://localhost:1337/Services?userId=userId) returns and empty object.
Am I forced to actually have a one-to-one or one-to-many association? I don't understand why I can no longer use the userId field (stored in the DB) to do queries once I start using associations. I guess I am looking for the best of both worlds here.
EDIT:
Let me try make this more clear. Before trying to do associations, I could call this URL (using blueprint)
http://localhost:1337/Services?userId=userId
The Services model used to look like this
attributes: {
status: {
defaultsTo: 'inactive'
},
userId:{
type: 'string',
required: true,
},
},
Then when a user is created, a service for that user is created with the userId matching the ID in the Users table.
Now I would like to implement associations using the above model scheme.
However, because (my best guess) the userId field of the service is mapped to the Users model, I am unable to search for a Server using the userId field that is stored.
I hope that makes sense? In another words, tryin to call
http://localhost:1337/Services?userId=userId
returns nothing when using associations but does return a value when I don't use associations
I recently started working with Sails and mongo.
I use Sails blueprints to generate part of my api.
The problem is, that the request body I send is being saved to the mongo collection, regardless of the fields defined in the model.
So for example, let's say I have the following Event model:
module.exports = {
attributes: {
title: {
type: 'string',
required: true
},
}
}
When I Send a POST request to the /event/ endpoint with the following params:
{"title":"Some Event", "random":"string"}
The saved mongo document contains also the "random":"string" value, even though it's not part of the model.
I've tried to come up with some common method to remove non-model attributes before creation for all models, but the possible solutions seemed not right and dirty.
Am I missing something?
Any help would be appreciated!
You can use schema option in your model. Just add it to model declaration and that's it.
// api/models/Model.js
module.exports = {
schema: true,
attributes: {
title: {
type: 'string',
required: true
}
}
};
On different projects, I've been stucking on a very basic idea.
Everytime, that's the same concern. I want to add a new record to an association, but grabbing the parent without it's primary key.
For example, let's take a api/models/car.js model :
module.exports = {
attributes: {
licensePlate: {
type: 'integer',
required: true,
unique: true
},
locations: {
collection: 'location',
via: 'car'
}
}
};
And an api/models/location.js model :
module.exports = {
attributes: {
coordinates: {
type: 'array',
required: true
},
car: {
model: 'car'
}
}
};
A car can have multiple locations, a location have a single car.
I'm able to add a location to car using the native addTo blueprint action
POST /car/1/locations
{"coordinates":[2.13654,50.323654]}
Now what if, for some reason, I've no access to the car identifier, and feel like using another field, like a unique licensePlate ?
Basically, I would make a custom route inside config/routes, like
POST /car/byplate/:licensePlate/locations': {
controller: 'Car',
action: 'addLocationByPlate'
}
In order to be able to call
POST /car/byplate/AW45RE65/locations
{"coordinates":[2.13654,50.323654]}
And here is the problem... opening my fresh new action controller, I realize that, despite selecting my car by plate, the following logic (validation, location creation, location creation publish, location add to car's locations collection, location addition publish, error handling) is already implemented in sails.js core.
So here is the question:
How to properly call a native blueprint action with a custom route ?
In your controller you can write something like this:
YourModelName.Query(data, function(err, items){
if(err) return err;
res.json(items);
})
So for example if you want to create a new object in Car model you can do something like this:
Car.create({"carID": req.param("carID")}, function(err, items){
if(err) return err;
res.json(items);
})
this will createa new object with the ID you sent as a param.
Same goes for the other queries like add to, update, destroy etc.
I'm looking at the example in the Waterline docs here.
var User = Waterline.Collection.extend({
types: {
// snip
password: function(password) {
return password === this.passwordConfirmation;
});
},
attributes: {
// snip
password: {
type: 'string',
password: true
},
passwordConfirmation: {
type: 'string'
}
}
});
Is there a way to tell Waterline that passwordConfirmation is not part of the schema so that it is not created if migrate is set to alter or drop, or if using a schemaless DB engine?
The actual use case I want is for a clear text password field to validated from the request, but use beforeCreate to populate a hash field that is actually stored (but not allowing the password property to be stored in the process).
Thanks.
Waterline doesn't support transient fields that are validated but not persisted. You can add the schema: true property to your model which will have it filter out any attributes that aren't explicitly declared, but still make them available in lifecycle callbacks. You'd have to do the validation for those attributes manually (in beforeCreate() or beforeValidate() for example), and you'd lose the ability to add arbitrary fields to schemaless dbs, but it's not necessarily a bad solution.
For your case though, I don't see why it's exactly necessary. Why not just hash the password in beforeCreate and save it back to password?
beforeCreate: function (values, cb) {
values.password = hash(values.password);
return cb();
}
I'm having a hard time trying to figure out if sails/waterline even does this.
(so an adequate answer would simply be if this is possible or not, I have been reading docs, looking through github issues and looking through code, but still not sure)
I have a one to one association setup where an 'account' has a 'contact'
I'm trying to create a contact within sails blueprints (so basically just using the create() method)
account =
{ name: 'Corp'
contact:{
firstName: 'Bob',
lastName: 'Jones'
}
}
so should Account.create(account).exec() create the account and the associated contact? Because I'm getting the following error
TypeError: Cannot convert null to object
My model is setup like so
account.js
module.exports = {
migrate: 'safe',
tableName: 'accounts',
autoPK: false,
attributes: {
id: {
type: 'INTEGER',
primaryKey: true,
autoIncrement: true
},
contactId: 'INTEGER',
name: {type: 'STRING', maxLength: 100},
contact: {
model: 'contact',
columnName:'contactId'
}
}
};
I'm using sails 10.0-rc8 / waterline 10.0-rc15
Creating an associated instance at the same time as its parent (aka "nested create") should work, but it's tricky to get things just right when you're dealing with a legacy database. In your case, the contactId attribute declaration is probably causing the issue, since Waterline expects the foreign key field to be implicit, not explicit. Try removing:
contactId: 'INTEGER',
entirely and see where that gets you.
After some research I found out that as of version 0.10.0-rc15 of waterline you can NOT have a customized foreign keys. In the above model if I change the "contactId" column to just "contact" (basically make it look exactly like it does in the docs. Then it works.
I made the following bug report
https://github.com/balderdashy/waterline/issues/529