Create related data in a related data: lead to 422 status code

Hi!

Creating a record in related data does not work:

Schema for this table:

{
    "name": "userHobby",
    "nameOld": "userHobby",
    "icon": null,
    "integration": null,
    "isReadOnly": false,
    "isSearchable": true,
    "isVirtual": false,
    "onlyForRelationships": false,
    "paginationType": "page",
    "fields": [{
      "field": "created",
      "type": "Date",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": false,
      "isReadOnly": false,
      "isRequired": false,
      "isSortable": true,
      "isVirtual": false,
      "reference": null,
      "inverseOf": null,
      "validations": []
    }, {
      "field": "hobby",
      "type": "Number",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": false,
      "isReadOnly": false,
      "isRequired": false,
      "isSortable": true,
      "isVirtual": false,
      "reference": "hobby.hobbyIdKey",
      "inverseOf": null,
      "relationship": "BelongsTo",
      "validations": []
    }, {
      "field": "level",
      "type": "Number",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": false,
      "isReadOnly": false,
      "isRequired": true,
      "isSortable": true,
      "isVirtual": false,
      "reference": null,
      "inverseOf": null,
      "validations": [{
        "message": null,
        "type": "is present",
        "value": null
      }]
    }, {
      "field": "updated",
      "type": "Date",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": false,
      "isReadOnly": false,
      "isRequired": false,
      "isSortable": true,
      "isVirtual": false,
      "reference": null,
      "inverseOf": null,
      "validations": []
    }, {
      "field": "user",
      "type": "String",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": false,
      "isReadOnly": false,
      "isRequired": false,
      "isSortable": true,
      "isVirtual": false,
      "reference": "user.userIdKey",
      "inverseOf": null,
      "relationship": "BelongsTo",
      "validations": []
    }],
    "segments": [],
    "actions": []
  }

I think this time it’s a bug no ?

Hello @alexis!

Thank you for reaching out to us

Could you please provide me a little bit more information about your project so I can try to reproduce the issue?

  • What database dialect do you use?
  • What is your version of forest-express-sequelize or forest-express-mongoose? If you could provide the exact installed version it would help me a lot.
  • Does this issue happen on other related data creation or only for this particular one?
  • Could you also share the definition of the userHobby and user models from models/ folder?

Thank you

Hi !

  1. PostgreSQL 12.4
  2. forest-express-sequelize ( “version”: “6.6.3” )
{
  "name": "shakai",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node ./server.js"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "body-parser": "1.19.0",
    "chalk": "~1.1.3",
    "cookie-parser": "1.4.4",
    "cors": "2.8.5",
    "debug": "~4.0.1",
    "dotenv": "~6.1.0",
    "express": "~4.17.1",
    "express-jwt": "5.3.1",
    "forest-express-sequelize": "^6.0.0",
    "form-data": "^3.0.0",
    "morgan": "1.9.1",
    "parse-data-uri": "^0.2.0",
    "pg": "~8.2.2",
    "require-all": "^3.0.0",
    "sequelize": "~5.15.1"
  }
}
  1. It happend not only on this particular table.

UserHobby:

// This model was generated by Lumber. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  // This section contains the fields of your model, mapped to your table's columns.
  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const UserHobby = sequelize.define('userHobby', {
    level: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    userId: {
      type: DataTypes.UUID,
      primaryKey: true,
      allowNull: false,
    },
    hobbyId: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      allowNull: false,
    },
    created: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('now()'),
      allowNull: false,
    },
    updated: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('now()'),
      allowNull: false,
    },
  }, {
    tableName: 'user_hobby',
    timestamps: false,
    schema: process.env.DATABASE_SCHEMA,
  });

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/v/v6/reference-guide/relationships#adding-relationships.
  UserHobby.associate = (models) => {
    UserHobby.belongsTo(models.hobby, {
      foreignKey: {
        name: 'hobbyIdKey',
        field: 'hobbyId',
      },
      as: 'hobby',
    });
    UserHobby.belongsTo(models.user, {
      foreignKey: {
        name: 'userIdKey',
        field: 'userId',
      },
      as: 'user',
    });
  };

  return UserHobby;
};

User:

// This model was generated by Lumber. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  // This section contains the fields of your model, mapped to your table's columns.
  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const User = sequelize.define('user', {
    id: {
      type: DataTypes.UUID,
      primaryKey: true,
      defaultValue: Sequelize.literal('uuid_generate_v4()'),
      allowNull: false,
    },
    firstName: {
      type: DataTypes.STRING,
    },
    lastName: {
      type: DataTypes.STRING,
    },
    birthday: {
      type: DataTypes.DATE,
      defaultValue: "2020-12-28 13:31:28.460958",
      allowNull: false,
    },
    seniority: {
      type: DataTypes.DATE,
      defaultValue: "2020-12-28 13:31:28.460958",
      allowNull: false,
    },
    isActive: {
      type: DataTypes.BOOLEAN,
      defaultValue: false,
      allowNull: false,
    },
    registrationCompleted: {
      type: DataTypes.BOOLEAN,
      defaultValue: false,
      allowNull: true,
    },
    admin: {
      type: DataTypes.BOOLEAN,
      defaultValue: false,
      allowNull: false,
    },
    email: {
      type: DataTypes.STRING,
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    avatar: {
      type: DataTypes.STRING,
      defaultValue: "https://localthomas.s3.eu-west-3.amazonaws.com/avatars/default.png",
      allowNull: false,
    },
    created: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('now()'),
      allowNull: false,
    },
    updated: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('now()'),
      allowNull: false,
    },
    deletedAt: {
      type: DataTypes.DATE,
    },
    jwtlostpassord: {
      type: DataTypes.STRING,
    },
  }, {
    tableName: 'user',
    timestamps: false,
    schema: process.env.DATABASE_SCHEMA,
  });

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/v/v6/reference-guide/relationships#adding-relationships.
  User.associate = (models) => {
    User.belongsTo(models.address, {
      foreignKey: {
        name: 'addressIdKey',
        field: 'addressId',
      },
      as: 'address',
    });
    User.belongsTo(models.village, {
      foreignKey: {
        name: 'villageIdKey',
        field: 'villageId',
      },
      as: 'village',
    });
    User.belongsTo(models.serviceVillage, {
      foreignKey: {
        name: 'serviceIdKey',
        field: 'serviceId',
      },
      as: 'service',
    });
    User.belongsTo(models.job, {
      foreignKey: {
        name: 'jobIdKey',
        field: 'jobId',
      },
      as: 'job',
    });
    User.belongsToMany(models.meeting, {
      through: 'meetingUsersHideUser',
      foreignKey: 'userId',
      otherKey: 'meetingId',
      as: 'meetingThroughMeetingUsersHideUsers',
    });
    User.belongsToMany(models.searchSaved, {
      through: 'searchSavedProposedUsers',
      foreignKey: 'userId',
      otherKey: 'searchSavedId',
      as: 'searchSavedThroughSearchSavedProposedUsers',
    });
    User.belongsToMany(models.house, {
      through: 'houseUsersUser',
      foreignKey: 'userId',
      otherKey: 'houseId',
      as: 'houseThroughHouseUsersUsers',
    });
    User.belongsToMany(models.value, {
      through: 'userValuesValue',
      foreignKey: 'userId',
      otherKey: 'valueId',
      as: 'valueThroughUserValuesValues',
    });
    User.belongsToMany(models.software, {
      through: 'userSoftwareneedsSoftware',
      foreignKey: 'userId',
      otherKey: 'softwareId',
      as: 'softwareThroughUserSoftwareneedsSoftwares',
    });
    User.belongsToMany(models.skill, {
      through: 'userSkillneedsSkill',
      foreignKey: 'userId',
      otherKey: 'skillId',
      as: 'skillThroughUserSkillneedsSkills',
    });
    User.belongsToMany(models.hobby, {
      through: 'userHobbyneedsHobby',
      foreignKey: 'userId',
      otherKey: 'hobbyId',
      as: 'hobbyThroughUserHobbyneedsHobbies',
    });
    User.hasMany(models.userSoftware, {
      foreignKey: {
        name: 'userIdKey',
        field: 'userId',
      },
      as: 'userSoftwares',
    });
    User.hasMany(models.userHobby, {
      foreignKey: {
        name: 'userIdKey',
        field: 'userId',
      },
      as: 'userHobbies',
    });
    User.hasMany(models.userSkill, {
      foreignKey: {
        name: 'userIdKey',
        field: 'userId',
      },
      as: 'userSkills',
    });
    User.hasMany(models.userMeeting, {
      foreignKey: {
        name: 'userIdKey',
        field: 'userId',
      },
      as: 'userMeetings',
    });
    User.hasMany(models.feedback, {
      foreignKey: {
        name: 'userIdKey',
        field: 'userId',
      },
      as: 'feedbacks',
    });
    User.hasMany(models.feedback, {
      foreignKey: {
        name: 'guestIdKey',
        field: 'guestId',
      },
      as: 'guestFeedbacks',
    });
    User.hasMany(models.meeting, {
      foreignKey: {
        name: 'proposalUserIdKey',
        field: 'proposalUserId',
      },
      as: 'proposalUserMeetings',
    });
    User.hasMany(models.meeting, {
      foreignKey: {
        name: 'authorIdKey',
        field: 'authorId',
      },
      as: 'authorMeetings',
    });
    User.hasMany(models.searchSaved, {
      foreignKey: {
        name: 'userIdKey',
        field: 'userId',
      },
      as: 'searchSaveds',
    });
  };

  return User;
};

Hobby:

// This model was generated by Lumber. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  // This section contains the fields of your model, mapped to your table's columns.
  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const Hobby = sequelize.define('hobby', {
    name: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    type: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    created: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('now()'),
      allowNull: false,
    },
    updated: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('now()'),
      allowNull: false,
    },
    imageName: {
      type: DataTypes.STRING,
    },
    type2: {
      type: DataTypes.STRING,
    },
    registration: {
      type: DataTypes.BOOLEAN,
      defaultValue: false,
      allowNull: false,
    },
  }, {
    tableName: 'hobby',
    timestamps: false,
    schema: process.env.DATABASE_SCHEMA,
  });

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/v/v6/reference-guide/relationships#adding-relationships.
  Hobby.associate = (models) => {
    Hobby.belongsTo(models.hobby, {
      foreignKey: {
        name: 'parentIdKey',
        field: 'parentId',
      },
      as: 'parent',
    });
    Hobby.belongsTo(models.category, {
      foreignKey: {
        name: 'categoryIdKey',
        field: 'categoryId',
      },
      as: 'category',
    });
    Hobby.belongsTo(models.category, {
      foreignKey: {
        name: 'category2IdKey',
        field: 'category2Id',
      },
      as: 'category2',
    });
    Hobby.belongsToMany(models.meeting, {
      through: 'meetingHobbiesHobby',
      foreignKey: 'hobbyId',
      otherKey: 'meetingId',
      as: 'meetingThroughMeetingHobbiesHobbies',
    });
    Hobby.belongsToMany(models.user, {
      through: 'userHobbyneedsHobby',
      foreignKey: 'hobbyId',
      otherKey: 'userId',
      as: 'userThroughUserHobbyneedsHobbies',
    });
    Hobby.hasMany(models.userHobby, {
      foreignKey: {
        name: 'hobbyIdKey',
        field: 'hobbyId',
      },
      as: 'userHobbies',
    });
    Hobby.hasMany(models.hobby, {
      foreignKey: {
        name: 'parentIdKey',
        field: 'parentId',
      },
      as: 'parentHobbies',
    });

    Hobby.belongsTo(models.user, {
      foreignKey: {
        name: 'createdByIdKey',
        field: 'createdById',
      },
      as: 'createdBy',
    });

    Hobby.belongsTo(models.village, {
      foreignKey: {
        name: 'createdByVillageIdKey',
        field: 'createdByVillageId',
      },
      as: 'createdByVillage',
    });
  };

  return Hobby;
};

Thank you for your response !

Thank you for sharing the information.

Could you please try to upgrade the forest-express-sequelize to 6.7.4 and let me know if the issue persists? Thank you

Hi, unfortunately, it resolves nothing :confused:

The version inside my node module is now the the 6.7.4.

Let’s try something different :thinking:

Can you please fo to models/index.js and set the logging: console.log in databaseOptions
Then please paste the output from your development console starting from the moment when you access the creation page. I might find some information that would tell me where the issue is :female_detective:

Thank you :pray:

I already added the logging to sequelize (it’s the first thing I am doing when there is a “bug” with forestadmin)

There is no query executed while I am clicking on the button.

Output:


query {}
path /userHobby
body {
  "data": {
    "attributes": {
      "created": "2021-01-26T15:10:54.000Z",
      "level": 3,
      "updated": "2021-01-26T15:11:03.000Z"
    },
    "relationships": {
      "hobby": {
        "data": {
          "type": "hobbies",
          "id": "1723"
        }
      },
      "user": {
        "data": {
          "type": "users",
          "id": "a7e66902-a07a-4705-b813-9312f3fa20b9"
        }
      }
    },
    "type": "userHobbies"
  }
}
params {}
POST /forest/userHobby 422 143 - 38.408 ms

Thank you for your patience.

I just found a different thread for one of the issues you faced with the same model.

Unfortunately, the issue is still the same - we do not support well the composite primary key. I am sorry about this limitation

I reported the issue to our technical team and you can follow the progress here.

I am really sorry to not be able to be of any help to you. If I can do something more to help you please let me know.

I think your suppositions are wrongs because I migrated my table with a single primary key, (throwing away the composite one) and it still do not work.

I also updated the model in the userHobby schema:

// This model was generated by Lumber. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  // This section contains the fields of your model, mapped to your table's columns.
  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const UserHobby = sequelize.define('userHobby', {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      allowNull: false,
    },
    level: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    userId: {
      type: DataTypes.UUID,
      primaryKey: false,
      allowNull: false,
    },
    hobbyId: {
      type: DataTypes.INTEGER,
      primaryKey: false,
      allowNull: false,
    },
    created: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('now()'),
      allowNull: false,
    },
    updated: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('now()'),
      allowNull: false,
    },
  }, {
    tableName: 'user_hobby',
    timestamps: false,
    schema: process.env.DATABASE_SCHEMA,
  });

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/v/v6/reference-guide/relationships#adding-relationships.
  UserHobby.associate = (models) => {
    UserHobby.belongsTo(models.hobby, {
      foreignKey: {
        name: 'hobbyIdKey',
        field: 'hobbyId',
      },
      as: 'hobby',
    });
    UserHobby.belongsTo(models.user, {
      foreignKey: {
        name: 'userIdKey',
        field: 'userId',
      },
      as: 'user',
    });
  };

  return UserHobby;
};

Same error, nothing change.

Removing

    userId: {
      type: DataTypes.UUID,
      primaryKey: false,
      allowNull: false,
      field: 'userId'
    },
    hobbyId: {
      type: DataTypes.INTEGER,
      primaryKey: false,
      allowNull: false,
      field: 'hobbyId',
    },

From the model was the solution (after deleting the composite primarykey).

However the simple fix should be to generate a good schema. The schema generated set the field name to user and hobby instead of userId and hobbyId.

The solution will be to keep the field from the schema instead of the alias defined in the association.

ResourceDeserializer.extractRelationships use the field name to set the column to use.

Hello @alexis ,

Thanks for sharing the solution with us. I’m glad you found how to fix the error.

I’m not sure to understand the issue you pointed out about field names. Is it a problem in the generated model?

Yes because I think that you are generating bad field names, when I had the composite primary key my schema was wrong because the field name wasn’t the name of the columns but the name of the alias declared in the association (aka hobby & user instead of hobbyId & userId)

I could eventually do a PR but I don’t have enough time :innocent:

Could you please share with us an example of tables definition that lead to this issue, once generating a project with lumber?