How to relate and see data in "Related Data" tab in forest admin

  • Project name: whydonate-admin
  • Team name: Operations
  • Environment name: staging
  • Agent type & version: node v16.14.2
  • Recent changes made on your end if any: no
    I am trying to create relation between two collections
    for example table A has one to one relation with table B as attached below,
    but these relations are not visible in “RELATED DATA” tab in forest admin, but they are visible in summary tab

Note: we do not have foreign key constrains in database

Can you please help me to understand why its not visible in related data

Hello @Zaeem_Ahmad,

Thanks for using the community and submitting your questions.

Could you please provide the details from the template to this section? To better help you, we need to know more about the context:

- Project name: ...
- Team name: ...
- Environment name: ...
- Agent type & version: ...
- Database type: ...

Can you also please describe the structure of the 2 collections involved?

@GuillaumeGautreau Thanks for you rapid response,
I have added details in the first question of thread

Let me explain the structure again with tables names and structure same as it is in our database

so we have auth_user table, that is linked with accounting_wallet table,

that means every user has one wallet id,
or in another words auth_user table has one to one relation with accounting_wallet table.

so how can i make relation between two so that it will be visible on “Related Data” tab in forest admin.

I have tried all the ways mentioned in forest admin documentation
but “Related Data” tab in forest admin remain unclickable as mentioned in attachment

Hello @Zaeem_Ahmad,

It seems that you are using forest-express-sequelize, so the correct way of declaring relationships is to use Sequelize to define them.

You can declare that your table auth_user has a belongsTo relationship with accounting_wallet. And on the other side, accounting_wallet has a hasOne or a hasMany relationship with auth_user.

Can you share with us how your sequelize models are defined for both of your collections? I just need the structure and the declaration of relationships you already have, if any.

@GuillaumeGautreau Yes we are using forest-express-sequelize,

I am attaching models files

auth_user

// This model was generated by Forest CLI. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/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/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const AuthUser = sequelize.define(
    "authUser",
    {
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
      },
      password: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      username: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      firstName: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      lastName: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      email: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      isStaff: {
        type: DataTypes.INTEGER,
        allowNull: false,
      },
      isActive: {
        type: DataTypes.INTEGER,
        allowNull: false,
      },
    },
    {
      tableName: "auth_user",
      underscored: true,
      timestamps: false,
    }
  );

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/reference-guide/relationships#adding-relationships.
  AuthUser.associate = (models) => {
  };

  return AuthUser;
};

accounting_wallet

// This model was generated by Forest CLI. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/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/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const AccountingWallet = sequelize.define(
    "accountingWallet",
    {
      createdAt: {
        type: DataTypes.DATE,
      },
      updatedAt: {
        type: DataTypes.DATE,
      },
      userId: {
        type: DataTypes.INTEGER,
        allowNull: false,
      },
      transferwiseAccountId: {
        type: DataTypes.INTEGER,
      },
    },
    {
      tableName: "accounting_wallet",
      underscored: true,
      timestamps: false,
    }
  );

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/reference-guide/relationships#adding-relationships.
  AccountingWallet.associate = (models) => {
  };

  return AccountingWallet;
};

Ok, then you just need to add this:

to auth_user:

  AuthUser.associate = (models) => {
   AuthUser.hasOne(models. AccountingWallet);
   // or
   // AuthUser.hasMany(models. AccountingWallet);
  };

to accounting_wallet

  AccountingWallet.associate = (models) => {
    AccountingWallet.belongsTo(models.AuthUser);
  };

An you can remove the declaration inside the collection.

It should to the trick

@GuillaumeGautreau I have tried this let me again reshare the model files

auth_user

// This model was generated by Forest CLI. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/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/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  
  const AuthUser = sequelize.define(
    "authUser",
    {
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
      },
      password: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      username: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      firstName: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      lastName: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      email: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      isStaff: {
        type: DataTypes.INTEGER,
        allowNull: false,
      },
      isActive: {
        type: DataTypes.INTEGER,
        allowNull: false,
      },
    },
    {
      tableName: "auth_user",
      underscored: true,
      timestamps: false,
    }
  );

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/reference-guide/relationships#adding-relationships.
  AuthUser.associate = (models) => {
    AuthUser.hasOne(models.accountingWallet);
  };

  return AuthUser;
};

accounting_wallet

// This model was generated by Forest CLI. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/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/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const AccountingWallet = sequelize.define(
    "accountingWallet",
    {
      createdAt: {
        type: DataTypes.DATE,
      },
      updatedAt: {
        type: DataTypes.DATE,
      },
      userId: {
        type: DataTypes.INTEGER,
        allowNull: false,
      },
      transferwiseAccountId: {
        type: DataTypes.INTEGER,
      },
    },
    {
      tableName: "accounting_wallet",
      underscored: true,
      timestamps: false,
    }
  );

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/reference-guide/relationships#adding-relationships.
  AccountingWallet.associate = (models) => {
    AccountingWallet.belongsTo(models.authUser);
  };

  return AccountingWallet;
};

The error I got is

[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node ./server.js`
Your application is listening on port 3310.
[forest] 🌳🌳🌳  Checking need for apimap update...
Your admin panel is available here: https://app.forestadmin.com/projects
[forest] 🌳🌳🌳  No change in apimap, nothing sent to Forest.
[forest] 🌳🌳🌳  Unexpected error: target: whydonate-staging.-.primary: vttablet: Unknown column 'accountingWallet.auth_user_id' in 'field list' (errno 1054) (sqlstate 42S22) (CallerID: 4va89atg60xg6jt5lime): Sql: "select authUser.id, authUser.`password`, authUser.username, authUser.first_name as firstName, authUser.last_name as lastName, authUser.email, authUser.is_staff as isStaff, authUser.is_active as isActive, accountingWallet.id as `accountingWallet.id`, accountingWallet.created_at as `accountingWallet.createdAt`, accountingWallet.updated_at as `accountingWallet.updatedAt`, accountingWallet.user_id as `accountingWallet.userId`, accountingWallet.transferwise_account_id as `accountingWallet.transferwiseAccountId`, accountingWallet.auth_user_id as `accountingWallet.authUserId` from auth_user as authUser left join accounting_wallet as accountingWallet on authUser.id = accountingWallet.auth_user_id where authUser.id = :authUser_id", BindVars: {REDACTED}
{
  "name": "SequelizeDatabaseError",
  "parent": {
    "code": "ER_BAD_FIELD_ERROR",
    "errno": 1054,
    "sqlState": "42S22",
    "sqlMessage": "target: whydonate-staging.-.primary: vttablet: Unknown column 'accountingWallet.auth_user_id' in 'field list' (errno 1054) (sqlstate 42S22) (CallerID: 4va89atg60xg6jt5lime): Sql: \"select authUser.id, authUser.`password`, authUser.username, authUser.first_name as firstName, authUser.last_name as lastName, authUser.email, authUser.is_staff as isStaff, authUser.is_active as isActive, accountingWallet.id as `accountingWallet.id`, accountingWallet.created_at as `accountingWallet.createdAt`, accountingWallet.updated_at as `accountingWallet.updatedAt`, accountingWallet.user_id as `accountingWallet.userId`, accountingWallet.transferwise_account_id as `accountingWallet.transferwiseAccountId`, accountingWallet.auth_user_id as `accountingWallet.authUserId` from auth_user as authUser left join accounting_wallet as accountingWallet on authUser.id = accountingWallet.auth_user_id where authUser.id = :authUser_id\", BindVars: {REDACTED}",
    "sql": "SELECT `authUser`.`id`, `authUser`.`password`, `authUser`.`username`, `authUser`.`first_name` AS `firstName`, `authUser`.`last_name` AS `lastName`, `authUser`.`email`, `authUser`.`is_staff` AS `isStaff`, `authUser`.`is_active` AS `isActive`, `accountingWallet`.`id` AS `accountingWallet.id`, `accountingWallet`.`created_at` AS `accountingWallet.createdAt`, `accountingWallet`.`updated_at` AS `accountingWallet.updatedAt`, `accountingWallet`.`user_id` AS `accountingWallet.userId`, `accountingWallet`.`transferwise_account_id` AS `accountingWallet.transferwiseAccountId`, `accountingWallet`.`auth_user_id` AS `accountingWallet.authUserId` FROM `auth_user` AS `authUser` LEFT OUTER JOIN `accounting_wallet` AS `accountingWallet` ON `authUser`.`id` = `accountingWallet`.`auth_user_id` WHERE `authUser`.`id` = '43623';"
  },

ok, you just need to specify which field is used for the relationship:

 AccountingWallet.associate = (models) => {
    AccountingWallet.belongsTo(models.authUser, {
      foreignKey: { name: 'userId' },
    });
  };

@GuillaumeGautreau the error is gone but “Related Data” tab is still unclickable,

is there any way we can make relations inside collection, and see that relations in “Related Data” tab

Hello @Zaeem_Ahmad,

On which environment did you make the change? I cannot see the declaration of a relation in the .forestadmin-schema.json received for the staging environment.

Can you please check the .forestadmin-schema.json of your environment, and copy/paste the content for the collections authUser and accountingWallet?

@GuillaumeGautreau In staging environment,

I am attaching .forestadmin-schema.json (authUser & accountingWallet only)

also I am attaching content for the collections of authUser and accountingWallet below

.forestadmin-schema.json

{
  "collections": [
    {
      "name": "accountingWallet",
      "nameOld": "accountingWallet",
      "icon": null,
      "integration": null,
      "isReadOnly": false,
      "isSearchable": true,
      "isVirtual": false,
      "onlyForRelationships": false,
      "paginationType": "page",
      "fields": [
        {
          "field": "createdAt",
          "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": "updatedAt",
          "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": "id",
          "type": "Number",
          "defaultValue": null,
          "enums": null,
          "integration": null,
          "isFilterable": true,
          "isPrimaryKey": true,
          "isReadOnly": false,
          "isRequired": false,
          "isSortable": true,
          "isVirtual": false,
          "reference": null,
          "inverseOf": null,
          "validations": []
        },
        {
          "field": "transferwiseAccountId",
          "type": "Number",
          "defaultValue": null,
          "enums": null,
          "integration": null,
          "isFilterable": true,
          "isPrimaryKey": false,
          "isReadOnly": false,
          "isRequired": false,
          "isSortable": true,
          "isVirtual": false,
          "reference": null,
          "inverseOf": null,
          "validations": []
        },
        {
          "field": "userId",
          "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
            }
          ]
        }
      ],
      "segments": [],
      "actions": []
    },
    {
      "name": "authUser",
      "nameOld": "authUser",
      "icon": null,
      "integration": null,
      "isReadOnly": false,
      "isSearchable": true,
      "isVirtual": false,
      "onlyForRelationships": false,
      "paginationType": "page",
      "fields": [
        {
          "field": "email",
          "type": "String",
          "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": "firstName",
          "type": "String",
          "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": "id",
          "type": "Number",
          "defaultValue": null,
          "enums": null,
          "integration": null,
          "isFilterable": true,
          "isPrimaryKey": true,
          "isReadOnly": false,
          "isRequired": false,
          "isSortable": true,
          "isVirtual": false,
          "reference": null,
          "inverseOf": null,
          "validations": []
        },
        {
          "field": "isActive",
          "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": "isStaff",
          "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": "isSuperuser",
          "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": "lastName",
          "type": "String",
          "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": "password",
          "type": "String",
          "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": "username",
          "type": "String",
          "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": "languageCode",
          "type": "String",
          "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": "organisationName",
          "type": "String",
          "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": "type",
          "type": "String",
          "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": "isReceiver",
          "type": "String",
          "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": "deactivated",
          "type": "String",
          "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": "walletId",
          "type": "String",
          "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": "merchantUid",
          "type": "String",
          "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": "merchantStatus",
          "type": "String",
          "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": "complianceStatus",
          "type": "String",
          "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": "bankAccountUid",
          "type": "String",
          "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": "bankAccountStatus",
          "type": "String",
          "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": "bankAccountStatus",
          "type": "String",
          "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": "fundraisers",
          "type": "String",
          "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": "donationsReceived",
          "type": "String",
          "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": "completedTransaction",
          "type": "String",
          "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
            }
          ]
        }
      ],
      "segments": [],
      "actions": []
    },
  ],
  "meta": {
    "liana": "forest-express-sequelize",
    "liana_version": "8.5.14",
    "stack": {
      "database_type": "mysql",
      "engine": "nodejs",
      "engine_version": "16.17.0",
      "orm_version": "5.15.2"
    }
  }
}

authUser Collection

collection("authUser", {
  actions: [],
  fields: [
  ],
  segments: [],
});

accountingWallet Collection

const { collection } = require('forest-express-sequelize');

collection('accountingWallet', {
  actions: [],
  fields: [],
  segments: [],
  
});

It seems that the file .forestadmin-schema.json does not contain modifications related to the relationships you added in the code.

Can you please share the code related to relationships you finally decided to add?

Also, did you copied the .forestadmin-schema.json from a development environment, after having started the server? This file is regenerated in development server on startup. On production servers, it’s not automatically regenerated.

Can you please share the code related to relationships you finally decided to add?

for now I am only making relations between auth_user and accounting_wallet table

inside auth_user.js (models file)

AuthUser.associate = (models) => {
    
  };

inside accounting_wallet.js (models file)

 AccountingWallet.associate = (models) => {
    AccountingWallet.belongsTo(models.authUser, {
      foreignKey: { name: 'userId' }
    });
  };

Also, did you copied the .forestadmin-schema.json from a development environment, after having started the server? This file is regenerated in development server on startup. On production servers, it’s not automatically regenerated.

yes, even after restarting server, I haven’t seen any changes in .forestadmin-schema.json

Ok, then please also add the relationship on the AuthUser collection:

  AuthUser.associate = (models) => {
   AuthUser.hasOne(models. AccountingWallet, {
     foreignKey: { name: 'userId' }
   });
   // or, if the user can have multiple wallets
   // AuthUser.hasMany(models. AccountingWallet, {
   //  foreignKey: { name: 'userId' }
   // });
  };

What is the value of NODE_ENV in your .env file? If its value is “production”, it might explain why the schema has not been regenerated.

After doing this change and restarting your server, can you copy/paste the content related to your 2 collecitons from the schema?

Thanks

That above mentioned code is added, and server is restarted but no progress

and NODE_ENV value is already set to “staging”

I am attaching schema file below

{
  "collections": [
    {
      "name": "accountingWallet",
      "nameOld": "accountingWallet",
      "icon": null,
      "integration": null,
      "isReadOnly": false,
      "isSearchable": true,
      "isVirtual": false,
      "onlyForRelationships": false,
      "paginationType": "page",
      "fields": [
        {
          "field": "createdAt",
          "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": "updatedAt",
          "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": "id",
          "type": "Number",
          "defaultValue": null,
          "enums": null,
          "integration": null,
          "isFilterable": true,
          "isPrimaryKey": true,
          "isReadOnly": false,
          "isRequired": false,
          "isSortable": true,
          "isVirtual": false,
          "reference": null,
          "inverseOf": null,
          "validations": []
        },
        {
          "field": "transferwiseAccountId",
          "type": "Number",
          "defaultValue": null,
          "enums": null,
          "integration": null,
          "isFilterable": true,
          "isPrimaryKey": false,
          "isReadOnly": false,
          "isRequired": false,
          "isSortable": true,
          "isVirtual": false,
          "reference": null,
          "inverseOf": null,
          "validations": []
        },
        {
          "field": "userId",
          "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
            }
          ]
        }
      ],
      "segments": [],
      "actions": []
    },
    {
      "name": "authUser",
      "nameOld": "authUser",
      "icon": null,
      "integration": null,
      "isReadOnly": false,
      "isSearchable": true,
      "isVirtual": false,
      "onlyForRelationships": false,
      "paginationType": "page",
      "fields": [
        {
          "field": "email",
          "type": "String",
          "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": "firstName",
          "type": "String",
          "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": "id",
          "type": "Number",
          "defaultValue": null,
          "enums": null,
          "integration": null,
          "isFilterable": true,
          "isPrimaryKey": true,
          "isReadOnly": false,
          "isRequired": false,
          "isSortable": true,
          "isVirtual": false,
          "reference": null,
          "inverseOf": null,
          "validations": []
        },
        {
          "field": "isActive",
          "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": "isStaff",
          "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": "isSuperuser",
          "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": "lastName",
          "type": "String",
          "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": "password",
          "type": "String",
          "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": "username",
          "type": "String",
          "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": "languageCode",
          "type": "String",
          "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": "organisationName",
          "type": "String",
          "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": "type",
          "type": "String",
          "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": "isReceiver",
          "type": "String",
          "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": "deactivated",
          "type": "String",
          "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": "walletId",
          "type": "String",
          "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": "merchantUid",
          "type": "String",
          "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": "merchantStatus",
          "type": "String",
          "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": "complianceStatus",
          "type": "String",
          "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": "bankAccountUid",
          "type": "String",
          "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": "bankAccountStatus",
          "type": "String",
          "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": "bankAccountStatus",
          "type": "String",
          "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": "fundraisers",
          "type": "String",
          "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": "donationsReceived",
          "type": "String",
          "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": "completedTransaction",
          "type": "String",
          "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
            }
          ]
        }
      ],
      "segments": [],
      "actions": []
    },     
  ],
  "meta": {
    "liana": "forest-express-sequelize",
    "liana_version": "8.5.14",
    "stack": {
      "database_type": "mysql",
      "engine": "nodejs",
      "engine_version": "16.17.0",
      "orm_version": "5.15.2"
    }
  }
}

Can you run the code with the NODE_ENV set to development to generate a new schema? With NODE_ENV set to anything else, the schema is not updated, to prevent unwanted changes in production.

The recommended way of proceeding when doing changes on the agent is the following:

  1. On a development environment (local), make the changes and test them on a testing DB
  2. Commit the .forestadmin-schema.json along with changes in the source code
  3. Deploy on staging the modified code and schema and test them
  4. Deploy on production the modified code and schema

@GuillaumeGautreau,

sure I will test the above point and will let you know here

@GuillaumeGautreau I changed node_env to development and followed above steps
I can see changes is .forestadmin-schema.json file, I will attach.json file below (only authUser and accountingWallet),
also I am seeing relations between authUser and accountingWallet in .forestadmin-schema.json

every thing runs without error but “related data” tab is still unclickable

authUser


"name": "authUser",
    "nameOld": "authUser",
    "icon": null,
    "integration": null,
    "isReadOnly": false,
    "isSearchable": true,
    "isVirtual": false,
    "onlyForRelationships": false,
    "paginationType": "page",
    "fields": [{
      "field": "accountingWallet",
      "type": "Number",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": false,
      "isReadOnly": false,
      "isRequired": false,
      "isSortable": true,
      "isVirtual": false,
      "reference": "accountingWallet.id",
      "inverseOf": "authUser",
      "relationship": "HasOne",
      "validations": []
    }, {
      "field": "email",
      "type": "String",
      "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": "firstName",
      "type": "String",
      "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": "id",
      "type": "Number",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": true,
      "isReadOnly": false,
      "isRequired": true,
      "isSortable": true,
      "isVirtual": false,
      "reference": null,
      "inverseOf": null,
      "validations": []
    }, {
      "field": "isActive",
      "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": "isStaff",
      "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": "lastName",
      "type": "String",
      "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": "password",
      "type": "String",
      "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": "username",
      "type": "String",
      "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
      }]
    }],
    "segments": [],
    "actions": []
  }

accountingWallet

"name": "accountingWallet",
    "nameOld": "accountingWallet",
    "icon": null,
    "integration": null,
    "isReadOnly": false,
    "isSearchable": true,
    "isVirtual": false,
    "onlyForRelationships": false,
    "paginationType": "page",
    "fields": [{
      "field": "authUser",
      "type": "Number",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": false,
      "isReadOnly": false,
      "isRequired": false,
      "isSortable": true,
      "isVirtual": false,
      "reference": "authUser.id",
      "inverseOf": "accountingWallet",
      "relationship": "BelongsTo",
      "validations": []
    }, {
      "field": "createdAt",
      "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": "id",
      "type": "Number",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": true,
      "isReadOnly": false,
      "isRequired": false,
      "isSortable": true,
      "isVirtual": false,
      "reference": null,
      "inverseOf": null,
      "validations": []
    }, {
      "field": "transferwiseAccountId",
      "type": "Number",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": false,
      "isReadOnly": false,
      "isRequired": false,
      "isSortable": true,
      "isVirtual": false,
      "reference": null,
      "inverseOf": null,
      "validations": []
    }, {
      "field": "updatedAt",
      "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": []
    }],
    "segments": [],
    "actions": []
  }

Ok, I can see the changes on our side.

Now, can you please:

  1. Access your staging environment on forestadmin
  2. Click on “Edit layout”
  3. Select your “authUser” collection on the left
  4. Click on the reorder button in the table view (see my capture)
  5. Make sur that the eye is active on the column “accountingWallet”

@GuillaumeGautreau Thanks for helping, its working, now I am able to move between tables.

One more thing which I want to ask is that, like in admin forest demo (see attachment).
Related data is visible on “summary tab”, I want to achieve same thing in my case, please help me on that