Search code examples
ember.jsember-dataember-cliembedding

How to embed model in model (nest records) using ember-data?


I am trying to setup embedded records in my project using Ember-cli. I can't get it working, I tried different configurations with {embedded: "always", etc...} but all I get is: Error: Assertion Failed: You must include an id for user in an object passed to push Please help.

I'm using

DEBUG: -------------------------------
DEBUG: Ember             : 1.12.1
DEBUG: Ember Data        : 1.13.4
DEBUG: jQuery            : 2.1.4
DEBUG: Ember Simple Auth : 0.8.0
DEBUG: -------------------------------

My JSON with QuestionDefinitions is:

{
    "data": [
        {
            "created": 1439824135440,
            "updated": 1439824135440,
            "userID": 20,
            "user": {
                "password": null,
                "created": null,
                "updated": null,
                "photoID": null,
                "photo": null,
                "email": "super@duper.com",
                "emailConfirmed": false,
                "phoneNumber": null,
                "phoneNumberConfirmed": false,
                "accessFailedCount": 0,
                "id": 20,
                "userName": "qwerty"
            },
            "addCategoriesIDs": [],
            "removeCategoriesIDs": [],
            "recommendations": [],
            "removeRecommendstionIDs": [],
            "patternAnswers": [],
            "removePatternAnswerIDs": [],
            "hint": null,
            "version": 1,
            "commonVersion": 2,
            "id": 1,
            "questionText": "Test?",
            "weight": 0,
            "answerType": 0,
            "status": 0,
            "estimatedTime": null,
            "private": false
        },
        {
            "created": 1439824143340,
            "updated": 1439824143340,
            "userID": 20,
            "user": {
                "password": null,
                "created": null,
                "updated": null,
                "photoID": null,
                "photo": null,
                "email": "super@duper.com",
                "emailConfirmed": false,
                "phoneNumber": null,
                "phoneNumberConfirmed": false,
                "accessFailedCount": 0,
                "id": 20,
                "userName": "qwerty"
            },
            "addCategoriesIDs": [],
            "removeCategoriesIDs": [],
            "recommendations": [],
            "removeRecommendstionIDs": [],
            "patternAnswers": [],
            "removePatternAnswerIDs": [],
            "hint": null,
            "version": 1,
            "commonVersion": 3,
            "id": 2,
            "questionText": "Test?",
            "weight": 0,
            "answerType": 0,
            "status": 0,
            "estimatedTime": null,
            "private": false
        }
    ]
}

QuestionDefinition model is:

//app/models/questiondefinition.js
import Ember from 'ember';
import DS from "ember-data";

export default DS.Model.extend({
    //id          : DS.attr('string'), //sie nie uzywa
    created              : DS.attr('pl-date'),
    updated              : DS.attr('pl-date'),
    userID               : DS.attr('number'),
    user                 : DS.belongsTo('user',{async: false, embedded: 'always'}),
    //hint           : DS.attr('string'),
    hint                 : null,
    version              : DS.attr('number'),
    commonVersion        : DS.attr('number'),
    questionText         : DS.attr('string'),
    weight               : DS.attr('number'),
    answerType           : 0,
    status               : 0,
    estimatedTime        : DS.attr('number'),
    "private"            : DS.attr('boolean'),
    questionDefLegalBasis: function () {
        return this.get('questionText').length % 2 > 0;
    }.property('questionText'),

    /**
     * One-to-many
     */
    patternAnswers : DS.hasMany('patternanswer'),
    recommendations: DS.hasMany('recommendation'),
    categories     : DS.hasMany('questiondefinitioncategory', {async: true}),
    comments       : DS.hasMany('questiondefinitioncomment', {async: true})
});

User model is:

//app/models/user.js
import Ember from 'ember';
import DS from "ember-data";

export default DS.Model.extend({
    "password": DS.attr('string'),
    "created": DS.attr('pl-date'),
    "updated": DS.attr('pl-date'),
    "photoID": DS.attr('number'),
    "photo": DS.attr('string'),
    "email": DS.attr('string'),
    "emailConfirmed": DS.attr('boolean'),
    "phoneNumber": DS.attr('string'),
    "phoneNumberConfirmed": DS.attr('boolean'),
    "accessFailedCount": DS.attr('number'),
    "userName": DS.attr('string'),
    /**
     * One-to-many
     */
    //questionDefinitions : DS.hasMany('questiondefinition'),
    questionDefinitionComments : DS.hasMany('questiondefinitioncomment'),
    patternAnswers : DS.hasMany('patternanswer'),
});

And last but not least, serializer:

//app/serializers/questiondefinition.js:4
import DS from "ember-data";

function removeErrorsIfEmtpy(payload) {
    if (typeof payload.errors !== 'undefined' && payload.errors.length === 0) {
        delete payload.errors;
    }
}

export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
    attrs        : {
        user: {embedded: 'always',
            serialize  : 'record',
            deserialize: 'record'
        }
        //comments: { serialize: 'ids' }
    },
    extractArray : function (store, type, payload) {
        payload.questiondefinitions = payload.data;
        delete payload.data;
        removeErrorsIfEmtpy(payload);
        //console.log(arguments);
        //return this._super(store, type, payload);
        return this._super.apply(this, arguments);
    },
    extractSingle: function (store, primaryTypeClass, payload, recordId) {
        payload.questiondefinition = payload.data;
        delete payload.data;
        removeErrorsIfEmtpy(payload);
        //return this._super(store, primaryTypeClass, payload, recordId);
        return this._super.apply(this, arguments);
    }
});

Solution

  • This enigmatic question could be without answer to the next 20m of Stack, but here is the answer. Looking at the questiondefinition serializer one sees that there is tampering with payload because server responses with objects kept in 'data' property. The enigmatic error Error: Assertion Failed: You must include an id for user in an object passed to push led me to the source of ember-data where I find out that I didn't prepared serializer for user model. So ember just wanted the user properties but all it could get it was 'data' property. So remember, always keep your serializers up to date.

    That's IT!