Deleted association breaks collection display

Hi,

I have 2 models in my Ruby on Rails app : User and Freelance, with Freelance having a belongs_to User relationship.
When a user deletes their account, I destroy the User object but not the Freelance object, to keep data consistency in my app, so some Freelances have a nil user_id.
(for instance, some Freelances might have a conversation together and one deletes their account, I need to keep the deleted account’s freelance id to keep it all working).

Forest displays my Freelance collection fine, until I destroy a Freelance’s User. Then I get the “cannot reach your data” message, with this network result on fetch:

I can see in the “includes” array that the first User has a null id, which I guess is the cause of the error message.

I tried various things to “hide” the user field or ask Forest not to load it, but I couldn’t find anything to make it work. Can I reconfigure the user field to let it have a null relationship? Or even ask Forest not to load this particular field?

Expected behavior

Forest shows the Freelance collection even with a destroyed belongs_to relationship.

Actual behavior

I get the “Cannot reach your data” error message when trying to display the collection with an object having an empty belongs_to relationship.

  • Package Version: forest_liana (6.1.1)
  • Database Dialect: PostgreSQL
  • Database Version: 12.4
  • Project Name: ArtelinkAdmin

Hey @AlexandreCC, and welcome to our community :wave:

I can see in the “includes” array that the first User has a null id, which I guess is the cause of the error message.

Indeed, this is most likely the source of this issue.

Did you override any routes on your end to achieve this behavior? If yes, would you mind sharing a bit of the code responsible of this?

I can see that this belongsTo relationship is optional, so the included part of the response should indeed not contain the “null” user.

I’ll also try to reproduce this issue on my end.

EDIT: I did a quick test on my end, and I’m currently not able to reproduce. If any custom code is involved in the issue you describe, it could help.
Otherwise, would it be possible for you to share a DDL of the database you are using (Privately is you consider it as a sensible information). Thanks :pray:

2 Likes

Hey @jeffladiray,

thank you very much for your quick answer.
As you spoke about custom code involved, I checked my code and tried to remove a custom method and it solved the issue.

The method was a user “placeholder” in the Freelance model :

  def user
    return super unless user_id.nil?

    User.new(first_name: 'Compte', last_name: 'Désactivé')
  end

Thing is I really need this method in order to achieve the desired behavior of deleted accounts, and I also want the id to be null so I can check it in my app.

I’ve played around a little bit, and adding a fake placeholder id string like this

User.new(id: "placeholder", first_name: 'Compte', last_name: 'Désactivé')

did the trick for Forest. It points to a User with an id of 0 (which is not ideal, but at least it’s not breaking).

Is there maybe some way I can know when it’s Forest calling my method so I can add this placeholder id only in Forest context ?

There’s probably some other solution to that problem but that’s the first one coming to my mind

Good to know that you found a workaround to make it work.

You should be able to override routes that generate the unwanted call, but that would in my opinion require a lot of code to make it work.

To find a better fix, I think I’ll need a little bit more context:

  • Isn’t belongs_to :user, optional: true an alternative?
  • You seems to be in a very good use case for a deletedAt/paranoid setup. Instead of removing a user from freelance, you would have just to set the deletedAt column. Is that an option for you?

Let me know if that help. Otherwise, let me know if you have further informations to share, so we can find a better alternative :pray:

Thank you for your hints Jeff.

  • belongs_to optional is not really an option since I override the user method of the Freelance model, this method will be called anyway.
  • deleted_at with paranoid pattern won’t work either because I need the user_id of my Freelance to be null in order to detect a deleted account within my app. So even if the user still exists in database, I need this null field and the problem will still be there.

I’ll try and find some other possible fix tomorrow, maybe checking within my app that the id is a string instead of null to detect deleted accounts, which would fix Forest collection display…

Thanks a lot for your time and thoughts :slight_smile:

1 Like

No problem @AlexandreCC.

To me the forest issue is still related to your def user method, that will always override the user value is case the user_id field is nil.
Sadly, there is no way for the JSONAPI serializer we use to detect that this field should not be serialized. Even though id is not defined, you still provide a value for the user, which explain the issue.

Good to know you have a workaround that could work though, so I’m marking this thread as resolved.
Still, let me know if you are able to find a better alternative for your use case, it could help other member of the community :slight_smile:

Let me know :pray:

2 Likes

Hello,

just so you know, I found a workaround :

def user
    return super unless user_id.nil?

    User.new(id: '', first_name: 'Compte', last_name: 'Désactivé')
end

Giving an empty string id prevents Forest from crashing on list display (it points to a 0 id), and my app renders this id as null when returning it from the api, so my app can keep working the way it already did.