So I've been struggling for a few hours now with a one-to-many mapping update.
I've got a project which has certain tasks (for example).
I add and remove tasks through the frontend and send the revised object to by backend running with sequelize.
Then I tried to update the records as follows:
return models.Project
.findOne({
where: { id: projectToUpdate.id },
include: [models.Task]
})
.then(function (ProjectFromDb) {
return models.sequelize
.transaction({
isolationLevel: models.sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED
},
function (t) {
return ProjectFromDb
.update(projectToUpdate,
{
include: [{ model: models.Task }]
})
});
})
.then(function (result) {
return output.getSuccessResult(....
})
.catch(function (error) {
return output.getErrorResult(....
});
But this would only update the Project
Next I tried to update them with an additional then call:
.then(function (updateResult) {
return updateResult.setTasks(projectToUpdate.Tasks, {transaction: t})
})
But this would give me the result that he is trying to update the Task and set the ProjectId to NULL which is not possible because it is non-nullable.
I am currently "manually" adding the tasks and removing them but this seems to be a silly way of using the framework.
Can anyone tell me how to properly make this work with a one-to-many relationship without me calling Tasks.createBulk and Tasks.destroy?
EDIT TO INCLUDE MODEL
JSON object looks like this:
{
id: 1,
projectName: 'nameOfTheProject',
Tasks: [
projectId: 1,
name: 'taskName'
]
}
Please try changing the property name projectId to ProjectId on your Tasks objects that are nested to the projectToUpdate object.
Update
Looking at sequelize's source, it seems that the Instance.$save() function (which is called by Instance.$update() that you're using) does not support nested models creation when you're updating it - it checks if the flag wasNewRecord is true before doing it.
Related
I'm trying to create an object in one part of vuex store, and then pass id to it to another object, and i'm not sure how to properly do that since mutations can't return returning anything (in this case, id).
Two store objects look like this:
// store/report.js
const state = {
name: 'Untitled Report',
subReportIds: []
};
// store/subReport.js
const state = { ... }
And i'd like this action to create blank report, then blank subreport, and then assign subreport id to newly created report. (subreports are independent entities, and can be used by multiple reports, hence different area in store)
const actions = {
createNewReport({ state, commit }) {
commit(mutationTypes.CREATE_NEW_REPORT)
// below doesn't work - i can't get return from mutation
let newSubreportId = commit(mutationTypes.ADD_NEW_SUBREPORT)
// if this worked, i'd then do something like
commit(mutationTypes.ADD_SUBREPORT_TO_REPORT, newSubreportId)
}
};
How can i achieve the above?
So best way to accomplish to me would be to dispatch actions instead of committing the mutations. If you look at the methods in Vuex source, commit only executes with no return (so is a void) and dispatch returns the value you return from the action (which is a function)
For my actions, i always return a promise so that i can compose them like you mention above. Here is an example.
fetchSomething ({ commit }) {
return mockApiGetIds()
.then(response => {
commit({
type: SOME_MUTATION,
ids: response
});
return response;
});
},
Disclaimer : I don't know if it is truely a good idea, but at least, it seems to work, and to me, it feels prettier than having to use actions and promises, or to generate the id in the action...
With your mutation, you can pass an argument. To return a value from a mutation (like a newly created id), I write it to a placeholder in that argument :
someMutation(state, arg){
//...
arg.out = {
status : "succeed"
}
}
//...
this.$store.commit('someMutation', arg);
if(arg.out !== "succeed") console.log("ERROR");
I have a (simplified) query that looks as follows.
var pageViews = new Keen.Query('count', {
eventCollection: 'Loaded a Page',
groupBy: 'company.id'
});
And use it as follows.
client.run(pageViews, function(result, error) {
// Do something here
});
This will give me the following JSON to work with:
{
"result": [
{
"company.id": 1,
"result": 3
},
{
"company.id": 2,
"result": 11
},
{
"company.id": 3,
"result": 7
}
]
}
However, I would also like to get back the name of each company, i.e. the company.name property. I looked through keen.io's documentation, and I could find no way of doing this. Is there a way to do this? Logically speaking, I don't see any reason why it would not be possible, but the question is if it has been implemented.
Grouping by multiple properties will get you what you're looking for:
var pageViews = new Keen.Query('count', {
eventCollection: 'Loaded a Page',
groupBy: ['company.id','company.name']
});
That being said, it's important to note that Keen is not an entity database. Keen is optimized to store and analyze event data, which is different than entity data. More complex uses of entity data may not perform well using this solution.
I'm trying write a conditional model schema based if exists other models.
For example, if exist model 'Message' then add a inbox/outbox:
var userSchema = {
attributes: {
...
}
}
var messageSchema = {
attributes: {
inbox: {
collection: 'Message'
},
outbox: {
collection: 'Message'
}
}
};
if (<function to check that Message Model is available)
_.merge userSchema, messageSchema
Is it possible do something like this?
I think we need one "pre model load" fase on plugins and one hook to alow others modules do alter it structure like drupal 7 scheme.
You cold exports one function to plug this "fase feature" in plugin npm loader like i did with default configs: https://github.com/wejs/we-plugin/blob/master/lib/index.js#L18 and set in https://github.com/wejs/we-example/blob/master/app.js#L63
I have a User entity that has a subscriptions property. This is an array of IDs.
When I perform a fetch, the API will populate those subscriptions, and return something like this:
{
subscriptions: [1, 2, 3],
__subscriptions: [
{
id: 1,
name: 'Example'
},
{
id: 2,
name: 'Example'
},
{
id: 3,
name: 'Example'
}
]
}
I have done this so that I can still perform actions on the original subscriptions and then save them back to the API. Any changes I make to __subscriptions will not be persisted as the API doesn't recognise this field – it is simply the populated data.
In the parse function of my User, I create the nested collection:
parse: function (response) {
this.subscriptions = new Subscriptions(response.__subscriptions)
}
However, if I want to remove a subscription, I have to splice it from the subscriptions field of the User entity, and then I also have to remove it from the subscriptions collected that is nested as a property on the User:
// Clone the subscriptions property, delete the model with a matching ID, and then set it again.
var value = _.clone(this.get('subscriptions'))
// Use splice instead of delete so that we don't leave an undefined value
// in the array
value.splice(value.indexOf(model.id), 1)
// Also remove the same model from the nested collection
var removedSubscription = this.subscriptions.get(model)
this.subscriptions.remove(removedSubscription)
this.set('subscriptions', value)
this.save()
This is sort of annoying. Ideally, removing an ID from the subscriptions property should automatically update the collection.
Does this seem like a good way to deal with nested models and collections? I've heard bad things about Backbone.Relational so I was interested in a simpler solution.
I would listen to events of Subscriptions collection and update subscriptions argument accordingly.
var User = Backbone.Model.extend({
initialize: function () {
this.subscriptions = new Subscriptions;
this.subscriptions.on('add remove', this.updateSubscriptions, this)
},
updateSubscriptions: function() {
this.set('subscriptions', this.subscriptions.pluck('id'))
},
parse: function (response) {
this.subscriptions.reset(response.__subscriptions);
return Backbone.Model.parse.call(this, response);
}
});
So then removing subscription will update subscriptions attribute of user model:
user.subscriptions.remove(subscription)
I currently have a Sproutcore app setup with the following relationships on my models:
App.Client = SC.Record.extend({
name: SC.Record.attr(String),
brands: SC.Record.toMany('App.Brand', {isMaster: YES, inverse: 'client'})
});
App.Brand = SC.Record.extend({
name: SC.Record.attr(String),
client: SC.Record.toOne('App.Client, {isMaster: NO, inverse: 'brands'})
});
When I was working with fixtures my fixture for a client looked like this:
{
guid: 1,
name: 'My client',
brands: [1, 2]
}
And my fixture for a brand looked like this:
{
guid: 1,
name: 'My brand',
client: 1
}
Which all worked fine for me getting a clients brands and getting a brands client.
My question is in regards to how Datasources then fit into this and how the server response should be formatted.
Should the data returned from the server mirror exactly the format of the fixtures file? So clients should always contain a brands property containing an array of brand ids? And vice versa.
If I have a source list view which displays Clients with brands below them grouped. How would I go about loading that data for the source view with my datasource? Should I make a call to the server to get all the Clients and then follow that up with a call to fetch all the brands?
Thanks
Mark
The json you return will mostly mirror the fixtures. I recently had pretty much the same question as you, so I built a backend in Grails and a front end in SC, just to explore the store and datasources. My models are:
Scds.Project = SC.Record.extend(
/** #scope Scds.Project.prototype */ {
primaryKey: 'id',
name: SC.Record.attr(String),
tasks: SC.Record.toMany("Scds.Task", {
isMaster: YES,
inverse: 'project'
})
});
Scds.Task = SC.Record.extend(
/** #scope Scds.Task.prototype */ {
name: SC.Record.attr(String),
project: SC.Record.toOne("Scds.Project", {
isMaster: NO
})
});
The json returned for Projects is
[{"id":1,"name":"Project 1","tasks":[1,2,3,4,5]},{"id":2,"name":"Project 2","tasks":[6,7,8]}]
and the json returned for tasks, when I select a Project, is
{"id":1,"name":"task 1"}
obviously, this is the json for 1 task only. If you look in the projects json, you see that i put a "tasks" array with ids in it -- thats how the internals know which tasks to get. so to answer your first question, you dont need the id from child to parent, you need the parent to load with all the children, so the json does not match the fixtures exactly.
Now, it gets a bit tricky. When I load the app, I do a query to get all the Projects. The store calls the fetch method on the datasource. Here is my implementation.
Scds.PROJECTS_QUERY = SC.Query.local(Scds.Project);
var projects = Scds.store.find(Scds.PROJECTS_QUERY);
...
fetch: function(store, query) {
console.log('fetch called');
if (query === Scds.PROJECTS_QUERY) {
console.log('fetch projects');
SC.Request.getUrl('scds/project/list').json().
notify(this, '_projectsLoaded', store, query).
send();
} else if (query === Scds.TASKS_QUERY) {
console.log('tasks query');
}
return YES; // return YES if you handled the query
},
_projectsLoaded: function(response, store, query) {
console.log('projects loaded....');
if (SC.ok(response)) {
var recordType = query.get('recordType'),
records = response.get('body');
store.loadRecords(recordType, records);
store.dataSourceDidFetchQuery(query);
Scds.Statechart.sendEvent('projectsLoaded')
} else {
console.log('oops...error loading projects');
// Tell the store that your server returned an error
store.dataSourceDidErrorQuery(query, response);
}
}
This will get the Projects, but not the tasks. Sproutcore knows that as soon as I access the tasks array on a Project, it needs to get them. What it does is call retrieveRecords in the datasource. That method in turn calls retrieveRecord for every id in the tasks array. My retrieveRecord method looks like
retrieveRecord: function(store, storeKey) {
var id = Scds.store.idFor(storeKey);
console.log('retrieveRecord called with [storeKey, id] [%#, %#]'.fmt(storeKey, id));
SC.Request.getUrl('scds/task/get/%#'.fmt(id)).json().
notify(this, "_didRetrieveRecord", store, storeKey).
send();
return YES;
},
_didRetrieveRecord: function(response, store, storeKey) {
if (SC.ok(response)) {
console.log('succesfully loaded task %#'.fmt(response.get('body')));
var dataHash = response.get('body');
store.dataSourceDidComplete(storeKey, dataHash);
} ...
},
Note that you should use sc-gen to generate your datasource, because it provides a fairly well flushed out stub that guides you towards the methods you need to implement. It does not provide a retrieveMethods implementation, but you can provide your own if you don't want to do a single request for each child record you are loading.
Note that you always have options. If I wanted to, I could have created a Tasks query and loaded all the tasks data up front, that way I wouldn't need to go to my server when I clicked a project. So in answer to your second question, it depends. You can either load the brands when you click on the client, or you can load all the data up front, which is probably a good idea if there isn't that much data.