Imagine a travel website where you have
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
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
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
After a simple
hotel_owner.reservations return nil.
What is the correct database design and Rails association model for something like this?