On saving entity PrimaryGeneratedColumn value not generated, gives NOT NULL constraint error - angularjs

Typeorm 0.2.8
I'm building an Ionic app for both mobile use and browser (PWA) use. Below is some shortened code from my project. I create a simple entity with a PrimaryGeneratedColumn and try to insert one instance. This generates an error about the primary column being NULL. Doesn't the word 'generated' mean the column value gets generated?
Is this a bug? Something specific to the sqljs driver? Or something obvious and simple I missed?
Entity
#Entity()
export class MyEntity {
#PrimaryGeneratedColumn()
id:number;
#Column()
name:string;
#CreateDateColumn()
createdAt:string;
}
Migration
export class Migration20181022133900 implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.createTable(new Table({
name: 'my_entity',
columns: [
{
name: 'id',
type: 'int',
isPrimary: true,
isGenerated: true
},
{
name: 'name',
type: 'varchar'
},
{
name: 'createdAt',
type: 'timestamp',
'default': 'now()'
}
]
}), true);
}
async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropTable('my_entity');
}
}
Database provider
const DATABASE_SHARED_OPTIONS:Partial<ConnectionOptions> = {
entities: [
MyEntity
],
logging: 'all',
logger: new DatabaseLogger(),
migrationsRun: true,
migrations: [
Migration20181022133900
]
};
#Injectable()
export class Database {
constructor(public platform:Platform) {}
setup(): Promise<Connection> {
let options: CordovaConnectionOptions | SqljsConnectionOptions;
// Mobile app
if (this.platform.is('cordova')) {
options = {
type: 'cordova',
database: 'my_project.db',
location: 'default',
};
options = Object.assign(options, DATABASE_SHARED_OPTIONS);
}
// Browser PWA app
else {
options = {
type: 'sqljs',
autoSave: true,
location: 'my_project',
};
options = Object.assign(options, DATABASE_SHARED_OPTIONS);
}
return createConnection(options);
}
}
App component
export class MyApp {
constructor(
platform: Platform,
database: Database
) {
platform.ready().then(() => {
database.setup()
.then((connection) => {
this.insertTest();
});
});
}
insertTest() {
const myEntity= new MyEntity();
myEntity.name = 'foo';
getRepository(MyEntity).save(myEntity)
.then((data) => {
console.log(data); // never reached due to error
});
}
}
The database log show the following query (with parameters ["foo"]):
INSERT INTO "my_entity"("name", "createdAt") VALUES (?, datetime('now'))
The following error shows up in my console:
ERROR Error: Uncaught (in promise): QueryFailedError: NOT NULL constraint failed: my_entity.id
Update 1
It only seems to give the error when using migrations. Removing the migrations and using synchronize: true on the database setting works and generates an id for the entity. So is there something wrong with my column definition in the migration code?
{
name: 'id',
type: 'int',
isPrimary: true,
isGenerated: true
}
Update 2
Okay, I fixed it. The migration configuration for a #PrimaryGeneratedColumn seems to be very specific. For anyone else facing this issue, this fixed it for me:
{
name: 'id',
type: 'integer', // instead of 'int', required for the increment strategy
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment' // thought this was the default
}

Okay, I fixed it. The migration configuration for a #PrimaryGeneratedColumn seems to be very specific. For anyone else facing this issue, this fixed it for me:
{
name: 'id',
type: 'integer', // instead of 'int', required for the increment strategy
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment' // thought this was the default
}

Related

Typeorm synchronize throws QueryFailedError: Could not drop constraint. See previous errors

I'm trying to connect to a Microsoft SQL DB having synchronize: true option within TypeORM but it seems it doesn't work for some reason and I can't figure out why.
main.module.ts
providers: [
{
provide: "OMS_DB",
useFactory: async () => {
const connection = new DatabaseConnection(
new DatabaseConfiguration("OMS_DATABASE"),
[OMSOrderDto, OMSOrderLinesDto],
false
);
if (await connection.connect()) {
return connection;
}
return null;
},
},
]
database.connection.ts
constructor(
private configuration: DatabaseConfiguration,
private entities?: any,
private synchronize = false
) {}
/**
* Creates the actual connection
*/
async connect(): Promise<boolean> {
const config: DataSourceOptions = {
name: this.configuration.name,
type: "mssql",
host: this.configuration.hostname || "localhost",
port: this.configuration.port || 1433,
username: this.configuration.username,
password: this.configuration.password,
database: this.configuration.database,
entities: this.entities,
synchronize: false,
//logging: "all",
extra: {
trustServerCertificate: true,
},
};
this.logger.debug(
`Connecting to MSSQL Database ${this.configuration.hostname} / ${this.configuration.database}`
);
try {
this.connection = new DataSource(config);
await this.connection.initialize();
this.logger.log(`Conected to DB: ${this.configuration.hostname} / ${this.configuration.database}!`);
return true;
} catch (ex) {
this.logger.error(`Failed to connect to the database: ${this.configuration.name}`);
this.logger.error(ex);
}
return false;
}
Throwing Error:
Could not drop constraint. See previous errors.
I don't see any other errors.

How can a model in Extjs only read one field from a call to a service that returns a json with multiple nodes?

I would like to ask you a question with the use of a model that calls a data reading service which returns a json.
All fields are declared in the model.
When the model is used in the creation of a store, it consequently calls a data reading service which is returned in json format. When the delete service is launched, which expects to receive only the ID field, it goes into error because the store from the sync method passes all the fields.
How can I make sure that only the ID field is passed to the service when calling the sync mode?
I also tested it by creating a model with only one field:
fields: ['firstName', {
type: 'int',
name: 'ID'
}],
Even if only one field is specified in the model, the store is initialized with all the fields coming from the json and then it correctly executes the remove method but when the sync is launched it goes into error because all the fields are present and not just the ID.
This is the model:
Ext.define('AmpelideWeb.model.VignaToponimo', {
extend: 'AmpelideWeb.model.Base',
fields: [{
name: 'ID',
type: 'int'
},
{
name: 'CODICE',
type: 'string'
},{
name: 'DESCRIZIONE',
type: 'string'
},{
name: 'SIAN_CODICE',
type: 'string'
},{
name: 'SIAN_EXPFLG',
type: 'string'
},
{
name: 'ID_REGIONE',
type: 'int'
},{
name: 'ID_PROVINCIA',
type: 'int'
}],
statics: {
baseUrl: 'vigne/toponimi'
},
});
This is the model Base:
Ext.define('AmpelideWeb.model.Base', {
extend: 'Ext.data.Model',
identifier: 'negative',
idProperty: 'ID',
inheritableStatics: {
format: 'json',
idParam: 'ID',
readMethod: 'POST',
costantUrl:'http://192.168.24.8:8080/API/v1/'
},
schema: {
proxy: {
type: 'ajax',
idParam: '{idParam}',
paramsAsJson: false,
api: {
create : '{costantUrl}'+'{baseUrl}'+'/insert',
read : '{costantUrl}'+'{baseUrl}',
update : '{costantUrl}'+'{baseUrl}'+'/edit',
destroy : '{costantUrl}'+'{baseUrl}'+'/delete'
},
actionMethods: {
create: 'POST',
read: '{readMethod}',
update: 'POST',
destroy: 'POST'
},
reader: {
type: '{format}'
},
writer: {
//rootProperty:'',
type: '{format}',
writeAllFields: true,
allowSingle: false,
},
}
}
});
This is delete method:
onDeleteClick: function () {
var form = Ext.getCmp('DettaglioVignaToponimo');
let vignaToponimoIdDelete = this.vignaToponimoId;
let store = Ext.create('Ext.data.Store', {
model: 'AmpelideWeb.model.VignaToponimoDelete',
});
store.load();
store.proxy.paramsAsJson = true;
if (form.getValues().ID!==null){
Ext.Msg.confirm('Eliminazione Vigne Toponimi', 'Se sicuro di voler eliminare il dato selezionato ?', function (id, value) {
if (id === 'yes') {
store.proxy.headers = {'Content-Type': 'application/x-www-form-urlencoded'};
const record = new AmpelideWeb.model.VignaToponimoDelete({ID: vignaToponimoIdDelete});
store.remove(record);
store.sync({
success: function (batch) {
Ext.Msg.alert('Eliminazione dati Vigne Toponimi!', 'Eliminazione Vigne Toponimi avvenuta correttamente', function (btn) {
});
},
failure: function (batch, opt) {
responseObj = batch.exceptions[0].error.response.responseJson;
Ext.Msg.alert('Eliminazione Vigne Toponimi fallita', 'messaggio di errore: ' + responseObj.message);
}
});
}else{
return false;
}
})
}else{
Ext.Msg.alert('Eliminazione Vigne Toponimi fallito', 'messaggio di errore: ID Vigne Toponimi รจ null, nessun dato da eliminare.');
}
},
Even if only one field is specified in the model, the store is initialized with all the fields coming from the json and then it correctly executes the remove method but when the sync is launched it goes into error because all the fields are present and not just the ID.
Two things:
Ext dynamically adds fields into models based on data. It encourages to not maintain field metadata and make it more flexible.
writeAllFields is true, that's why proxy sending all parameters.
writeAllFields make sense only for UPDATE operation, but anyways we don't have a control on request type here.
As I understand, you want to process the data before sending request. I think transform of Ext.data.writer.Writer will be good approach.
You can have in writer it like below:
writer: {
type: 'json',
transform: function(data, request) {
var requestAction = request.getAction(),
serialized;
// keep only id if a delete operation
serialized = (requestAction == "delete") ? { id: data.id } : data;
return serialized;
}
}
Fiddle Link

Sequelize migrations - Unable to change reference on column

My database is MSSQL.
I have a migration script where I have created the table like this:
module.exports = {
up: function(queryInterface) {
return queryInterface.createTable(
'tablename', {
'column1': {
type: SEQUELIZE.INTEGER,
references: {
model: 'Foobar',
key: 'id',
onDelete: 'cascade'
}
}
}
}
}
};
I want to change the reference for this column, so I wrote another migration script:
module.exports = {
up: function(queryInterface) {
return queryInterface.changeColumn(
'tablename', {
'column1': {
type: SEQUELIZE.INTEGER,
references: {
model: 'BarFoo', //I changed the reference
key: 'id',
onDelete: 'cascade'
}
}
}
}
}
};
However, on running the migration, I find that the reference is not changed in the database. The column still refers to the old reference - FooBar instead of BarFoo. The migration script ran successfully but the change did not happen. Any idea why?
I run the migrations using sequelize cli - sequelize db:migrate

Mongoose deep populate with self-referencing children

This is working code to populate my Locations collection, which can have self-referencing child Locations. It uses mongoose-deep-populate. But I need to add another ".childLocations" to my path variable for every extra level of depth that I want to handle. Is there a better way to accomplish unknown depths of self-references?
I have this node.js code.
var path = 'childLocations.childLocations.childLocations.childLocations.childLocations.childLocations';
exports.list = function(req, res) {
Location.find().sort('-created').populate('user', 'displayName').deepPopulate(path).exec(function(err, locations) {
console.log(locations);
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(locations);
}
});
};
Here is the schema:
var LocationSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill Location name',
trim: true
},
projectsExecutedHere: Boolean,
childLocations: [{
type: Schema.ObjectId,
ref: 'Location'
}],
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Location', LocationSchema);
var deepPopulate = require('mongoose-deep-populate');
LocationSchema.plugin(deepPopulate);
----------
Before trying mongoose-deep-populate, I had seen that mongoose 3.6 had support for deep population, but I could only make it go one level deep. This is what I tried:
Location.find().sort('-created').populate('user', 'displayName').populate('childLocations').exec(function (err, locations) {
Location.populate(locations, {path: 'childLocations.childLocations'},
function (err, data) {
console.log(err);
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(locations);
}
}
);
For the path I also tried 'childLocations.childLocations.childLocations' but it didn't populate it at all.

backbone-forms remove built-in validators in runtime

I have successfully implemented backbone-forms plug-in with it's validators, for example:
var SampleModel = Backbone.Model.extend({
schema: {
field1: {
title: $t.field1, validators: ['required', 'number']
},
field2: {
title: $t.field2, type: 'Select', options: $options.field2, validators: ['required']
},
notes: {
title: $t.notes
}
}
});
Now I am trying to find "right" (at this moment - any) way to disable built-in validators, on, for example, some check box click. After checkbox is clicked, allow form to be saved without validation.
I tried to rebuild this.model.schema for each field without validators and after did this.model.form.commit(), but it did nothing.
Can you, please, give some advice?
EDIT:
At now, I am using "dirty" method adding additional argument into commit method. See Backbone-forms commit method source:
commit: function(options, dontValidate) {
//Validate
options = options || {};
var validateOptions = {
skipModelValidate: !options.validate
};
// DIRTY
if(!dontValidate) {
var errors = this.validate(validateOptions);
if (errors) return errors;
}
//Commit
var modelError;
var setOptions = _.extend({
error: function(model, e) {
modelError = e;
}
}, options);
this.model.set(this.getValue(), setOptions);
if (modelError) return modelError;
},

Resources