Question

I'm trying to create simple internal messaging service for that i have used devise for authentication so i have users table. I have created a messages table for messages and a join table called messages_users. I want to use has_many through association so my associations are like this:-

user.rb :-

has_many :sent_messages, class_name: "Message", foreign_key: "user_id"
has_many :message_users
has_many :received_messages, through: :message_users, source: :message

message.rb:-

belongs_to :user
has_many :message_users
has_many :receivers, through: :message_users, source:  :user

message_user.rb :-

class MessageUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :message
end

and schema.rb :-

ActiveRecord::Schema.define(version: 20130912100741) do

create_table "messages", force: true do |t|
  t.text     "subject"
  t.text     "body",       limit: 2147483647
  t.integer  "user_id"
  t.datetime "created_at"
  t.datetime "updated_at"
end

create_table "messages_users", id: false, force: true do |t|
  t.integer "user_id",    null: false
  t.integer "message_id", null: false
end

add_index "messages_users", ["message_id"], name: "index_messages_users_on_message_id",       using: :btree
add_index "messages_users", ["user_id"], name: "index_messages_users_on_user_id", using: :btree 
create_table "users", force: true do |t|
  t.string   "email",                  default: "", null: false
  t.string   "encrypted_password",     default: "", null: false
  t.string   "reset_password_token"
  t.datetime "reset_password_sent_at"
  t.datetime "remember_created_at"
  t.integer  "sign_in_count",          default: 0
  t.datetime "current_sign_in_at"
  t.datetime "last_sign_in_at"
  t.string   "current_sign_in_ip"
  t.string   "last_sign_in_ip"
  t.string   "confirmation_token"
  t.datetime "confirmed_at"
  t.datetime "confirmation_sent_at"
  t.string   "unconfirmed_email"
  t.integer  "failed_attempts",        default: 0
  t.string   "unlock_token"
  t.datetime "locked_at"
  t.string   "authentication_token"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "provider"
  t.string   "uid"
  t.string   "name"
end

add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_index "users", ["unlock_token"], name: "index_users_on_unlock_token", unique: true, using: :btree

end

It works fine when i use user_object.sent_messages but when i use user_object.received_messages it gives me Mysql2::Error: Table 'to_do.message_users' doesn't exist: SHOW FULL FIELDS FROM message_users ActiveRecord::StatementInvalid: Mysql2::Error: Table 'to_do.message_users' doesn't exist error. I want to use has_many through only and not habtm. There were a couple of questions here on stackoverflow but none of them solved my issue so any suggestions?

Was it helpful?

Solution

In Rails, we rely on "convention over configuration" to define a lot of the magic here. What is happening here is there's just some confusion over the pluralization of the "through" model.

Give this a try:

user.rb

has_many :sent_messages, class_name: "Message", foreign_key: "user_id"
has_many :messages_users
has_many :received_messages, through: :messages_users, source: :message

message.rb

belongs_to :user
has_many :messages_users
has_many :receivers, through: :messages_users, source:  :user

messages_user.rb

class MessagesUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :message
end

schema.rb - same as you had it with "create_table messages_users"...

Essentially, in Rails the convention is that:

  • Model name must always be "Singular" (ie. MessagesUser)
  • Database and collection names will always be "Pluralized" (ie. messages_users)

The confusing part of this is that your model name is a concatenation of two words and so probably the fact that one of them is pluralized and the other isn't was confusing. You can either leave it like this, or simply use one word to define the "through" model.

Try saying out in words something like "User has many Messages through Inbox". In which case your "through" model can be called Inbox (with a plural of Inboxes).

Either way, it will work as long as you follow convention :) hope this helps!

OTHER TIPS

I would only have two models for this User and Messages.

In messages I would give two fields sender_id and received_id.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top