hasOne without belongsTo (i.e. one-way relationships)

I have two tables, let’s call them property and address, plus some other tables.

Every property has a foreign key which references address, but other tables may also reference address. Not every address is associated with a property (for example, the address could be for a user).

Expected behavior

I’ve defined a hasOne relation:

Property.associate = (models) => {
    Property.hasOne(models.address);
  };

I expect this to work, e.g. I can navigate to the address from the property.

Actual behavior

I get an error: Forest Admin tries to access address.propertyId (which doesn’t exist — not every address is associated with a property).

I believe this is because even though I only added the hasOne relationship to property, Forest Admin infers that address belongsTo the property, which is incorrect.

How should I change the model so that property hasOne address, but address does not belongTo property ?

Failure Logs

forest_admin_coral | [forest] 🌳🌳🌳  Unexpected error: column address.propertyId does not exist
forest_admin_coral | {
forest_admin_coral |   "name": "SequelizeDatabaseError",
forest_admin_coral |   "parent": {
forest_admin_coral |     "length": 117,
forest_admin_coral |     "name": "error",
forest_admin_coral |     "severity": "ERROR",
forest_admin_coral |     "code": "42703",
forest_admin_coral |     "position": "154",
forest_admin_coral |     "file": "parse_relation.c",
forest_admin_coral |     "line": "3349",
forest_admin_coral |     "routine": "errorMissingColumn",
forest_admin_coral |     "sql": "SELECT count(\"property\".\"id\") AS \"count\" FROM \"franklin\".\"property\" AS \"property\" LEFT OUTER JOIN \"franklin\".\"address\" AS \"address\" ON \"property\".\"id\" = \"address\".\"propertyId\";"

Context

Please provide any relevant information about your setup.

  • Package Version: Not sure - docker-compose.yml has 3.4?
  • Express Version: ~4.17.1
  • Sequelize Version: ~5.15.1
  • Database Dialect: PostgreSQL
  • Database Version: 12.5
  • Project Name: forest-admin-coral

I can see propertyId is defined in .forestadmin-schema.json, even though I’ve neither added that field nor relationship to the model:

{
      "field": "propertyId",
      "type": "String",
      "field": "propertyId",
      "type": "String",
      "defaultValue": null,
      "enums": null,
      "integration": null,
      "isFilterable": true,
      "isPrimaryKey": true,
      "isReadOnly": false,
      "isRequired": true,
      "isSortable": true,
      "isVirtual": false,
      "reference": null,
      "inverseOf": null,
      "validations": [{
        "message": null,
        "type": "is present",
        "value": null
      }]
    },

Hello @jimmy and welcome on our cummunity forum !

BelongTo and hasOne relationships are the same and just depend on the point of view from which one looks at the relationship. Can you describe the problem you had when the relationship was defined as belongsTo?

For the propertyId field, it is directly managed by the sequelize relations so it is normal if the field does not exist in your DB.

Thanks @Iclisson. So it sounds like you’re saying there’s no way to express a one-way foreign key relationship like I described?

For example, I have 3 tables: address, user, and property.

Both user and property hold an addressId. However, address contains neither userId nor propertyId (since there’s no guarantee which table is referencing the address), hence ‘one-way relationship’.

Within Forest Admin, I’d like to navigate to address from either user or property. Is there alternative way to do this, if using hasOne + belongsTo doesn’t work?

BTW, this is a very common pattern in DB schema design, so it would be great if Forest Admin/Sequelize is able to accommodate this use case.

Hello @jimmy,

The behavior you observe is caused by Sequelize directly.

You might want to look into Polymorphic associations in Sequelize that seems to describe exactly your use-case.

The other solution I might think of is to create a smart relationship for your table instead of using the native one.

Both solutions should work fine, but with a polymorphic approach, you would be able to segregate the correct associated table if it is property or something else.

Please let me know if it helps you and if we can be of any more assistance.

When re-reading your original message, the issue might be much simpler actually.

Since it’s the Property that has the foreign key, could you please try to use a belongsTo not a hasOne relationship

Property.belongsTo(models.address, {
  foreignKey: {
    name: 'addressIdkey',
    field: 'address_id',
  },
  as: 'address',
});

and tell me if you still have the issue? In which case my first answer would be the way to go