Associations are my rails achilles heel.
I have 3 models and controllers: User
, List
, Item
.
User can can create username
and password
. List can create list name
, Item can create item_name
Ideally, a list belongs to a user. An item belongs to a list. List has many items. User has many lists. So I came up with:
class Item < ActiveRecord::Base
belongs_to :list
delegate :user, to: :list
end
class List < ActiveRecord::Base
belongs_to :user
has_many :items
end
class User < ActiveRecord::Base
has_many :lists
has_many :items, through: :lists
end
On rails console, just to make sure, I checked the columns:
2.2.1 :005 > List.column_names
=> ["id", "name", "user_id", "created_at", "updated_at"]
2.2.1 :006 > Item.column_names
=> ["id", "item_name", "list_id", "created_at", "updated_at"]
2.2.1 :007 > User.column_names
=> ["id", "username", "password", "created_at", "updated_at"]
So I went to create new User, Item, and List:
User.create(username: "iggy2", password: "helloworld")
#let's say iggy2 has user_id 2.
I want to have iggy2 to have a List named "important stuff", with Item "Wash dishes".
List.create(name: "Important", user_id: 2 ) #let's say this has id of 1
Item.create(item_name: "Wash dishes", list_id: 1)
I assumed that Item is connected to List and List is connected to User. But when I type User.last.name, instead of seeing "Important", I get a NoMethodError: undefined method 'name'
. I also get similar error on List.last.item_name
This is what my schema looks like
create_table "items", force: :cascade do |t|
t.text "item_name"
t.integer "list_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "lists", force: :cascade do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "password"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
What is missing on my code? Was my assumption wrong? How can I get User.last.name
or even User.last.item_name
to show the last user's item name or list name?
Your code is correct but your assumption is wrong. User.last
returns an user record so you will get NoMethodError
when accessing methods in associated records.
I suppose what you want is below:
List.where(user_id: User.last.id).pluck(:name) # Return all list names belong to last user
id = List.find_by(user_id: User.last)
Item.where(list_id: id).pluck(:item_name) # Return all item names belong to last user