Skip to content

Instantly share code, notes, and snippets.

@hetsch
Last active November 4, 2017 20:53
Show Gist options
  • Select an option

  • Save hetsch/23b36e1ad01b46dcf2226e327667f3a6 to your computer and use it in GitHub Desktop.

Select an option

Save hetsch/23b36e1ad01b46dcf2226e327667f3a6 to your computer and use it in GitHub Desktop.

Revisions

  1. hetsch revised this gist Nov 4, 2017. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ function initDatabase() {
    const knex = Knex({
    dialect: 'sqlite3',
    connection: {
    filename: ':memory:'
    filename: './test.db' // ':memory:'
    },
    useNullAsDefault: true,
    debug: false
    @@ -73,12 +73,12 @@ class BaseModel extends objection.Model {
    }

    castBooleanFields(json) {
    const schema = this.constructor.jsonSchema.properties;
    const jsonSchema = this.constructor.jsonSchema.properties;
    // const booleanFields = Object.keys(json).filter(key => schema.hasOwnProperty(key) && schema[key].type === "boolean");
    Object.entries(json).forEach(([key, value]) => {
    if (
    Object.prototype.hasOwnProperty.call(schema, key) &&
    schema[key].type === 'boolean'
    Object.prototype.hasOwnProperty.call(jsonSchema, key) &&
    jsonSchema[key].type === 'boolean'
    ) {
    json[key] = Boolean(value);
    }
    @@ -335,6 +335,7 @@ function* fixtures(knex) {
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes: [
    // See: https://github.com/Vincit/objection.js/issues/480
    /* OLD RELATION: This is the ProductAttribute that was created in the fixtures */
    {
    isOptional: true,
  2. hetsch revised this gist Nov 4, 2017. 1 changed file with 2 additions and 3 deletions.
    5 changes: 2 additions & 3 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -31,8 +31,7 @@ class BaseModel extends objection.Model {
    }

    static find(id) {
    let query = this.query()
    .findById(parseInt(id, 10);
    let query = this.query().findById(parseInt(id, 10));
    if (this.relations.length > 0) {
    query = query.eager(this.relations);
    }
    @@ -368,7 +367,7 @@ function* fixtures(knex) {

    // const productClass = await ProductClass.find(1);
    console.log('\n\n%s\n\n', util.inspect(productClass));
    console.log(productClass.productAttributes[0])
    console.log(productClass.productAttributes[0]);

    console.log('\nAFTER UPDATE\n');

  3. hetsch revised this gist Nov 4, 2017. 1 changed file with 11 additions and 8 deletions.
    19 changes: 11 additions & 8 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -32,8 +32,7 @@ class BaseModel extends objection.Model {

    static find(id) {
    let query = this.query()
    .where('id', parseInt(id, 10))
    .first();
    .findById(parseInt(id, 10);
    if (this.relations.length > 0) {
    query = query.eager(this.relations);
    }
    @@ -77,11 +76,14 @@ class BaseModel extends objection.Model {
    castBooleanFields(json) {
    const schema = this.constructor.jsonSchema.properties;
    // const booleanFields = Object.keys(json).filter(key => schema.hasOwnProperty(key) && schema[key].type === "boolean");
    for (let [key, value] of Object.entries(json)) {
    if (schema.hasOwnProperty(key) && schema[key].type === 'boolean') {
    Object.entries(json).forEach(([key, value]) => {
    if (
    Object.prototype.hasOwnProperty.call(schema, key) &&
    schema[key].type === 'boolean'
    ) {
    json[key] = Boolean(value);
    }
    }
    });
    return json;
    }

    @@ -337,13 +339,13 @@ function* fixtures(knex) {
    /* OLD RELATION: This is the ProductAttribute that was created in the fixtures */
    {
    isOptional: true,
    '#dbRef': 1
    id: 1 // this is the pk of the related ProductAttribute?
    },
    /* NEW RELATION: This should relate the current ProductClass with an EXISTING ProductAttribute
    with ProductAttribute::id = 2 */
    {
    isOptional: false,
    '#dbRef': 2
    id: 2 // this is the pk of the related ProductAttribute?
    },
    /* NEW RELATION: Should create a NEW ProductAtribute and realte it to the current ProductClass */
    {
    @@ -357,7 +359,7 @@ function* fixtures(knex) {
    /* OLD RELATION: This is the ProductAttribute that was created in the fixtures */
    {
    isOptional: false,
    '#dbRef': 2
    id: 2 // this is the pk of the related ProductAttribute?
    }
    ]
    });
    @@ -366,6 +368,7 @@ function* fixtures(knex) {

    // const productClass = await ProductClass.find(1);
    console.log('\n\n%s\n\n', util.inspect(productClass));
    console.log(productClass.productAttributes[0])

    console.log('\nAFTER UPDATE\n');

  4. hetsch revised this gist Nov 4, 2017. 1 changed file with 0 additions and 160 deletions.
    160 changes: 0 additions & 160 deletions console_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,160 +0,0 @@
    INITIAL DATABASE STATE



    *****************
    ProductClasses:

    [ ProductClass {
    id: 1,
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes: [ [Object] ],
    variantAttributes: [ [Object] ] } ]
    *****************




    *****************
    ProductAttributes:

    [ ProductAttribute {
    id: 1,
    name: 'attribute_name_1',
    slug: 'attribute_slug_1',
    isOptional: false,
    dataType: 'string' },
    ProductAttribute {
    id: 2,
    name: 'attribute_name_2',
    slug: 'attribute_slug_2',
    isOptional: false,
    dataType: 'number' } ]
    *****************




    *****************
    ProductClass_ProductAttribute:

    [ { id: 1,
    productClassId: 1,
    productAttributeId: 1,
    isOptional: 1,
    order: 1 } ]
    *****************




    *****************
    ProductClass_VariantAttribute:

    [ { id: 1,
    productClassId: 1,
    productAttributeId: 2,
    isOptional: 0,
    order: 2 } ]
    *****************



    UPDATED PRODUCT CLASS



    ProductClass {
    id: 1,
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes:
    [ ProductAttribute {
    id: 3,
    name: 'attribute_name_3',
    slug: 'slug_name_3',
    isOptional: true,
    dataType: 'float',
    order: null } ],
    variantAttributes: [] }



    AFTER UPDATE



    *****************
    ProductClasses:

    [ ProductClass {
    id: 1,
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes: [ [Object] ],
    variantAttributes: [] } ]
    *****************




    *****************
    ProductAttributes:

    [ ProductAttribute {
    id: 3,
    name: 'attribute_name_3',
    slug: 'slug_name_3',
    isOptional: false,
    dataType: 'float' } ]
    *****************




    *****************
    ProductClass_ProductAttribute:

    [ { id: 1,
    productClassId: 1,
    productAttributeId: 1,
    isOptional: 1,
    order: 1 },
    { id: 2,
    productClassId: 1,
    productAttributeId: 1,
    isOptional: 1,
    order: null },
    { id: 3,
    productClassId: 1,
    productAttributeId: 2,
    isOptional: 0,
    order: null },
    { id: 4,
    productClassId: 1,
    productAttributeId: 3,
    isOptional: 1,
    order: null } ]
    *****************




    *****************
    ProductClass_VariantAttribute:

    [ { id: 1,
    productClassId: 1,
    productAttributeId: 2,
    isOptional: 0,
    order: 2 },
    { id: 2,
    productClassId: 1,
    productAttributeId: 2,
    isOptional: 0,
    order: null } ]
    *****************
  5. hetsch revised this gist Nov 4, 2017. 1 changed file with 8 additions and 2 deletions.
    10 changes: 8 additions & 2 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -40,9 +40,15 @@ class BaseModel extends objection.Model {
    return query;
    }

    static create(data) {
    static create(data, upsertGraphOptions) {
    return objection.transaction(this.knex(), trx => {
    return this.query(trx).insertGraph(data);
    return this.query(trx).insertGraph(
    data,
    upsertGraphOptions || {
    relate: true,
    unrelate: true
    }
    );
    });
    }

  6. hetsch revised this gist Nov 4, 2017. 1 changed file with 29 additions and 6 deletions.
    35 changes: 29 additions & 6 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -46,11 +46,17 @@ class BaseModel extends objection.Model {
    });
    }

    static update(data) {
    static update(data, upsertGraphOptions) {
    return objection.transaction(this.knex(), trx => {
    return this.query(trx)
    .where('id', data.id)
    .upsertGraph(data);
    .upsertGraph(
    data,
    upsertGraphOptions || {
    relate: true,
    unrelate: true
    }
    );
    });
    }

    @@ -289,10 +295,26 @@ function* fixtures(knex) {

    const dumpData = Promise.coroutine(function* dump() {
    const line = '\n\n*****************\n%s:\n\n%s\n*****************\n\n';
    console.info(line, 'ProductClasses', util.inspect(yield ProductClass.all()));
    console.info(line, 'ProductAttributes', util.inspect(yield ProductAttribute.all()));
    console.info(line, 'ProductClass_ProductAttribute', util.inspect(yield knex.select().from('ProductClass_ProductAttribute')));
    console.info(line, 'ProductClass_VariantAttribute', util.inspect(yield knex.select().from('ProductClass_VariantAttribute')));
    console.info(
    line,
    'ProductClasses',
    util.inspect(yield ProductClass.all())
    );
    console.info(
    line,
    'ProductAttributes',
    util.inspect(yield ProductAttribute.all())
    );
    console.info(
    line,
    'ProductClass_ProductAttribute',
    util.inspect(yield knex.select().from('ProductClass_ProductAttribute'))
    );
    console.info(
    line,
    'ProductClass_VariantAttribute',
    util.inspect(yield knex.select().from('ProductClass_VariantAttribute'))
    );
    // yield Promise.resolve();
    });

    @@ -336,6 +358,7 @@ function* fixtures(knex) {

    console.log('\nUPDATED PRODUCT CLASS\n');

    // const productClass = await ProductClass.find(1);
    console.log('\n\n%s\n\n', util.inspect(productClass));

    console.log('\nAFTER UPDATE\n');
  7. hetsch revised this gist Nov 4, 2017. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -336,7 +336,6 @@ function* fixtures(knex) {

    console.log('\nUPDATED PRODUCT CLASS\n');

    // const productClass = await ProductClass.find(1);
    console.log('\n\n%s\n\n', util.inspect(productClass));

    console.log('\nAFTER UPDATE\n');
  8. hetsch revised this gist Nov 4, 2017. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -41,21 +41,21 @@ class BaseModel extends objection.Model {
    }

    static create(data) {
    objection.transaction(this.knex(), trx => {
    return objection.transaction(this.knex(), trx => {
    return this.query(trx).insertGraph(data);
    });
    }

    static update(data) {
    objection.transaction(this.knex(), trx => {
    return objection.transaction(this.knex(), trx => {
    return this.query(trx)
    .where('id', data.id)
    .upsertGraph(data);
    });
    }

    static delete(id) {
    objection.transaction(this.knex(), trx => {
    return objection.transaction(this.knex(), trx => {
    return this.query(trx)
    .where('id', parseInt(id, 10))
    .delete();
    @@ -300,7 +300,7 @@ function* fixtures(knex) {

    await dumpData();

    await ProductClass.update({
    const productClass = await ProductClass.update({
    id: 1,
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    @@ -311,8 +311,8 @@ function* fixtures(knex) {
    isOptional: true,
    '#dbRef': 1
    },
    /* NEW RELATION: This should relate to the current ProductClass with an EXISTING ProductAttribute
    where ProductAttribute::id = 2 */
    /* NEW RELATION: This should relate the current ProductClass with an EXISTING ProductAttribute
    with ProductAttribute::id = 2 */
    {
    isOptional: false,
    '#dbRef': 2
    @@ -336,7 +336,7 @@ function* fixtures(knex) {

    console.log('\nUPDATED PRODUCT CLASS\n');

    const productClass = await ProductClass.find(1);
    // const productClass = await ProductClass.find(1);
    console.log('\n\n%s\n\n', util.inspect(productClass));

    console.log('\nAFTER UPDATE\n');
  9. hetsch revised this gist Nov 4, 2017. No changes.
  10. hetsch revised this gist Nov 4, 2017. 1 changed file with 160 additions and 0 deletions.
    160 changes: 160 additions & 0 deletions console_output.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,160 @@
    INITIAL DATABASE STATE



    *****************
    ProductClasses:

    [ ProductClass {
    id: 1,
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes: [ [Object] ],
    variantAttributes: [ [Object] ] } ]
    *****************




    *****************
    ProductAttributes:

    [ ProductAttribute {
    id: 1,
    name: 'attribute_name_1',
    slug: 'attribute_slug_1',
    isOptional: false,
    dataType: 'string' },
    ProductAttribute {
    id: 2,
    name: 'attribute_name_2',
    slug: 'attribute_slug_2',
    isOptional: false,
    dataType: 'number' } ]
    *****************




    *****************
    ProductClass_ProductAttribute:

    [ { id: 1,
    productClassId: 1,
    productAttributeId: 1,
    isOptional: 1,
    order: 1 } ]
    *****************




    *****************
    ProductClass_VariantAttribute:

    [ { id: 1,
    productClassId: 1,
    productAttributeId: 2,
    isOptional: 0,
    order: 2 } ]
    *****************



    UPDATED PRODUCT CLASS



    ProductClass {
    id: 1,
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes:
    [ ProductAttribute {
    id: 3,
    name: 'attribute_name_3',
    slug: 'slug_name_3',
    isOptional: true,
    dataType: 'float',
    order: null } ],
    variantAttributes: [] }



    AFTER UPDATE



    *****************
    ProductClasses:

    [ ProductClass {
    id: 1,
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes: [ [Object] ],
    variantAttributes: [] } ]
    *****************




    *****************
    ProductAttributes:

    [ ProductAttribute {
    id: 3,
    name: 'attribute_name_3',
    slug: 'slug_name_3',
    isOptional: false,
    dataType: 'float' } ]
    *****************




    *****************
    ProductClass_ProductAttribute:

    [ { id: 1,
    productClassId: 1,
    productAttributeId: 1,
    isOptional: 1,
    order: 1 },
    { id: 2,
    productClassId: 1,
    productAttributeId: 1,
    isOptional: 1,
    order: null },
    { id: 3,
    productClassId: 1,
    productAttributeId: 2,
    isOptional: 0,
    order: null },
    { id: 4,
    productClassId: 1,
    productAttributeId: 3,
    isOptional: 1,
    order: null } ]
    *****************




    *****************
    ProductClass_VariantAttribute:

    [ { id: 1,
    productClassId: 1,
    productAttributeId: 2,
    isOptional: 0,
    order: 2 },
    { id: 2,
    productClassId: 1,
    productAttributeId: 2,
    isOptional: 0,
    order: null } ]
    *****************
  11. hetsch revised this gist Nov 4, 2017. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -311,8 +311,8 @@ function* fixtures(knex) {
    isOptional: true,
    '#dbRef': 1
    },
    /* NEW RELATION: This should relate the current ProductClass with an EXISTING ProductAttribute
    with ProductAttribute::id = 2 */
    /* NEW RELATION: This should relate to the current ProductClass with an EXISTING ProductAttribute
    where ProductAttribute::id = 2 */
    {
    isOptional: false,
    '#dbRef': 2
  12. hetsch revised this gist Nov 4, 2017. 1 changed file with 10 additions and 5 deletions.
    15 changes: 10 additions & 5 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -306,18 +306,18 @@ function* fixtures(knex) {
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes: [
    /* The ids of the next two point are the ids of
    existing ProductAttributes */
    /* OLD RELATION: This is the ProductAttribute that was created in the fixtures */
    {
    isOptional: true,
    '#dbRef': 1
    },
    /* NEW RELATION: This should relate the current ProductClass with an EXISTING ProductAttribute
    with ProductAttribute::id = 2 */
    {
    isOptional: false,
    '#dbRef': 2
    },
    /* This item is a new ProductAttribute item that should be inserted and
    related with this ProductClass in the m2m table */
    /* NEW RELATION: Should create a NEW ProductAtribute and realte it to the current ProductClass */
    {
    name: 'attribute_name_3',
    slug: 'slug_name_3',
    @@ -326,14 +326,19 @@ function* fixtures(knex) {
    }
    ],
    variantAttributes: [
    /* Allready existing item */
    /* OLD RELATION: This is the ProductAttribute that was created in the fixtures */
    {
    isOptional: false,
    '#dbRef': 2
    }
    ]
    });

    console.log('\nUPDATED PRODUCT CLASS\n');

    const productClass = await ProductClass.find(1);
    console.log('\n\n%s\n\n', util.inspect(productClass));

    console.log('\nAFTER UPDATE\n');

    await dumpData();
  13. hetsch created this gist Nov 4, 2017.
    342 changes: 342 additions & 0 deletions m2m_update.mjs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,342 @@
    import Knex from 'knex';
    import objection from 'objection';
    import Promise from 'bluebird';
    import util from 'util';

    function initDatabase() {
    const knex = Knex({
    dialect: 'sqlite3',
    connection: {
    filename: ':memory:'
    },
    useNullAsDefault: true,
    debug: false
    });

    objection.Model.knex(knex);
    return knex;
    }

    class BaseModel extends objection.Model {
    static get relations() {
    return [];
    }

    static all() {
    let query = this.query();
    if (this.relations.length > 0) {
    query = query.eager(this.relations);
    }
    return query;
    }

    static find(id) {
    let query = this.query()
    .where('id', parseInt(id, 10))
    .first();
    if (this.relations.length > 0) {
    query = query.eager(this.relations);
    }
    return query;
    }

    static create(data) {
    objection.transaction(this.knex(), trx => {
    return this.query(trx).insertGraph(data);
    });
    }

    static update(data) {
    objection.transaction(this.knex(), trx => {
    return this.query(trx)
    .where('id', data.id)
    .upsertGraph(data);
    });
    }

    static delete(id) {
    objection.transaction(this.knex(), trx => {
    return this.query(trx)
    .where('id', parseInt(id, 10))
    .delete();
    });
    }

    castBooleanFields(json) {
    const schema = this.constructor.jsonSchema.properties;
    // const booleanFields = Object.keys(json).filter(key => schema.hasOwnProperty(key) && schema[key].type === "boolean");
    for (let [key, value] of Object.entries(json)) {
    if (schema.hasOwnProperty(key) && schema[key].type === 'boolean') {
    json[key] = Boolean(value);
    }
    }
    return json;
    }

    // SQLITE stores booleans as integers (0 or 1)
    // Cast them to Booleans, otherwise tcomb-form
    // throws validation errors that integers are no
    // booleans.
    // See: https://github.com/Vincit/objection.js/issues/204
    $parseDatabaseJson(json) {
    let newJson = super.$parseDatabaseJson(json);
    newJson = this.castBooleanFields(newJson);
    return newJson;
    }
    }

    class ProductAttribute extends BaseModel {
    static get tableName() {
    return 'ProductAttribute';
    }

    static get jsonSchema() {
    return {
    type: 'object',

    properties: {
    id: { type: 'integer' },
    name: { type: 'string' },
    slug: { type: 'string' },
    dataType: { type: 'string' },
    /* extra field on the m2m table */
    isOptional: { type: 'boolean' }
    },

    required: ['name', 'slug']
    };
    }
    }

    class ProductClass extends BaseModel {
    static get relations() {
    return '[productAttributes, variantAttributes]';
    }

    static get tableName() {
    return 'ProductClass';
    }

    static get jsonSchema() {
    return {
    type: 'object',

    properties: {
    id: { type: 'integer' },
    name: { type: 'string' },
    slug: { type: 'string' },
    hasVariants: { type: 'boolean' },
    productAttributes: {
    type: 'array',
    items: ProductAttribute.jsonSchema
    },
    variantAttributes: {
    type: 'array',
    items: ProductAttribute.jsonSchema
    }
    },

    required: ['name']
    };
    }

    static get relationMappings() {
    return {
    productAttributes: {
    relation: objection.Model.ManyToManyRelation,
    modelClass: ProductAttribute,
    join: {
    from: 'ProductClass.id',
    through: {
    from: 'ProductClass_ProductAttribute.productClassId',
    to: 'ProductClass_ProductAttribute.productAttributeId',
    extra: ['isOptional', 'order']
    },
    to: 'ProductAttribute.id'
    }
    },
    variantAttributes: {
    relation: objection.Model.ManyToManyRelation,
    modelClass: ProductAttribute,
    join: {
    from: 'ProductClass.id',
    through: {
    from: 'ProductClass_VariantAttribute.productClassId',
    to: 'ProductClass_VariantAttribute.productAttributeId',
    extra: ['isOptional', 'order']
    },
    to: 'ProductAttribute.id'
    }
    }
    };
    }
    }

    function* schema(knex) {
    yield knex.schema.dropTableIfExists('ProductAttribute');
    yield knex.schema.dropTableIfExists('ProductClass');
    yield knex.schema.dropTableIfExists('ProductClass_ProductAttribute');
    yield knex.schema.dropTableIfExists('ProductClass_VariantAttribute');

    yield knex.schema.createTable('ProductAttribute', table => {
    table.bigincrements('id').primary();
    table.string('name');
    table.string('slug');
    table.boolean('isOptional');
    table.string('dataType');
    });

    yield knex.schema.createTable('ProductClass', table => {
    table.bigincrements('id').primary();
    table.string('name');
    table.string('slug');
    table.boolean('hasVariants');
    });

    yield knex.schema.createTable('ProductClass_ProductAttribute', table => {
    table.increments('id').primary();
    table
    .biginteger('productClassId')
    .unsigned()
    .references('id')
    .inTable('ProductClass');
    // .onDelete("CASCADE");
    // .index();
    table
    .integer('productAttributeId')
    .unsigned()
    .references('id')
    .inTable('ProductAttribute');
    // .onDelete("CASCADE");
    // .index();
    table.boolean('isOptional');
    table.boolean('order');
    });

    yield knex.schema.createTable('ProductClass_VariantAttribute', table => {
    table.increments('id').primary();
    table
    .biginteger('productClassId')
    .unsigned()
    .references('id')
    .inTable('ProductClass');
    // .onDelete('CASCADE');
    // .index();
    table
    .integer('productAttributeId')
    .unsigned()
    .references('id')
    .inTable('ProductAttribute');
    // .onDelete('CASCADE');
    // .index();
    table.boolean('isOptional');
    table.boolean('order');
    });
    }

    function* fixtures(knex) {
    yield knex
    .insert({
    name: 'attribute_name_1',
    slug: 'attribute_slug_1',
    dataType: 'string'
    })
    .into('ProductAttribute');

    yield knex
    .insert({
    name: 'attribute_name_2',
    slug: 'attribute_slug_2',
    dataType: 'number'
    })
    .into('ProductAttribute');

    yield knex
    .insert({
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    hasVariants: true
    })
    .into('ProductClass');

    yield knex
    .insert({
    productClassId: 1,
    productAttributeId: 1,
    isOptional: true,
    order: 1
    })
    .into('ProductClass_ProductAttribute');

    yield knex
    .insert({
    productClassId: 1,
    productAttributeId: 2,
    isOptional: false,
    order: 2
    })
    .into('ProductClass_VariantAttribute');
    }

    (async () => {
    const knex = initDatabase();

    // DB setup
    await Promise.each(
    [Promise.coroutine(schema), Promise.coroutine(fixtures)],
    func => func(knex)
    );

    const dumpData = Promise.coroutine(function* dump() {
    const line = '\n\n*****************\n%s:\n\n%s\n*****************\n\n';
    console.info(line, 'ProductClasses', util.inspect(yield ProductClass.all()));
    console.info(line, 'ProductAttributes', util.inspect(yield ProductAttribute.all()));
    console.info(line, 'ProductClass_ProductAttribute', util.inspect(yield knex.select().from('ProductClass_ProductAttribute')));
    console.info(line, 'ProductClass_VariantAttribute', util.inspect(yield knex.select().from('ProductClass_VariantAttribute')));
    // yield Promise.resolve();
    });

    console.log('\nINITIAL DATABASE STATE\n');

    await dumpData();

    await ProductClass.update({
    id: 1,
    name: 'productclass_name_1',
    slug: 'productclass_slug_1',
    hasVariants: true,
    productAttributes: [
    /* The ids of the next two point are the ids of
    existing ProductAttributes */
    {
    isOptional: true,
    '#dbRef': 1
    },
    {
    isOptional: false,
    '#dbRef': 2
    },
    /* This item is a new ProductAttribute item that should be inserted and
    related with this ProductClass in the m2m table */
    {
    name: 'attribute_name_3',
    slug: 'slug_name_3',
    dataType: 'float',
    isOptional: true
    }
    ],
    variantAttributes: [
    /* Allready existing item */
    {
    isOptional: false,
    '#dbRef': 2
    }
    ]
    });

    console.log('\nAFTER UPDATE\n');

    await dumpData();
    })();

    // Run it with: node --experimental-modules test.mjs