I'm using the latest version of EmberJS and Ember Data.
I have the next JSON data:
[{
"id": 6,
"name": "First object",
"vol": 40,
"description": "bla bla bla",
"category": "first"
}, {
"id": 7,
"name": "Second object",
"vol": 17,
"description": "Some description",
"category": "second"
}]
And the next model:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
vol: DS.attr('number'),
description: DS.attr('string'),
category: DS.attr('string')
});
And I don't understand how to make Ember data works. I have suspicious that ember data expects something like next:
[{
model-name {
"id": 6,
"name": "First object",
"vol": 40,
"description": "bla bla bla",
"category": "first"
}
}, {
model-name {
"id": 7,
"name": "Second object",
"vol": 17,
"description": "Some description",
"category": "second"
}
}]
In console I have a bunch of warnings:
WARNING: Encountered "0" in payload, but no model was found for model
name "0" (resolved model name using emdber-drink-
it#serializer:application:.modelNameFromPayloadKey("0"))
WARNING: Encountered "1" in payload, but no model was found for model
name "1" (resolved model name using emdber-drink-
it#serializer:application:.modelNameFromPayloadKey("1"))
And more than hundred similar records (that is how many records server returns on request).
And no data in store.
How can I fix this problem without changing JSON that I receive from server?
From http://emberjs.com/api/data/classes/DS.JSONSerializer.html
In Ember Data a Serializer is used to serialize and deserialize
records when they are transferred in and out of an external source.
This process involves normalizing property names, transforming
attribute values and serializing relationships.
At this time there are 4 types of serializers:
DS.Serializer
DS.JSONSerializer
DS.RESTSerializer
DS.JSONAPISerializer
The JSON data you have is plain json.
JSONSerializer is useful for simpler or legacy backends that may not
support the http://jsonapi.org/ spec
So you need JSONSerializer which is the most simple solution to consume plain json data.
With ember-cli is pretty easy create a new serializer, for example for a model book:
ember generate serializer book
which will produce something like this:
version: 1.13.8
installing serializer
create app/serializers/book.js
installing serializer-test
create tests/unit/serializers/book-test.js
The previous command will create a serializer of type RESTSerializer. (If you dont use ember -cli just create this file at hand)
// app/serializers/book.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
});
and then change RESTSerializer by JSONSerializer:
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
primaryKey: '_id', // useful for mongodb-like data
});
I hope you enjoy learning ember as much as I do.
You should be able to add a custom serializer to add a root key to your JSON data. I haven't tested this but you could try something like:
(using a "book" model as an example)
// app/serializers/book.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
extractArray: function(store, type, payload) {
var newPayload = {};
newPayload[type.modelName] = payload;
return this._super(store, type, newPayload);
}
});
If this is a problem with the entire API, then you might need to make a custom serializer for the entire application and use the passed-in type argument to build the root keys.
Also as a side note, I probably wouldn't use Ember Data if I didn't have control over the API. Unless it follows strict guidelines like jsonapi.org, you're bound to have a few headaches with it.
EDIT - changed answer to the suggestion above (using 'type' to build root key).
I took another approach and overloaded the 'modelNameFromPayloadKey' method.
If the payload key is of type integer, it will be mapped to the name of the model instead.
// app/serializers/<model>.js
import DS from 'ember-data';
import normalizeModelName from "ember-data/-private/system/normalize-model-name";
import {singularize} from "ember-inflector";
export default DS.RESTSerializer.extend({
model: '<model>',
modelNameFromPayloadKey(key) {
// Check whether the key is of type integer
if (!isNaN(key)) {
return normalizeModelName(this.get('model'));
}
return singularize(normalizeModelName(key));
},
});
Related
My Backend Rails API consumes POST requests in the form of
{
"name": "Mark White",
"email" : "mark#xyz.com"
}
Have tried this in Postman and this creates a record successfully.
In my ember frontend, Im using the following code to create a new record, from a form input:
app/routes/addUser.js
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
addUser(){
let formData = {
"name": this.controller.get('name'),
"email": this.controller.get('email')
};
let newUser = this.store.createRecord('user',formData);
newUser.save();
}
}
});
which generates the request as
{
"user": {
"name": "Mark White",
"email" : "mark#xyz.com"
}
}
which my backend is not prepared to handle and throws an error.
Any way I can quickly conform to the json format that works for me in Postman.
When the question is along the lines with the "How can I change my payload to server for it to be in a certain format?" then you should have a look at customizing a serializer.
But it looks like for solving your problem you might wanna take the standard JSON Serializer; in your app/serializers/application.js just type the following:
import JSONSerializer from '#ember-data/serializer/json';
export default class ApplicationSerializer extends JSONSerializer {
// ...
}
Note that it's for the Ember.js v4, according to your example you're using a bit older version so just adjust the syntax accordingly.
I am building python backend for SPA (Angular) using MongoDB.
Here is what I use: Python 3.4, MongoDB 3, Flask, flask-mongoengine and flask-restful
Now I receive the following JSON from my backend:
[
{
"_id": {
"$oid": "55c737029380f82fbf52eec3"
},
"created_at": {
"$date": 1439129906376
},
"desc": "Description.....",
"title": "This is title"
},
etc...
]
And I want to receive something like that:
[
{
"_id": "55c737029380f82fbf52eec3",
"created_at": 1439129906376,
"desc": "Description.....",
"title": "This is title"
},
etc...
]
My code for now:
from flask import json
from vinnie import app
from flask_restful import Resource, Api
from vinnie.models.movie import Movie
api = Api(app)
class Movies(Resource):
def get(self):
movies = json.loads(Movie.objects().all().to_json())
return movies
api.add_resource(Movies, '/movies')
Model:
import datetime
from vinnie import db
class Movie(db.Document):
created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
title = db.StringField(max_length=255, required=True)
desc = db.StringField(required=True)
def __unicode__(self):
return self.title
What is the best way to format convenient JSON for front-end?
If you are confident you want to get rid of all the similar cases, then you can certainly write code that matches that pattern. For example:
info = [
{
"_id": {
"$oid": "55c737029380f82fbf52eec3"
},
"created_at": {
"$date": 1439129906376
},
"desc": "Description.....",
"title": "This is title"
},
#etc...
]
def fix_array(info):
''' Change out dict items in the following case:
- dict value is another dict
- the sub-dictionary only has one entry
- the key in the subdictionary starts with '$'
In this specific case, one level of indirection
is removed, and the dict value is replaced with
the sub-dict value.
'''
for item in info:
for key, value in item.items():
if not isinstance(value, dict) or len(value) != 1:
continue
(subkey, subvalue), = value.items()
if not subkey.startswith('$'):
continue
item[key] = subvalue
fix_array(info)
print(info)
This will return this:
[{'title': 'This is title', 'created_at': 1439129906376, 'desc': 'Description.....', '_id': '55c737029380f82fbf52eec3'}]
Obviously, reformatting that with JSON is trivial.
I found a neat solution to my problem in flask-restful extension which I use.
It provides fields module.
Flask-RESTful provides an easy way to control what data you actually render in your response. With the fields module, you can use whatever objects (ORM models/custom classes/etc.) you want in your resource. fields also lets you format and filter the response so you don’t have to worry about exposing internal data structures.
It’s also very clear when looking at your code what data will be rendered and how it will be formatted.
Example:
from flask_restful import Resource, fields, marshal_with
resource_fields = {
'name': fields.String,
'address': fields.String,
'date_updated': fields.DateTime(dt_format='rfc822'),
}
class Todo(Resource):
#marshal_with(resource_fields, envelope='resource')
def get(self, **kwargs):
return db_get_todo() # Some function that queries the db
Flask-RESTful Output Fields Documentation
The topic of this post is: my solution is too slow for a large query return.
I have a Web Api serving REST results like below from a call to localhost:9090/api/invetories?id=1:
[
{
"inventory_id": "1",
"film_id": "1",
"store_id": "1",
"last_update": "2/15/2006 5:09:17 AM"
},
{
"inventory_id": "2",
"film_id": "1",
"store_id": "1",
"last_update": "2/15/2006 5:09:17 AM"
}
]
Since my WebAPI did not provide a root key for my JSON response, I made a RESTSerializer like following.
export default DS.RESTSerializer.extend({
extract:function(store,primaryType,payload,id,requestType){
var typeName = primaryType.typeKey;
var data = {};
data[typeName] = payload; // creating root
payload = data;
return this._super(store,primaryType,payload,id,requestType)
}
});
When this gets run, I get the following error message: Assetion failed: You must include an 'id' for inventory in an object passed to 'push'
As you can see, these objects do not have the attribute id, so I found that the default behaviour of Ember RESTSerializer forces me to write my own.
Okay, so here's where I'm not sure my solution is right. inventory_id from my return is unique, therefore I choose to use that as an id, okay I'm thinking to my self, I'll just add it manually. The function looks like this now:
export default DS.RESTSerializer.extend({
extract:function(store,primaryType,payload,id,requestType){
var typeName = primaryType.typeKey;
for(var i=0;i<payload.length;i++){
payload[i].id = payload[i].inventoryId;
}
var data = {};
data[typeName] = payload; // creating root
payload = data;
return this._super(store,primaryType,payload,id,requestType)
}
});
By just manually duplicating an attribute, I feel like I'm cheating my way over this error message. In addition, I sometimes return a large payload array (over 150k rows). Looping O(n) just doesn't seem a right price to pay for just a simple mapping.
Is there some other way to set either my WebAPI or serializer up so I avoid the for loop in assigning the id that ember so desperately wants.
I think this should fix your problem:
export default DS.RESTSerializer.extend({
primaryKey: 'inventory_id'
});
With this parameter Ember Data will map inventory_id to it's id parameter.
DEBUG: Ember : 1.8.1
ember.js:15373DEBUG: Ember Data : 1.0.0-beta.18
ember.js:15373DEBUG: Handlebars : 1.3.0
ember.js:15373DEBUG: jQuery : 1.11.3
So I have a "Request" model which can depend on an account or other Requests that stand in for and account when it is fulfilled.
I'm a bit confused about how to get the JSON right for Ember to pick this up.
On the server-side, ActiveModelSeriailzers seems to want to serialize polymorphic relationships like this:
{
"id" : 1,
"account_id" : { "id": 1, "type": "account"}
}
I have the following ED Models:
// abstract-account.js
export default DS.Model.extend();
//account.js
export default AbstractAccount.extend();
//request.js
export default DS.Model.extend({
account: DS.belongsTo('abstractAccount', { polymorphic: true} )
});
When I try to load the record using the default json, I get
Error: Assertion Failed: You must include an `id` for account in an object passed to `push`
In my reading, I had gotten the impression that belongsTo polymorphic assocaitions should get sent like:
{
"id" : 1,
"account_id" : 1,
"account_type" : "Account"
}
If I change the server to serialize like that, I instead get
Uncaught Error: Assertion Failed: You may not pass `null` as id to the store's find method
I'm not really sure what I'm missing,or what the current recommended practice is.
I am using a similar setup with the first convention. Keep in mind Ember expects JSON objects to be namespaced under the class name, so for a GET to /requests you would need to return something like
{
'requests': [
{
'id': 1,
'account': { id: 10, type: 'account' }
},
{
'id': 2,
'account': { id: 20, type: 'some_other_abstract_account' }
}
]
}
This is with the RESTAdapter declared in application/adapter.js, I haven't tried the ActiveModelAdapter. I have seen documents advising another convention, but it's working for me with ember data 1.0.0-beta.16
I'm working on a project using Ember/Ember-Data and there is a related/already existing service which is provide API with JSON response.
My project must interact with that service, but the response from that API is someting like below:
{ "id": 39402, "name": "My Name" }
or
[ {"id": 38492, "name": "Other Name" } ]
there is no person: or persons: that is required by Ember-Data compatable response.
How can I using this response on Ember-Data without change on the service or without build API gateway?
Ember-Data uses DS.RestAdapter, which in turn uses DS.RESTSerializer which extends from DS.JSONSerializer for serializing, extracting and massaging data that comes in from the server.
Since in your case you already have the data in your payload, all you need to do for reading the data is override extract method in the JSONSerializer which is actually quite simple.
If you are using ember-cli (which you should :)), your person.js file located inside your app/serializers directory would look as follows.
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
extract: function(store, primaryType, payload) {
return payload;
}
});
If you are not using ember-cli, you can do the following:
App.PersonSerializer = DS.JSONSerializer.extend({
extract: function(store, primaryType, payload) {
return payload;
}
});