Ask A Question

Notifications

You’re not receiving notifications from this thread.

Trying to emulate `belongs_to trough` using `has_one trough` but having problems

TL asked in Rails

Imagine a travel website where you have HotelOwners and Tourists. When they start a conversation, the app creates a join model named Conversation using has_many through. It's a classic many to many association:

class HotelOwner
  has_many :tourists, through: :conversations
  has_many :conversations
end

class Tourist
  has_many :hotel_owners, through: :conversations
  has_many :conversations
end

class Conversation
  belongs_to :hotel_owner
  belongs_to :tourist
end

Now we can use hotel_owner.tourists and tourist.hotel_owners. Also, the join model Conversation is also being used to keep some state on that association between them both (like, HotelOwner comments on Tourist and vice-versa).

But now we need a Reservation model. My initial ideia was this:

class Reservation
  belongs_to :hotel_owner
  belongs_to :tourist
end

But we also need to create the Conversation join model, since app logic requires that there cannot be a Reservation without a previous Conversation, even if a blank one. Also, the hotel_owner notes on tourist and vice-versa should be kept there and need to exist if a reservation exists.

After thinking about using manual callbacks to manually create the join model Conversation, I read that it would not be a good idea to add a belongs_to :conversation on Reservation because it could lead to database inconsistencies (like the problem if reservation.conversation.tourist pointed to a different tourist then reservation.tourist .. there should be a single source of truth to this association right?)

I then had the idea of using Conversation as a proxy to Reservations, like this:

class HotelOwner
  has_many :tourists, through: :conversations
  has_many :conversations
  has_many :reservations, through: :conversations
end

class Tourist
  has_many :hotel_owners, through: :conversations
  has_many :conversations
  has_many :reservations, through: :conversations
end

class Conversation
  belongs_to :hotel_owner
  belongs_to :tourist
  has_many   :reservations
end

class Reservation
  has_one :hotel_owner, through: :conversation
  has_one :tourist,     through: :conversation
  belongs_to :conversation
end

Since there is no belongs_to through in Rails to use in Reservation, other posts in SO suggest using has_one trough instead, just like I did above.

The problem is that conversation has_many reservations, and does not belong_to a reservation (like it does belong to a Tourist and HotelOwner).

It's not only semantics that bother me. If I do hotel_owner.reservations.create(tourist: Tourist.last), it does create the Reservation, but the join model Conversation is not created, leaving reservation.conversation nil.

After a simple hotel_owner.reload, hotel_owner.reservations return nil.

What is the correct database design and Rails association model for something like this?

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 76,990+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more. Icons by Icons8

    © 2023 GoRails, LLC. All rights reserved.