Thanks @terzicigor for continuing the discussion! I’m sure we can figure out how to deal with this use-case.
##TLDR
- Why does
store.pushPayload accept a type parameter but does not pass it to the serializer’s pushPayload?
- if the serializer’s
pushPayload shouldn’t know the type, why do all of the other methods that need to know about the type in the serializer (source) get passed the type?
- if
store.pushPayload should only accept generic payloads, why does it have a type parameter at all?
- The key difference from JSON API or the ActiveModel JSON serializer is that the JSON data format for Django REST Framework does not include the type as a key in the returned data, meaning the type can’t be determined solely from raw data
How store.pushPayload currently works
The method store.pushPayload (source) accepts a type and payload argument. It uses the type to determine which serializer to use for that type, and calls the appropriate serializer’s pushPayload (source) with the parameters store and payload. The serializer’s pushPayload (at least in the case of the RESTSerializer) normalizes the payload and then pushes it into the store. The main difference between store.pushPayload and store.push (source) is that store.pushPayload defers the actual work to the type’s serializer’s version of pushPayload, where store.push does not use the serializer at all.
The serializer’s pushPayload only functions correctly because it assumes the payload argument contains enough information to determine the payload’s type. This is the case for JSON API and the ActiveModel JSON serializer, but is not the case for other popular REST APIs such as Django REST Framework.
Couple of questions:
- How would a new
store.pushAndNormalize differ from store.pushPayload? It seems like the only different might be passing the type argument to the serializer’s pushPayload.
- You mention the adapter’s
pushPayload method being called, but adapter’s don’t have that method. Do you mean the serializer’s pushPayload method? In that case, shouldn’t the serializer be able to know the type?
##Issues when creating a custom adapter/serializer
The problem I encounter when working on ember-data-django-rest-adapter is when I need to manually push data into the store, through some side-channel such as during application boot. The JSON format from Django REST Framework would be in a form like this (note that there is no high level type key, or any other indication of what the model should be):
// data returned from /users/1/
{
"id": 1,
"first_name": "Tom",
"last_name": "Dale",
"username": "tdale",
"email": "example@example.com"
}
Manually adding this data to the store would ideally be accomplished by something such as:
var currentUserObj = { /* Tom Dale's user info here */ };
var type = 'user';
store.pushPayload(type, currentUserObj);
But it will not work! store.pushPayload uses the passed in type to figure out which serializer to use, but it will not pass the type to the serializer’s version of pushPayload, and as you can see from the JSON format, if the type is not sent to the serializer’s pushPayload the serializer will have no way to know which type to use.
This makes it impossible to write a custom serializer pushPayload method that gets called from store.pushPayload when the data format doesn’t include the type. The only way to manually push records into the store is to actually bypass the store and reach directly into the serializer to call a push method:
var currentUserObj = { /* ... */ };
var type = 'user';
store.serializerFor(type).pushPayload(store, type, currentUserObj); // note that our JSON format requires pushPayload to know the type
This doesn’t feel like it should be the way to gain this functionality, especially when it seems like the store.pushPayload method is intended to exactly provide this functionality.
##The strange case of store.pushPayload accepting a type
One inconsistency I have noticed is store.pushPayload accepting a type. This is required so that the correct serializer can be used for the type that we are manually pushing into the store, but as we’ve seen above, this type information doesn’t make it into the serializer’s pushPayload implementation.
This means that you actually can’t push generic JSON into store.pushPayload. If you wanted to pre-load multiple records of many types (such as on initial application load), your application would need to manually separate them into their types and call store.pushPayload for each model type:
var bootstrappedRecords = {
"users": [
{
"id": 1,
"first_name": "Tom",
"last_name": "Dale",
"username": "tdale",
"email": "example@example.com"
},
{
"id": 2,
"first_name": "Yehuda",
"last_name": "Katz",
"username": "tdale",
"email": "example@example.com"
}
],
"posts": [
{
"id": 100,
"content": "...",
"author": 1
},
{
"id": 101,
"content": "...",
"author": 2
}
]
};
store.pushPayload('user', bootstrappedRecords.users);
store.pushPayload('posts', bootstrappedRecords.posts);
I know that the JSON API and the ActiveModel JSON serializer include the type as a key, but the Django REST Framework doesn’t. Maybe a future path will be to create a Django-based REST API project that conforms to the JSON API specification, but the current format of Django REST Framework seems like it should be something that Ember-Data should be able to support.