Smart Relationships

Expected behavior

I followed the guide here: https://docs.forestadmin.com/documentation/reference-guide/relationships/create-a-smart-relationship

to make a smart relationship. I don’t have a FK in the database, but want to save the ID of an item in another table. I thought smart fields could do this as a “virtual” relationship.

Actual behavior

When I try and save the record with the item selected in the smart relationship, there is a put request generated to a /relationships/field_name endpoint that doesn’t exist.

Failure Logs

forestadmin | PUT /forest/city/C58AD591-D364-4F34-974C-2F1D60B44667/relationships/pageImage 404 211 - 1.835 ms

Context

Please provide any relevant information about your setup.

  • Database Dialect: mssql

Hello @Jonathan_Nye !

In order to make this work, you would have to implement routes related to this new relationship (That’s why smart relationships are set to read only by default).

Here is a quick example using Cars and Users models. Users have a field car that should match with the Cars name field.

In case I want to have a smart relationship car_relation, I would need to add :
In forest/users.js:

collection('users', {
  actions: [],
  fields: [{
    field: 'car_relation',
    type: 'String',
    reference: 'cars.name',
    get: function (user) {
      return models.cars.findOne({ where: { name: user.car }});
    }
  }],
  segments: [],
});

Then, if you want to be able to create new records with this relation, you need to override the POST route in routes/users.js :

// Create a User with the new relation
router.post('/users', permissionMiddlewareCreator.create(), async (request, response, next) => {
  try {
    const requestBody = request.body;
    // if the relation is present
    if(requestBody.data.relationships.car_relation.data.id) {
      // Find the associated relation
      const relationshipCar = await models.cars.findByPk(request.body.data.relationships.car_relation.data.id);
      // Manually update the given body to handle the relation
      requestBody.data.attributes.car = relationshipCar.name;
    }
    // Update the record and send it back
    const recordCreator = new RecordCreator(models.users);
    const recordToCreate = await recordCreator.deserialize(requestBody);
    const newRecord = await recordCreator.create(recordToCreate);
    const serializedRecord = await recordCreator.serialize(newRecord);
    response.send(serializedRecord);
  } catch (err) {
    next(err);
  }
});

Finally, if you want to update the record via the same mecanism, you will need to add a relationship routes, in the same routes/users.js file :

router.put('/users/:userId/relationships/car_relation', async (request, response, next) => {
  try {
    // data.id contains the id of the selected car in the UI
    if(request.body.data.id) {
      // Get all the record needed and update
      const user = await models.users.findByPk(request.params.userId);
      const car = await models.cars.findByPk(request.body.data.id);
      user.car = car.name;
      await user.save();
      response.send(user);
    }
  } catch (err) {
    next(err);
  }
});

Hope this will help :slight_smile:

4 Likes

Thank you so much. This works perfectly!

1 Like