Adapter that converges two API sources into one model - ember.js

Let's say I have a JSON API where I can access two models: cats and dogs. However, on my Ember application I only have one model: animals.
Although every time I save() an animal, it's POSTed to the API as a cat, if I receive a dog linked in any other model it should be locally stored as an animal by ember-data.
What would be the most coherent way to achieve this? Thank you!

Fixed. For future reference, it is possible to create an alias to a model, extending the ApplicationSerializer (in this case, our model is animal, and although it had an adapter that used the cat model in the API, the dog model needed to be parsed as an animal as well):
App.ApplicationSerializer = DS.ActiveModelSerializer.extend({
typeForRoot: function(root) {
if (root == 'dog' || root == 'dogs') { root = 'animal'; }
return this._super(root);
}
);

You should be able to achieve what you want by customizing the REST adapter. Specifically, look into overriding the buildURL method to detect the type of the record passed and to construct the URL to be passed based on logic that determines whether this particular model should be persisted to the cats endpoint or dogs endpoint.

Related

How to retrieve the same model from another REST url in EmberJS

The models tree of my emberJS app match the tree of my API however, I have 2 different routes returning the same type of data :
/products/ and /users/:id/supported_products/ both return products data.
When I need to have the products of the app there is no problem :
this.store.query('product',params);
However I am not sure how to query products from the user path. The place to do so would be the adapter, but I need to define a secondary adapter that I would call when I need supported products,and I have no idea how to do so.
I think if it were me I would create a virtual query parameter that would instruct a custom adapter on how to change the endpoint on the fly.
For example I might have a supportedByUser flag. Then in my app/adapters/product.js do something like this:
import JSONAPIAdapter from 'ember-data/adapters/json-api';
export default JSONAPIAdapter.extend({
urlForQuery(query, modelName) {
let userId = query.supportedByUser;
delete query.supportedByUser;
return userId
? `${this.namespace || ''}/users/${userId}/supported_products`
: this._super(...arguments);
}
});
Here is an example twiddle demoing this: https://ember-twiddle.com/b406391e98ed4fda30bc227a894fa7c9

Django creating model instances - Validation before creation? In manager class? What is manager class for?

Say I have a class "Book" and I want to hit an API to verify the book exists before creating my model.
Do I create my "BookManager" class, override create, hit the api, and throw an exception if not valid or create if valid?
Then in Book I'd write objects = BookManager()
And create a book with.
new_book = Book.objects.create(name)?
Basically, this feels like a good way to organize my code, but I'm not sure if this is intended use for the Manager class as opposed to only modifying the queryset.
Additionally, does anyone have a good reference on how to structure your django rest framework app? Folder structure etc
I will start with very basics. I am assuming you api calls are simple get requests for now which you can achieve with python http package.
(I am assuming the api is a third party api for now)
you can define a simple view let say view name be : bookM
Next you have defined you model with lets say a primary key, book_name, other_attrs, date
now when you hit you api within this view you can get the response from get request
requests.get(url = URL, params = PARAMS)
With this if you find the response sent back with some text or null you can act on model as below :
book= BookSave(
name = "book1",
)
book.save()
If this is not the case you can save error message in python variable and display while rendering html
You can use this view as api and do ajax call from web page, as well in this case you can just return back messages

Find the class name of a relation from the instance of a model in ember JS

I have foo an instance of the ember-data model thing. thing.js has the following property :
owner: DS.belongsTo('user')
If I have foo with an empty owner, how can I, with only foo and the 'owner' string, retrieve the value 'user' representing the model of the owner relation?
EDIT: I want to allow my select-relation component to works with relations where the name is different from the class name
It sounds like you have some work to do to finish setting up your relationships. Have a read through this page of the guides.
If the relationships are set up correctly, to get the associated user, you should be able to do foo.owner. This assumes that users are already present in the store. I recommend using the Ember Inspector browser plugin to debug the relationships.
This looks like a use case for typeForRelationship.
In your example you should be able to do something like
store.modelFor('thing').typeForRelationship('owner', store);
If you don't like that approach you can use the belongsTo reference API, where you use the meta data from the relationship to get the type
foo.belongsTo('owner').type
The only thing with that approach is that the type property may not be public API and possible (though unlikely) to change at some point.
It seems I can do the following :
this.get('model').get('_internalModel._relationships.initializedRelationships.'+this.get('relation')+'.relationshipMeta.type')
model being an instance and relation the string of the relation name, it correctly return the model of the relation.
EDIT : a better solution not using private API courtesy from the ember discord :
function getRelatedModelName(record, relationName){
let ParentModelClass = record.constructor;
let meta = get(ParentModelClass, 'relationshipsByName').get(relationName);
return meta.type;
}

adding models to backbone collections

I have a rails app with 4 Model classes, with multiple instances in each table. I have created backbone Model and Collection classes in CoffeeScript to match. I can successfully load all of the collections and can render them in views. So far, so good. Here is one of my collections, with its associated model:
class window.CoffeeWater.Collections.Histories extends Backbone.Collection
url: '/api/histories'
model: History
class window.CoffeeWater.Models.History extends Backbone.Model
I need to be able to create a History model object, and then add it to the Histories collection. The documentation states that I have to set a 'collection' property when creating my new model, in order for it to get the 'url' property from the collection. My problem is that I can't seem to set the 'collection' property value correctly, because the url property does not get set on the model instance
attributes = {'start_time': new Date (1434740259016), 'stop_time': new Date (1434740259016 +(86400*1000)), 'valve_id': 2}
options = { collection: window.CoffeeWater.Collections.Histories }
history = new window.CoffeeWater.Models.History(attributes, options)
window.CoffeeWater.Objects.Collections.Histories.add(history)
Inspecting the resulting 'history' object does not show the same attributes that exist in models already in the collection, and the url property is missing.
I am currently at a loss. Does anyone have an example on how to do this? The backbone.js docs do not show any relevant examples.
The way the url in model is defined like this.
If there is a url property in model.
class window.CoffeeWater.Models.History extends Backbone.Model
url:'/api/history/1'
Then when model.fetch(), it will call that url. If this property is not found, it will see if the associated collection does have a 'url'. You try to set this collection variable.
What you are missing is this. This
class window.CoffeeWater.Collections.Histories extends Backbone.Collection
is actually a definition of a class. It is not an object yet. You need to initialize it, like you do in java. so
var historyCollection=new window.CoffeeWater.Collections.Histories()
Now you have a collection object. When you do
historyCollection.add(history);
The "collection" of model is automatically set to the "historyCollection" object, which contains a 'url'. So in fact, you do not need to manually put this in the option.
basically
attributes = {'start_time': new Date (1434740259016), 'stop_time': new Date (1434740259016 +(86400*1000)), 'valve_id': 2}
var historyCollection=new window.CoffeeWater.Collections.Histories()
var history = new window.CoffeeWater.Models.History(attributes)
historyCollection.add(history)

Ember data: interpolated pluralization of model name

Say I have a DS.Store model called "userPreferences". Say I have a backend API returning a SINGLE "userPreferences" record:
{"userPreferences":{"userID":"7","latitude":null,"longitude":null}}
Say I have a route that I want to use this model with:
App.SettingsRoute = Ember.Route.extend({
model: function() {
myModel = this.store.find('userPreferences', 7);
return myModel;
}
});
If I try to do this, I get the following error from ember:
Error: No model was found for 'userPreference'
How do I specify that I'm already returning the singular form?
If you don't ever plan to refer to a collection of userPreferences, as Steve H. suggested you might need to, you can define the phrase as uncountable:
Ember.Inflector.inflector.uncountable('userPreferences');
Just be careful, you might need to also define:
Ember.Inflector.inflector.uncountable('user_preferences');
Because at present the inflector doesn't assume that you want both defined. The underscore version is sometimes used by ember-data when referring to model properties during serialization. I always define both camelCase and underscore versions.
I've opened an issue about this on ember-inflector.
What do you plan to call a collection of userPreferences? Here, I'm calling them "manyUserPreferences", but I'd suggest something better :) You can customize the way Ember understands the model names using an Inflector:
Ember.Inflector.inflector.irregular('userPreferences', 'manyUserPreferences');