I'm using strapi alongside knex, however strapi doesn't create any migrations and when you edit a content type (Model) by deleting a property or renaming it, it doesn't delete the targeted property, it just creates a new one, and when you delete a model it doesn't actually delete the table on the database.
This is really annoying because you'll end up with a lot of redundancies and unused tables and fields while developing.
What I'm doing is overriding the Strapi controllers that are responsible for creating, removing or updating a model and I want to be able to create a migration with the new schema on those controllers and then run it so I keep my database clean and up-to-date with strapi's models.
I know about the existence of knex.migrate.make however it doesn't seem to address my issue and the docs don't really tell you how to provide the migration fields to that function.
I was able to also find this http://knexjs.org/#custom-migration-sources however once again the docs are not very clear on what options I can provide and how to solve my issue.
Ideally I would want to end up with something like:
updateModel({attributes, table}){
const updatedSchena = attributes
knex.migrate.make(`update_${table.name}_table`, updatedSchema);
}
Edit:
I was able to create the new migration at run time but the contents of the file remain empty.
This is what I have so far
New custom-migration-sources as indicated in the docs:
class MyMigrationSource {
// Must return a Promise containing a list of migrations.
// Migrations can be whatever you want, they will be passed as
// arguments to getMigrationName and getMigration
constructor(name, attributes) {
this.name = name
this.attributes = attributes
console.log('attributes: ', attributes)
}
getMigrations() {
// In this example we are just returning migration names
return Promise.resolve(['migration1'])
}
getMigrationName(migration) {
return migration;
}
getMigration() {
return {
up(knex) {
//const migrate = require('widget-knex-schema')
return migrate.createTable(knex, this.name, this.attributes)
},
down(knex) {
return knex.schema.dropTableIfExists(this.name)
}
}
}
}
Instantiate the new migration source
const migration = new MyMigrationSource(modelJSON.collectionName, formatedAttributes)
Create the new migration with the custom configuration:
knex.migrate.make(`update_${name}_table`, {
migrationSource: migration
})
And the result is a new migration with these contents:
exports.up = function(knex) {
};
exports.down = function(knex) {
};
NOTE: I know that ´widget-knex-schema´is working because I use for other manually created migrations.
Can anyone link to any working examples or solutions on how to create migrations in run time?
Related
I'm trying to load includes on an existing model in sequelize. In express we pre check the models to see if they exist in the middleware.
So once we're in the actual "controller" we want to run some includes on that existing model that is passed in.
req.models.item.incude([
{model: Post, as: 'posts'}
])
Is there any way to accomplish this?
EDIT:
I know we can do something like this.
return req.models.item.getThing()
.then(function (thing) {
req.models.item.thing = thing;
return req.models.item;
});
But:
My expansions for includes are a dynamic property that come via url parameters, so they are not know ahead of time.
It I return the above you will not see the "thing" in the response. I need it nicely built as part of the original instance.
Something like a .with('thing', 'other.thing'); notation would be nice. Or in the case of sequelize .with({include: ...}); or .include([{model: ...}]);
If the variable req.models.item is already an Instance but without its other related instances ("includes"), then you could include them using something like the following code:
Item.findAll({
where: req.models.item.where(),
include: [{
model: SomeAssociateModel,
}]
})
.then(function(itemWithAssoc) {
// itemWithAssoc is an Instance for the same DB record as item, but with its associations
});
See here for some documentation. See here for a script demo'ing this.
Update: Given the instance, how do I just get the associated models?
To do this just use the automatically generated "getAssociation" getter functions, e.g.:
function find_associations_of_instance(instance) {
return instance.getDetails();
}
I've updated the script to include this as an example. For more information on these functions, see the SequelizeJS docs.
What I'm trying to do is integrate ember-cli-mirage into this todo app, https://github.com/ember-cli/ember-cli-todos. This app uses Ember 2.0 or greater. The setup:
Clone the todo app, then cd into the app directory.
shell> npm install
shell> bower install
shell> ember serve
I verified that the app is working as advertised (except for one minor thing that is not relevant here). I can create, update and delete a todo item. The todo app uses ember-data-fixture-adapter/FIXTURES to seed the app with data.
The steps I did to integrate ember-cli-mirage are:
Comment out the Todo.reopenClass block in app/models/todo.js (the code that creates the FIXTURES/seed data).
Removed the app/adapters directory (which only contains one file, application.js (which only contains one line, "export { default } from 'ember-data-fixture-adapter';")). I'm fairly certain this whole directory is only of use to the FIXTURES setup.
shell> ember install ember-cli-mirage
Setup the ember-cli-mirage parts (app/mirage/{config.js,factories/post.js,scenarios/default.js}) per instructions here http://www.ember-cli-mirage.com/docs/v0.1.x/working-with-json-api/.
I'll post the code for app/mirage/{config.js,factories/post.js,scenarios/default.js} if anybody needs to see it but it's basically just a copy of the instructions in the ember-cli-mirage page (with the "user" model name replaced with "post").
I restarted the ember server. Everything works fine except for the removal of a todo item. Record removal is done by pressing the "x" button that will appear when you move your pointer to the right of a todo item. I found out that it fails in the save() part of the record removal. The code for the record removal (using the "x" button interface) resides in app/components/todo-item/component.js and it looks like this:
removeTodo() {
var todo = this.get('todo');
todo.deleteRecord();
todo.save();
}
When I try to delete a todo item, the browser console prints "Successful request: DELETE /todos/n" ("n" being the todo id), then it prints a cryptic error message along with the stack trace.
I commented out the "todo.save();" line above. When I removed a todo item, removal still fails but on the console, there's no error message anymore after the "Successful request: DELETE /todos/n" message.
So I changed the removeTodo code above in an attempt to make the error messages clearer. I changed it to this:
todo.save().then(function() {
console.log('Save OK.');
}).catch((err) => {
console.log('Save failed.');
console.log(err.message);
});
I've tried various changes to things here and there but the error message that consistently appears is:
Assertion Failed: An adapter cannot assign a new id to a record that already has an id. had id: 3 and you tried to update it with undefined. This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.
I saw an error message that had something like "normalizeserializer..." text on it but I forgot to copy the whole message.
I added an adapter:
shell> ember g adapter application
// app/adapters/application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
});
But this didn't fix it.
BTW, a todo item creation, which also calls save on a todo item, works. The code resides in app/components/todos-route/component.js:
createTodo() {
const store = this.get('store');
// Get the todo title set by the "New Todo" text field
var title = this.get('newTitle');
if (title && !title.trim()) {
this.set('newTitle', '');
return;
}
// Create the new Todo model
var todo = store.createRecord('todo', {
title: title
});
// Clear the "New Todo" text field
this.set('newTitle', '');
// Save the new model
todo.save();
}
What does your Mirage mock for DELETE to /todos/:id look like? It looks like you're responding to the DELETE request with a JSON payload that contains an id, which is causing problems.
Instead you could try something that returns an empty response body, like
this.del('/todos/:id', (db, request) => {
let id = request.params.id;
db.todos.remove(id);
return new Mirage.Response(204, {}, {});
});
(This code is untested but hopefully you get the idea).
Another small point, there's a destroyRecord method on models which essentially does deleteRecord and save all at once.
I've been migrating my app from RethinkDb to Firebase and have run into a wall trying to get my collections to save when calling collection.create(). The odd thing is, I have two instances of Backbone.Firebase.Collection in my app and one of them is able to create new objects properly. The other collection, which is seemingly identical, cannot write data and generates no error messages.
I have two Collections, each with their own type of Model:
1) Projects (Collection) -> Project (Model)
2) Projects (Collection) -> Shots (Collection) -> Shot (Model)
Here are the definitions for the above collections/models:
/* Projects Collection - An ordered list of Projects */
var ProjectModelFirebase = require('../models/projectModelFirebase.js');
module.exports = Backbone.Firebase.Collection.extend({
model: ProjectModelFirebase,
firebase: new Firebase(app.fbUrl),
initialize: function() {
}
});
/* Project Model - data layer for a single Project */
module.exports = Backbone.Firebase.Model.extend({
firebase: new Firebase(app.fbUrl),
initialize: function() {
}
});
/* Shots Collection - An ordered list of Shots */
var ShotModelFirebase = require('../models/shotModelFirebase.js');
module.exports = Backbone.Collection.extend({
model: ShotModelFirebase,
firebase: new Firebase(app.fbUrl),
initialize: function() {
}
});
/* Shot Model - data layer for a single Shot */
module.exports = Backbone.Firebase.Model.extend({
firebase: new Firebase(app.fbUrl),
initialize: function() {
}
});
In my Projects route (routes file), I bind the collection to the view normally and call this.collection.create with some inputs from the template. I'm able to successfully create projects in this way and they show up in my Forge with no problem. I call the view from my route function:
// Display list of latest projects
projectsCollectionFirebase = new ProjectsCollectionFirebase();
var projectsView = new ProjectsView({collection: projectsCollectionFirebase});
In my Project route (routes file), I bind the project collection to the view and retrieve the project's information from Firebase normally:
// Display a single project
projectModelFirebase = new ProjectModelFirebase({id: project});
var projectView = new ProjectView({model: projectModelFirebase});
I then proceed (projectView file) to create a new collection of shots:
shotsCollectionFirebase = new ShotsCollectionFirebase();
shotsView = new ShotsView({ collection: shotsCollectionFirebase, project: this.model.get('id') });
The Shots view (ShotsView file) renders the template and input fields for the user to input a shot.
From here, I call this.collection.create when a user clicks the 'Save' button. Backbone recognizes the new model and updates the view accordingly, but Firebase doesn't act on it. There is an error message in the console that states 'Save called on a Firebase model, ignoring.' but I do not have any saves called out explicitly in my code.
I've tried turning on debugging and noticed that the 'projects' create command causes this message:
r:0: update {"path":"/projects/test","value":{"shots":null}}
Whereas the 'shots' collection does not ever trigger an 'update' event.
The files are nearly identical. The only thing difference I can spot is that the first example has only one collection loaded at a time whereas the second example loads a collection, grabs the data, then loads another collection as a result.
Any help would be appreciated.
You can find the full code for the project in my repo here: https://github.com/bdickason/shots/tree/firebase
There are two modes of operation in Backfire, one with models/collection that have the word 'Firebase' in them, eg: Backbone.Firebase.Model/Backbone.Firebase.Collection; and the other mode that uses regular backbone collections and models but have the 'firebase' property defined on them that overrides the backbone sync() method.
These two modes should not be mixed and matched. For example, if you're using a Backbone.Firebase.Collection, all the models in that collection will automatically be synchronized with the server any time a change is made to any model (same for Backbone.Firebase.Model). Calling save, or sync doesn't do anything because the changes are saved and synced in realtime as they happen.
Sometimes this isn't what you want in the app, for example, you want the user to explicitely click a save button before saving anything to Firebase. In these cases, it's best to use a regular Backbone collection or model, and simply provide the firebase property on them. You then need to call sync or save explicitely to put any changes on the local model/collection into Firebase.
I don't mean remove a document or documents. I mean remove the model entirely so that mongoose is no longer aware of it. After declaring a model I can't figure out how to make mongoose forget that model so that it could be recreated.
mongoose.model('Book', bookSchema);
mongoose.model('Book', bookSchema);
Currently the above throws an exception.
OverwriteModelError: Cannot overwrite 'Book' model once compiled.
I'd like to be able do something like this...
mongoose.model('Book', bookSchema);
mongoose.removeModel('Book');
mongoose.model('Book', bookSchema);
...and not get any errors. Any ideas?
try this
delete mongoose.connection.models['Book'];
and then re-register/re-initialize it . it will work fine
It appears that you'd have to overwrite some of the source code in order to be able to remove a model an add a new one since Mongoose makes sure that a model doesn't exist before it's willing to create a new one, which may or may not be more than you care to do:
if (this.models[name] && !collection) {
// model exists but we are not subclassing with custom collection
if (schema instanceof Schema && schema != this.models[name].schema) {
throw new MongooseError.OverwriteModelError(name);
}
return this.models[name];
}
Line 587 https://github.com/LearnBoost/mongoose/blob/master/lib/connection.js
Question Author's Update:
Thanks to this answer I discovered that you can access the models defined on the connection through connection.models. In my scenario I was testing a mongoose plugin with Mocha and and I wanted to clear the models between each unit test.
afterEach(function () {
mongoose.connection.models = {};
});
You can use the deleteModel method
mongoose.deleteModel(modelName);
This is actually a better way to get rid of models, schemas and collections in mongoose
mongoose.connections.forEach(connection => {
const modelNames = Object.keys(connection.models)
modelNames.forEach(modelName => {
delete connection.models[modelName]
})
const collectionNames = Object.keys(connection.collections)
collectionNames.forEach(collectionName => {
delete connection.collections[collectionName]
})
})
const modelSchemaNames = Object.keys(mongoose.modelSchemas)
modelSchemaNames.forEach(modelSchemaName => {
delete mongoose.modelSchemas[modelSchemaName]
})
Reference: https://github.com/Automattic/mongoose/issues/2874#issuecomment-388588452
I'm working my way with BreezeJS in combination with Durandal. This all works fine but I have a question about creating a new entity that has a relation with another entity.
Lets say we have an entity customer and order. When I create a new order which relies on customer it has 2 fields one for customerID and one for Customer it self! The customerID is given correctly but it crashes for not having a Customer object! How do I solve this?
My Dataservice.createT is:
// This function creates an entity for the specific table (entity)
// that is passed as an parameter. The entity is the same as the table name
//in the map folders. So for creating a new Memo entity should be tblMemo
Dataservice.prototype.createT = function (initialValues, entity) {
return this.manager.createEntity(entity, initialValues);
};
var repairOrderLineN = dataservice.createT({
RepairOrderID: '1', CustomerID: result[0].CustomerId,
Description: result[0].Description, InternalCode: result[0].Code,
Hours: result[0].Hours, Tariff: result[0].Tariff,
Costs: result[0].Costs, Quantity: result[0].Qty,
DamageCode: result[0].Dam, RepairCode: result[0].Rep,
MaterialCode: result[0].Mat, LocationCode: result[0].Location,
IsAgreement: result[0].IsAgreement, IsAuthorized: result[0].IsAuthorized,
DoRepair: result[0].DoRepair
}, 'tblRepairOrderLine');
if (repairOrderLineN.entityAspect.validateEntity()) {
extendItem(repairOrderLineN);
repairorderlines.push(new repairorderline(repairOrderLineN));
dataservice.saveChanges();
updateFinance();
}
A JSON is given from a modal to create the entity and off course I understand the customer object isn't in it! damn i'm lost :S Help :(
I need more information about your case and specifically what you mean by "crashes".
I don't know what your dataservice.createT does. It's actually a little too complex for easy answer here. Can you simplify a test case for us?
Meanwhile, here is a passing test that I'm adding to "entityTests.js" of the DocCode sample that demonstrates your scenario with Northwind.
/*********************************************************
* Add an Order with initializer that set its parent Customer by Id
*********************************************************/
test("add Customer created using initializer with parent Customer Id", 4, function () {
var em = newEm();
// create a new parent Customer
var parentCustomer = em.createEntity("Customer", {
CustomerID: breeze.core.getUuid(),
CompanyName: 'TestCo'
});
// a new Order which is a child of the parent Customer
var newOrder = em.createEntity("Order", { CustomerID: parentCustomer.CustomerID() });
ok(newOrder.entityAspect.entityState.isAdded(), "newOrder should be 'added'");
ok(parentCustomer.entityAspect.entityState.isAdded(), "parentCustomer should be 'added'");
var orderCustomer = newOrder.Customer();
ok(orderCustomer, "newOrder's parent 'Customer' property should return a Customer entity");
ok(orderCustomer === parentCustomer,
"newOrder's parent Customer should be " + parentCustomer.CompanyName());
});
It would work just as well if the parent Customer previously existed (e.g., had been fetched).
Btw, I had to initialize parentCustomer.CustomerID because it is defined in metadata as a client-generated key. In most of our examples, the entity key is store-generated (e.g., Order.OrderID) and would not be specified.
Update 4/30/2013
I suspect that somewhere in your initializer object is a result[0].something where something is an entity rather than a simple data value (e.g,. an integer id).
As I write, the createEntity blows up if any initializer value is a Breeze entity. That will be fixed in v.1.3.2 which should be out soon. Note that this new feature will be beta at first. The API won't change but there are many edge conditions and it's hard to cover them all. It will take time to drive out lingering bugs.
No matter what, you have alternatives today. You would be fine if you just initialized using the foreign key ids for the related entities instead of initializing with the related entities themselves. Or you could assign the related entities after first creating the entity as in:
...
var thing = manager.createEntity('Thing', {
... initialize with simple data values
}
var thing.relatedEntityA = relatedEntityA;
var thing.relatedEntityB = relatedEntityB;
...