Reputation: 221
I am a beginner to RoR and have been doing some coding recently myself using the guides. I am currently stuck at a point and have done some googling around to find the reason, but with no luck. Can somebody please help me with this ?
My scenario:
class Enum < ActiveRecord::Base
establish_connection :common
attr_accessible :name, :description, :updated_at
has_many :enumlists
has_many :enumvalues, :through => :enumlists
validates :name, presence: true, length: { maximum:20}
validates :description, length: {maximum: 100}
end
class Enumlist < ActiveRecord::Base
establish_connection :common
belongs_to :enums
belongs_to :enumvalues
attr_accessible :updated_at
end
class Enumvalue < ActiveRecord::Base
establish_connection :common
attr_accessible :category, :description, :updated_at
has_many :enumlists
has_many :enums, :through => :enumlists
end
and the migration entry is:
class CreateEnums < ActiveRecord::Migration
def change
create_table :enums do |t|
t.string :name, :limit=>20
t.string :description, :limit=>100
t.timestamps
end
add_index :enums, [:name], :unique => true
create_table :enumvalues do |t|
t.string :category, :limit=>50
t.string :description, :limit=>50
t.timestamps
end
create_table :enumlists do |t|
t.integer :enum_id
t.integer :enumvalue_id
t.timestamps
end
add_index :enumlists, [:enum_id, :enumvalue_id], :unique => true
end
end
Now, i would like to view all the Enums with their values in the controller. From the examples i have seen, i can do Enum.find(params[:id]).enumlists, however i dont find a way to get all the enums with the linked tables. I'm obviously missing something simple here, but i'm unable to figure out what it is..
Thanks..
Update: I can use @enums = Enum.includes(:enumlists) and it does return the correct entries, however i cannot get 1 level deeper.. i.e @enums = Enum.includes(:enumlists, :enumvalues) and then use @enums.enumlists.enumvalues to get the list of all enumeration values.
To help, the data is structured as below:
Enums:
-----------------------------------------------------------------------------------
id name description
-----------------------------------------------------------------------------------
1 App1 OS Operating systems for Application 1
2 App2 OS Operating systems for Application 2
Enumvalues:
-----------------------------------------------------------------------------------
id category description
-----------------------------------------------------------------------------------
1 Operating Systems AIX
2 Operating Systems Linux
3 Operating Systems Windows
Enumlists:
-----------------------------------------------------------------------------------
id enum_id enumvalue_id
-----------------------------------------------------------------------------------
1 1 1
2 1 2
3 2 1
What i need in the output is:
enums = [ [1, [Operating Sysetms, AIX, Operating Systems, Linux], 2,[Operating Sysetms, AIX] ]
Update:
The following should work.
@enums = Enum.includes({:enumlists => :enumvalues})
Note, the names enum_lists and enum_values would be more idiomatic Ruby
@AlexBlakemore - Thanks. Your input made me believe that there was an issue in the model and once i found it, your method and the method i was using earlier both appear to be working fine..
I think i found the reason for the failure. It was due to the class Enumlist which had an incorrect definition. It should have been
class Enumlist < ActiveRecord::Base
establish_connection :common
belongs_to :enum <-- Renamed to enum instead of enums
belongs_to :enumvalue <-- Renamed to enumvalue instead of enumvalues
attr_accessible :updated_at
end
After making the above change, i tried using the following ways to link the tables and both of them work perfectly.. Method1:
@enums = Enum.includes(:enumlists, :enumvalues)
[1m[36mEnum Load (0.4ms)[0m [1mSELECT `enums`.* FROM `enums` [0m
[1m[35mEnumlist Load (0.3ms)[0m SELECT `enumlists`.* FROM `enumlists` WHERE `enumlists`.`enum_id` IN (1, 2)
[1m[36mEnumvalue Load (0.3ms)[0m [1mSELECT `enumvalues`.* FROM `enumvalues` WHERE `enumvalues`.`id` IN (1, 2)[0m
@enums = Enum.includes(:enumlists, :enumvalues)
[#<Enum id: 1, name: "WMB OS", description: "Operating System (WMB)", created_at: "2000-01-01 09:00:00", updated_at: "2000-01-01 09:00:00">, #<Enum id: 2, name: "4690 OS", description: "Operating System (DEC)", created_at: "2008-01-01 09:00:00", updated_at: "2008-01-01 09:00:00">]
Method2: Using the suggestion from @AlexBlakemore also i am getting a similar output.
@enums = Enum.includes({:enumvalues=>:enumlists})
[1m[36mEnum Load (0.2ms)[0m [1mSELECT `enums`.* FROM `enums` [0m
[1m[35mEnumlist Load (0.3ms)[0m SELECT `enumlists`.* FROM `enumlists` WHERE `enumlists`.`enum_id` IN (1, 2)
[#<Enum id: 1, name: "WMB OS", description: "Operating System (WMB)", created_at: "2000-01-01 09:00:00", updated_at: "2000-01-01 09:00:00">, #<Enum id: 2, name: "4690 OS", description: "Operating System (DEC)", created_at: "2008-01-01 09:00:00", updated_at: "2008-01-01 09:00:00">]
[1m[36mEnumvalue Load (0.6ms)[0m [1mSELECT `enumvalues`.* FROM `enumvalues` WHERE `enumvalues`.`id` IN (1, 2)[0m
[1m[35mEnumlist Load (0.4ms)[0m SELECT `enumlists`.* FROM `enumlists` WHERE `enumlists`.`enumvalue_id` IN (1, 2)
There are no errors so clearly the list is getting populated by both methods. But when i try to access the enumlist/enumvalues, i get the a NoMethodError. I have tried various combinations, none worked..
@enums.enumlists
@enums.enumlist
@enums.enumvalues
@enums.enumvalue
Not sure what is missing in here.. Any suggestions please ?? Once i get this to work, i can confirm which of the above two methods returns the correct result. Fingers crossed !!
Upvotes: 2
Views: 493
Reputation: 221
Ok.. Here's the solution to the problem.
The first mistake was that the join table was linking the table names in singular form, due to which the join was broken. I.e The table models should be as below. It contains 3 changes to my original model posted in the query. a. Renamed the join table fields to use singular form. b. As suggested by @Alex, the original table names have been renamed to more idomatic ruby c. t.integer is changed it to t.reference to make it explicit.
class Enum < ActiveRecord::Base
establish_connection :common
attr_accessible :name, :description, :updated_at
has_many :enum_lists
has_many :enum_values, :through => :enum_lists
validates :name, presence: true, length: { maximum:20}
validates :description, length: {maximum: 100}
end
class Enum_list < ActiveRecord::Base
establish_connection :common
belongs_to :enum <-- Renamed to enum instead of enums
belongs_to :enum_value <-- Renamed to enum_value instead of enum_values
attr_accessible :updated_at
end
class Enum_value < ActiveRecord::Base
establish_connection :common
attr_accessible :category, :description, :updated_at
has_many :enum_lists
has_many :enums, :through => :enum_lists
end
The second issue was that i was trying to use find(:all) in the controller to get the results. Instead it could easily be done using one of the below
@enums = Enum.includes(:enum_lists, :enum_values) or
@enums = Enum.includes(:enum_values) or
@enums = Enum.includes({:enum_lists => :enum_values}),
bearing in mind the number of queries generated by each usage.
The last issue was that i couldn't print the values on ruby console by using @enums.enum_lists. This would rather need to be written within a loop to print the values. Hence, i coded the following in my view to print the enumeration name and its corresponding values as:
<% @enums.each do |enum| %>
<h4><%= enum.name %></h4>
<table width="100%">
<tr class="<%= cycle('odd','even', :name=>"line") %>"><td>Description</td><td><%= enum.description %></td></tr>
<table>
<% enum.enum_values.each do |enum_value| %>
<tr class="<%= cycle('odd','even', :name=>"line") %>"><td><%= enum_value.category %></td><td><%= enum_value.description %></td></tr>
<% end %>
<tr class="<%= cycle('odd','even', :name=>"line") %>"><td colspan="2"><span class="greyed">Last updated <%= time_ago_in_words(enum.updated_at) %> ago</span></td></tr>
</table>
</table>
Thanks a lot to @Alex for his inputs.. !! I can continue my work on RoR further !!
Upvotes: 0
Reputation: 11896
The following should tell ActiveRecord to eagerly fetch enumlists and enumvalues when fetching Enums.
@enums = Enum.includes({:enumlists => :enumvalues})
Note, the names enum_lists and enum_values would be more idiomatic Ruby
or you could try just
@enums = Enum.includes(:enumvalues)
if you really just mean to have a has_and_belongs_to_many association. (HABTM)
Upvotes: 1