After many months away from Keystone, I'm back and redoing one of my earlier sites where Keystone was only used for part of the site. (and I wanted to play with the new shiny bits!)
However, I'm having issues with the create scripts. So I was reading #334 and decided that I liked what @LegitTalon was suggesting and thought I'd give it a go.
My issue is in the update scripts how can I setup a User and populate a relationship (many) field type?
So I have two update scripts:
./updates/0.0.1-roles.jsexports.create = {
Role: [
{ name: 'Member' },
{ name: 'Administrator' },
{ name: 'Speaker' }
]
};
./updates/0.0.2-admins.jsexports.create = {
User: [
{ 'name.first': 'Admin', 'name.last': 'User', email: '[email protected]', password: 'admin', isAdmin: true, roles: [ 'Member', 'Administrator' ] }
]
};
And the output is:
Successfully applied update 0.0.1-roles.
------------------------------------------------
Applying update 0.0.2-admins...
------------------------------------------------
Update 0.0.2-admins failed with error:
{ type: 'invalid ref',
srcData:
{ 'name.first': 'Admin',
'name.last': 'User',
email: '[email protected]',
password: 'admin',
isAdmin: true,
roles: [ 'Member' ],
__doc:
{ __v: 0,
isAdmin: true,
password: '$2a$10$u7xyETeUCeDUd2foAfhV2uGxH4wi9IygADLZPwtkk6wcU0dsPk0zW',
email: '[email protected]',
roles: [],
name: { last: 'User', first: 'Admin' },
_id: 54a3db35b1342dd31c34fd02 } },
message: 'Relationship User.roles contains an invalid reference.' }
Which seems to be from ./lib/core/createItems.js#L225
@jamlen you may have two issues here:
1) to resolve relationships using createItems I believe all updates should happen within the same updates file as it does everything in memory
2) it's hard to say because you didn't paste the list models but when you create the roles each role should have a relationship reference name. For example:
Role: [
{ name: 'Member', __ref: 'roleMember' },
{ name: 'Administrator', __ref: 'roleAdmin },
{ name: 'Speaker', __ref: 'roleSpeaker' }
]
Then when assigning roles in the user model you use the role relationship name. For example:
User: [
{ 'name.first': 'Admin', 'name.last': 'User', email: '[email protected]', password: 'admin', isAdmin: true, roles: [ 'roleMember', 'roleAdmin' ] }
]
Again, I made assumptions here about how your list models are defined.
Thanks @webteckie for the quick reply...
Heres the list models:
var Role = new keystone.List('Role', {
autokey: { from: 'name', path: 'key', unique: true }
});
Role.add({
name: { type: String, initial: true, index: true },
});
Role.relationship({ ref: 'User', path: 'roles' });
Role.register();
var keystone = require('keystone'),
Types = keystone.Field.Types;
var User = new keystone.List('User', { roles: [ 'Administrator' ] });
User.add({
name: { type: Types.Name, required: true, index: true },
email: { type: Types.Email, initial: true, required: true, index: true },
password: { type: Types.Password, initial: true, required: true }
}, 'Permissions', {
isAdmin: { type: Boolean, label: 'Can access Keystone', index: true },
roles: { type: Types.Relationship, ref: 'Role', many: true, initial: true, default: ['Member'] }
});
// Provide access to Keystone
User.schema.virtual('canAccessKeystone').get(function() {
return this.isAdmin;
});
User.relationship({ ref: 'Post', path: 'author' });
User.defaultColumns = 'name, email, roles';
User.register();
And here is the contents of the Roles Collection:
> db.roles.find()
{ "_id" : ObjectId("54a3e400c0b77f46b1b868fb"), "key" : "member", "name" : "Member", "__v" : 0 }
{ "_id" : ObjectId("54a3e401c0b77f46b1b868fc"), "key" : "administrator", "name" : "Administrator", "__v" : 0 }
{ "_id" : ObjectId("54a3e401c0b77f46b1b868fd"), "key" : "speaker", "name" : "Speaker", "__v" : 0 }
{
@jamlen yeah, my recommendations still hold. Give it a try!
@webteckie thanks again, so I changed it to one file:
exports.create = {
Role: [
{ name: 'Member', __ref: 'member' },
{ name: 'Administrator', __ref: 'administrator' },
{ name: 'Speaker', __ref: 'speaker' },
],
User: [
{ 'name.first': 'Admin', 'name.last': 'User', email: '[email protected]', password: 'admin', isAdmin: true, roles: [ 'member', 'administrator' ] },
{ 'name.first': 'Member', 'name.last': 'User', email: '[email protected]', password: 'test', isAdmin: true, roles: [ 'member' ] }
]
};
And that has now worked....
However, it seems to me that I shouldn't have to add the explicit __ref especially as there is the key property. Should this be considered a bug, improvement, or just one of those things?
@jamlen that's great that it works for you now! Yeah, you need the __ref property as currently that is what createItems uses to automate the relationship setup. Otherwise, since you are creating things for the first time you have no clue as to what key to use! Of course, there's another custom approach to initializing things where you manually set everything up and you can do without that __ref key but that is more work! createItems simplifies it for you :-)
@jamlen While we could simplify things by turning one of the properties (in your case name) into the __ref field, in a lot of cases it's less "safe" as keys are dynamically generated, fields can be validated or updated based on schema config, etc.
So in my opinion explicitly defining a __ref property in the update script is clunky but much clearer once you know to do it.
I'm open for suggestions on how we could do it otherwise syntactically, one that comes to mind is:
exports.create = {
Role: [
{ name: 'Member' },
{ name: 'Administrator' },
{ name: 'Speaker' },
],
Role__ref: 'name',
User: [
{ 'name.first': 'Admin', 'name.last': 'User', email: '[email protected]', password: 'admin', isAdmin: true, roles: [ 'Member', 'Administrator' ] },
{ 'name.first': 'Member', 'name.last': 'User', email: '[email protected]', password: 'test', isAdmin: true, roles: [ 'Member' ] }
]
};
I'm not sure that's a huge improvement though.
We could alternatively allow object-syntax definition:
exports.create = {
Role: {
member: { name: 'Member' },
administrator: { name: 'Administrator' },
speaker: { name: 'Speaker' }
},
User: [
{ 'name.first': 'Admin', 'name.last': 'User', email: '[email protected]', password: 'admin', isAdmin: true, roles: [ 'member', 'administrator' ] },
{ 'name.first': 'Member', 'name.last': 'User', email: '[email protected]', password: 'test', isAdmin: true, roles: [ 'member' ] }
]
};
@JedWatson could we make use of the autokey if it is set on the list?
Also some documentation for how to do this kind of relational setup would be good.
New to the project, but digging in deep and pretty impressed. I'm digging through the process of setting up a (fairly large) initial data load for a project, and ran into a similarly problem with references.
Barring the ability to specify a custom _id field for a schema ala mongoose, I think the object-syntax definition would be the next best thing. Referring to things by their incoming key in the import file feels a bit less magical than a __ref property that isn't actually persisted once the update is complete.
I'll close this due to inactivity, let me know if this should still be considered at the current state of things!
This method does not seem to work anymore in v4...
Any workarounds for this?
Any news about this? Did anyone solve it for v4?
Any news about this? Did anyone solve it for v4?
Most helpful comment
Any news about this? Did anyone solve it for v4?