Embedded Records in ember-data


#1

Hi,

I’m having the following model on the server side:

class User < ActiveRecord::Base
  belongs_to :company
  accepts_nested_attributes_for :company
end
class Company < ActiveRecord::Base
  has_many :users
end

Now, on my registration action (POST /users) I want to create a new user and a company in that one request (and thus in one transaction on the server side). My understanding is that in order for that to work I have to define company as embedded with

DS.RESTAdapter.map('App.User', {
  company: { embedded: 'always' }
});

However, to me it looks like that I would then have to also embed the company in the user JSON in order for it to be loaded - I don’t really want to do that though as a) I’d like to follow http://jsonapi.org and b) already experienced lots of problems with entities being embedded in each other in JSON. I actually still want my JSON to look like this:

{
  user: {
    name: 'some'
    company_id: 1
  },
  companies: [
    { id: 1, name: 'some' }
  ]
}

I tried to so this:

{
  user: {
    name: 'some'
    company: { id: 1 }
  },
  companies: [
    { id: 1, name: 'some' }
  ]
}

but it didn’t load the company in the client.


#2

maybe you need to do

companys : [ {id : 1, name : 'some'} ]


#3

Companys

What a word is that?

You can tell Ember.js what plural to use by:

DS.RESTAdapter.configure('plurals', {
  company: 'companies'
});

#4

Yes, we have

DS.RESTAdapter.configure('plurals', {
  company: 'companies'
});

in our code.

Alos I figured out that my initial problem was caused by the fact that I specified embedded: 'always' where it has to be embedded: 'load' to work for posts (no idea why that is but that actually solved it).


#5

I think there might be a conceptual problem with embedded/related record creation though: In the above example, when I want to create an account and a company in one request, I (think I) have to set them up like this:

return App.Account.createRecord({ company: App.Company.createRecord() });

The problem is that because of the 2 calls to createRecord, both records will be added to the current transaction and will be submitted to the backend store (resulting in 2 POST requests where one fails because we only allow creation of companies embedded in the account).

Is there sth. I got wrong or is this simply not supported (yet)? I’d be happy to try to add that and submit a pull request but would probably need some advice on the topic.


#6

@marcoow Did you ever find a solution to this? I am running into the same problem and can’t haven’t found anything workable.


#7

I submitted a pull request but that war rejected. However, when we updated to the ember-data 1.0 beta we were able to solve the problem easily with custom serializers/adapters:

App.AccountSerializer = DS.RESTSerializer.extend({
  serialize: function(account, options) {
    var json     = this._super.apply(this, arguments);
    json.company = account.get('store').serialize(account.get('company'), {});
    return json;
  },
  extractSingle: function(store, type, payload, id, requestType) {
    payload.company         = payload.account.company;
    payload.account.company = payload.account.company.id;

    return this._super.apply(this, arguments);
  }
});

App.Account = DS.Model.extend({
  login:     DS.attr('string'),
  firstName: DS.attr('string'),
  lastName:  DS.attr('string'),
  company:   DS.belongsTo('company')
});

and

App.CompanyAdapter = DS.RESTAdapter.extend({
  createRecord: function(store, type, record) {
    new Ember.RSVP.Promise(function(resolve, reject){
      reject('Company is embedded in Account and cannot be created directly');
    });
  },
  updateRecord: function(store, type, record) {
    new Ember.RSVP.Promise(function(resolve, reject){
      reject('Company is embedded in Account and cannot be updated directly');
    });
  },
  deleteRecord: function(store, type, record) {
    new Ember.RSVP.Promise(function(resolve, reject){
      reject('Company is embedded in Account and cannot be deleted directly');
    });
  }
});

App.Company = DS.Model.extend({
  name: DS.attr('string')
});

#8

@marcoow have you tried the DS.ActiveModelAdapter and DS.ActiveModelSerializer at all?


#9

No, those have only been added after we updated ember-data the last time


#10

@marcoow thanks for those examples. Since we are not on ember-data 1.0 yet I took a look at your old PR and that seems to do prevent the 2 post requests from being made.


#11

I’ve had a similar problem since adopting ember for a side project.

I have a form where a user can set up an Event and that Event can have one or more EventDates. They’re hooked up correctly via hasMany/belongsTo, but when I go to save the Event, it doesn’t post the EventDate submodels along with the request. So when the JSON comes back, without any dates, it effectively removes the EventDates from the Event. Still unsure of the best way to proceed, and I can’t seem to find any documentation about the embedded flag.