-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JSON.stringify() a model skips some properties #5464
Comments
Hmm wonder if toJSON() gets called automatically by JSON.stringify()? Doubt it.. What happens if you call toJSON() on the model instance first? |
Same thing, the properties still won't show up. |
@pixelfreak can you post your models and the code you are using the // User model
module.exports = {
attributes: {
name: 'string',
pets: {
collection: 'pet',
via: 'owner'
}
}
}; // Pet model
module.exports = {
attributes: {
name: 'string',
owner: {
model: 'user'
}
}
}; // Controller method
find: function(req, res) {
User.findOne(1).populate('pets').exec(function(err, user) {
if(err) return res.serverError(err);
var str = JSON.stringify(user);
console.log(str);
res.send(str);
});
} |
Here it is: module.exports =
{
tableName: 'screen_category',
attributes:
{
id:
{
type: 'INTEGER',
primaryKey: true
},
name: 'STRING',
type: { type: 'STRING', in: ['bowl-builder', 'grid-item'] },
sortOrder: 'INTEGER',
items:
{
collection: 'Item',
via: 'screenCategory'
}
}
}; module.exports =
{
tableName: 'item',
attributes:
{
id:
{
type: 'INTEGER',
primaryKey: true
},
name: 'STRING',
description: 'TEXT',
price: 'DECIMAL',
sku: 'STRING',
active: 'BOOLEAN',
sortOrder: 'INTEGER',
screenCategory:
{
model: 'ScreenCategory'
},
choiceCategories:
{
collection: 'ChoiceCategory',
via: 'items',
dominant: true
},
storeItems:
{
collection: 'StoreItemMap',
via: 'itemId'
}
}
};
|
I found that this is not just when |
@pixelfreak there was an issue a few days ago we ran into with populate and custom defined primary keys that may have been the issue here. I just pushed [
{
"items": [
{
"id": 1,
"name": "movie-item",
"createdAt": "2014-03-20T20:52:30.082Z",
"updatedAt": "2014-03-20T20:52:30.082Z",
"screenCategory": 1
},
{
"id": 2,
"name": "music-item",
"createdAt": "2014-03-20T20:52:37.200Z",
"updatedAt": "2014-03-20T20:52:37.200Z",
"screenCategory": 1
}
],
"id": 1,
"name": "movie",
"createdAt": "2014-03-20T20:51:58.989Z",
"updatedAt": "2014-03-20T20:51:58.989Z"
}
] |
Same thing. |
This is so weird...if I do this: myScreenCategory.foo = myScreenCategory.items;
JSON.stringify(myScreenCategory); ...then foo will show up with all the data that |
I had the same Problem with populated items not showing up after using toJSON, so I omitted it. I suspect it to be the additional add and remove function in many associations, which will break the stringify. Kind Regards Sent from my iPhone
|
My problem is when I populate an attribute, it shows all attribute including deleted one from to JSON
|
@TheFinestArtist can you check which version of Waterline you are using? You should be on {
"device": {
"type": "iphone",
"id": 1,
"owner": 1
},
"username": "appletest0",
"email": "apple0@apple.com",
"full_name": "Apple Test 0",
"createdAt": "2014-03-24T17:08:08.633Z",
"updatedAt": "2014-03-24T17:08:08.633Z",
"id": 1
} |
I am on It definitely looks like the javascript object properties defined is causing some of the properties to not be enumerable. Why is it done this way, I don't know... sails-mysql@0.10.0-rc3 |
@pixelfreak so here's a run down on how the associations and the The So given the model: attributes: {
cars: {
collection: 'car',
via: 'driver'
},
boats: {
collection: 'boat',
via: 'sailors'
}
} There are two virtual attributes: Model.find().exec(function(/* ... */)) I would get the following response when passed through [
{
id: 1,
createdAt: 'some timestamp',
updatedAt: 'some timestamp'
}
] But if I did the following query I would get a different response: Model.find()
.populate('cars')
.populate('boats')
.exec(function(/* ... */)) Would give me: [
{
id: 1,
cars: [],
boats: [],
createdAt: 'some timestamp',
updatedAt: 'some timestamp'
}
] This is because I told the model class that I care about the What I suspect is happening in your case, is that you are attaching associated collection properties If these virtual collection attributes are not stripped out when you don't query them, you would If this is the case, a fix in your code is to call cc @mikermcneil |
@particlebanana I've been trying to do "deeper populations" as you put it, and I'm not having much luck finding a nice clean way to do it. I would love to see a good example of how to do this. Why not have .toObject() check if any associated data is there rather than relying on attributes of the parent query? |
I may be having a similar issue, though there's a weird twist with express. I have a pair of HABTM model - DiaryItem and Tag. I have some logic in my controller to handle creating a DiaryItem with Tags attached to it, and then a helper method to reload the final result and send it back to the user. The reload-and-send method looks like this:
If I post something like this:
I see the right things in the console and response:
However, if I do
and I still get the weird behavior (tags present in log and missing in response). |
Hey guys as explained above the The solution is to either call We are working on deeper populations which once available will allow you do this stuff without worrying about building the response manually. |
Here is an example of how I managed to get a deep population to work... |
+1 this is still an issue for me. I'm using I tried calling toJSON and tried toObject, neither worked. |
+1 this is still an issue for me as well |
This is still an issue. A simple (but very bad) workaround is to set the .toJSON property of the returned record to anything other than a function, for example: User.find().exec(function(err, users){
console.log(JSON.stringify(users[0]))
})
//{ id:1, name:'Susan'} //no post IDs!
User.find().exec(function(err, users){
console.log(users[0].posts);
})
//['1', '2', '3', add:[Function add], remove:[Function remove]] //but actually here they are!!
User.find().exec(function(err, users){
users[0].toJSON="I won't allow you to use sails toJSON, just use the native stringification";
console.log(JSON.stringify(users[0]))
})
//{ id:1, name:'Susan', posts:[1,2,3]} //post IDs are back! As indicated by @particlebanana this is a problem with toObject, in particular it has to do with this line, blame it for all this mess!. Fixing the if condition or commenting it in your local waterline solves the problem, which is obviously a much better workaround, that is until it get official fixed. This is very related to the user-case behind https://github.com/balderdashy/waterline/issues/532, although it is a completely separate issue. cc @mphasize |
@particlebanana this was marked In Progress forever ago. is there any update or did it call through the cracks |
Any word here? @anasyb do you think your fix is solid enough to submit a PR? |
@devinivy I had it running for a while with that line commented out... no notice of anything going wrong. Before a PR I suppose we need an understanding of what that's line purpose in life is/was. Can someone run the tests with that line commented and post here which ones fail (if any)? |
I wonder if If someone could even write a failing test here, that would be awesome. |
Test case should be fairly simple, test if an association that has some items in it (therefore .length > 0) has same .length before and after calling .toObject(). Here is an example: User.find().exec(function(err, rec){
//15 (or whatever length truly is!)
console.log(rec[0].posts.length);
//0 (even though length isn't 0 as shown above)
console.log(rec[0].toObject().posts.length);
//false (so this should be the test case)
console.log(rec[0].posts.length===rec[0].toObject().posts.length
&& rec[0].posts.length !== 0);
}) Sorry didn't have time as yet to put that into a test file... let me know if you can do that :P |
@devinivy interesting.. seems like there is a test case to make sure the bug is there :D maybe we're missing something?! If we're not missing something: assuming it('should NOT strip out the association key when no options are passed', function() {
var person = new model({ name: 'foobar' });
var obj = person.toObject();
assert(obj === Object(obj));
assert(obj.name === 'foobar');
assert(obj.bars);
assert(obj.foobars);
assert(person.bars);
assert(person.foobars);
assert(person.bars.length > 0);
assert(person.foobars.length > 0);
assert(person.bars.length === obj.bars.length);
assert(person.foobars.length === person.foobars.length);
}); |
One possible reason for removing the associations (I'm speculating) is to avoid circular references before using |
Interesting... though on a second thought I can't see how cycles can emerge given that the if statement is doing the opposite: it's returning when the join wasn't used (which means the IDs will stay mere strings) |
Hi, I'm using Waterline v0.10.19 and I'm having the same issue.
And in the browser
|
Hi @m0g ! Here is a quick and dirty code snippet that keeps only the IDs if that's what you're after, can you try that? var Model = Question;
//populates those of N cardinality on any/both side(s)
var populateManyAssociations = function ( query, associations) {
var query = query;
for (var i = associations.length - 1; i >= 0; i--) {
var assoc = associations[i];
//if cardinality isn't many skip
if (assoc.type !== "collection") continue;
query = query.populate(assoc.alias);
};
return query;
}
var query = Model.findOne({ id: req.params.id });
query = populateManyAssociations(query, Model.associations);
query.exec(function(err, r) {
var robj = r.toObject();
function getId (obj){
if (obj) return obj.id;
}
//now manually keep IDs
for (var i = Model.associations.length - 1; i >= 0; i--) {
var assoc = Model.associations[i];
//if cardinality isn't many skip
if (assoc.type !== "collection") continue;
robj[assoc.alias] = robj[assoc.alias].map(getId);
};
res.json(robj);
}); |
Hey @anasyb, thanks for you help. Let me elaborate a bit more. What I want to do is to convert If I don't convert And if I do convert Here is an example:
Server side output
Browser output
Thanks |
I'm wondering if this can be caused by #891 (Model#toJSON returns an object with a toJSON method)? |
So I figured out an ugly workaround to my issue. If you guys are interested. Here it is
|
@m0g sorry you had to go as far as that... this is indeed caused by #891... in particular please read this comment https://github.com/balderdashy/waterline/issues/891#issuecomment-88361748 All you need is to stop waterline from being involved in your object. You can do so simply by:
|
@m0g, @dantswain, @pixelfreak, #969 has been merged to master, can you check if it fixes your issues? |
I'm on Sails 0.10. When I do JSON.stringify(someModel), some properties, specifically properties associated with other model is not outputted. How do I get it enumerated?
Thanks!
The text was updated successfully, but these errors were encountered: