Skip to main content

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

Rails • Asked by TL
73f48eba61fe1d101c4a365efc868299

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?


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 18,000+ developers who get early access to new screencasts, articles, guides, updates, and more.

By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

More of a social being? We're also on Twitter and YouTube.