I'm trying to use recursive association in Ruby on Rails and I'm not getting the expected output :) I want to have match with two players (winner and loser) and I thought about recursive association.
I used scaffolds to get the basics done:
rails g scaffold Player firstname:string lastname:string
rails g scaffold Match date:date result:integer winner:references loser:references
And then modified Match model:
class Match < ActiveRecord::Base
has_one :winner, class_name: 'Player', foreign_key: 'id'
has_one :loser, class_name: 'Player', foreign_key: 'id'
end
However if I add manually some players and go to match#index both winner and loser are the same (even if I set them to be different).
Part of view is like this:
<tbody>
<% #matches.each do |match| %>
<tr>
<td><%= match.date %></td>
<td><%= match.result %></td>
<td><%= match.winner %></td>
<td><%= match.loser %></td>
<td><%= link_to 'Show', match %></td>
<td><%= link_to 'Edit', edit_match_path(match) %></td>
<td><%= link_to 'Destroy', match, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
When I edit Match, then I clearly see that there are different IDs of players, but in show or index both players are the same.
What am I doing wrong?
Thank you :)
You are using an "has_one" association, which is actually setting the database link on the other class/table (here, Player). This is not what you want: you want to have two foreign keys (winner_id and loser_id) on the Match table.
This can be done using the much more common "belongs_to" association:
class Match < ActiveRecord::Base
belongs_to :winner, class_name: "Player"
belongs_to :loser, class_name: "Player"
end
Related
I've got "demand", "shift" and "parent" (this is going to be a baby sitter thingy).
Now the models look like this:
class Demand < ActiveRecord::Base
belongs_to :parent
belongs_to :shift
end
&
class Parent < ActiveRecord::Base
has_many :demands, :dependent => :destroy
has_many :shifts, :through => :demands
accepts_nested_attributes_for :demands, allow_destroy: true
# Returns fullname of parent
def fullname
"#{firstname} #{name}"
end
end
&
class Shift < ActiveRecord::Base
has_many :supps, :dependent => :destroy
has_many :nanns, :through => :supps
has_many :demands, :dependent => :destroy
has_many :parents, :through => :demands
end
If I now want to display a shift's description (a param of the shift table) instead of its _id, I get the following error:
undefined method `description' for nil:NilClass
Here is some code from the corresponding demands index view:
<td><%= demand.parent.name %></td>
<td><%= demand.demand %></td>
<td><%= demand.shift.description %></td> <----THIS LINE PRODUCES THE ERROR
<td><%= link_to 'Show', demand %></td>
<td><%= link_to 'Edit', edit_demand_path(demand) %></td>
<td><%= link_to 'Destroy', demand, method: :delete, data: { confirm: 'Are you sure?' } %></td>
I think that I gave the models the correct has_many and belongs_to associations so I don't really find the mistake here. Thanks in advance for any help!
You have a demand that has no associated shift. If you want to identify which one in your table, replace...
<td><%= demand.shift.description %></td>
with
<td><%= demand.shift ? demand.shift.description : 'missing shift!' %></td>
The lines with missing shifts will now tell you that shift is missing.
In my asset index view, when it loops through each Asset, I want to show the most recent scene name. I am using .last to pull out the most recent record. When I .inspect what is returned, I can see the values. The problem is, when I try to access one of the attributes of what is returned I get an undefined method.
So for instance if I do this:
<%= (asset.scene_assignments.where(asset_id: asset).order("created_at").last).scene_id %>
I get:
NoMethodError in Assets#index undefined method `scene_id' for SceneAssignment:0x4bc2c28
But if I call #inspect instead of #name, I can see what is contained inside. So if I do this:
<%= (asset.scene_assignments.where(asset_id: asset).order("created_at").last).inspect %>
It prints this:
SceneAssignment id: 4, scene_id: 3, asset_id: 1, arrival_time: nil, created_at: "2014-10-16 01:43:50", updated_at: "2014-10-16 01:43:50", location_id: 1, asset_role_id: 1
Why can't I access one of the attributes from what is returned?
In my asset index view, I have this:
<% #assets.each do |asset| %>
<tr>
<td><%= asset.name %></td>
<td>
<%= (asset.scene_assignments.where(asset_id: asset).order("created_at").last).inspect %>
</td>
<td><%= link_to 'Show', asset %></td>
<td><%= link_to 'Edit', edit_asset_path(asset) %></td>
<td><%= link_to 'Destroy', asset, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
My relationship between an asset and scene is set up like this:
class SceneAssignment < ActiveRecord::Base
belongs_to :scene
belongs_to :asset
belongs_to :location
belongs_to :asset_role
belongs_to :incident
accepts_nested_attributes_for :asset
end
class Scene < ActiveRecord::Base
has_many :scene_assignments
has_many :assets, :through => :scene_assignments
belongs_to :incident
belongs_to :scene_type
accepts_nested_attributes_for :scene_assignments, :allow_destroy => true
end
class Asset < ActiveRecord::Base
has_many :scene_assignments
has_many :scenes, :through => :scene_assignments
end
I believe it is .scene_id, not .last that is causing your error. The result of `.where(…) is a collection of records, not a single record.
To fix that, you could say asset.scene_assignments.where(asset_id: asset).order("created_at").last).first.scene_id though that makes some fairly messy code just a little bit worse. :)
Good day all,
Pardon me for my noob-ness in rails.
So here's my question.
So I've a category model and a itinerary model defined below
class Category < ActiveRecord::Base
has_many :categorizations, :dependent => :destroy
has_many :itineraries, :through => :categorizations
end
class Itinerary < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations
end
So in my view, I am looping through categories to display itineraries in groups.
<% #categories.each do |category| %>
<table>
<thead>
<tr>
<th colspan="4"><%= category.name %></th>
</tr>
</thead>
<tbody>
<% category.itineraries.each do |itinerary| %>
<tr>
<td><%= itinerary.name %></td>
<td><%= link_to 'Show', itinerary %></td>
<td><%= link_to 'Edit', edit_itinerary_path(itinerary) %></td>
<td><%= link_to 'Destroy', itinerary, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
So I am wondering, how do we display itineraries that are not categorised yet?
I've searched around and found out that using scopes in the model will be the way to go.
scope :without_categories, -> { includes(:categorizations).where( :categorizations => { :itinerary_id => nil } )}
I find it not very "DRY" due to the fact that I've to write another table to iterate through itinerary.without_categories again.
Is there a way where we're able to code it in such a way where categories.all shows everything with uncategorized items in it?
Thank you.
Update #1
Decided to use this in my controller, which builds a new "Uncategorized" category on index action and it'll add to the array.
def index
uncategorized = Category.new
uncategorized.name = "Uncategorized";
uncategorized.itineraries = Itinerary.without_categories
#categories = Category.all << uncategorized
end
I know that in rails, controllers should be as skinny as possible.
But I can't think of a better way.
Anyone with a better answer, please feel free to share. Thanks! :)
You just have to find the itineraries that dont have a reference inside the Categorizations table. You can do a nested query for this.
SELECT * FROM itineraries where id NOT IN ( SELECT itinerary_id FROM categorizations')
just do a method in your Itinerary model like this:
def self.uncategorized
Itinerary.find_by_sql('SELECT * FROM itineraries where id NOT IN ( SELECT itinerary_id FROM categorizations)')
end
Easy for anyone but this newbie, I'm sure, but I can't find the answer anywhere. I have a User model and a Role model, with role_id in the users table; I want to show the actual role (Admin, Visitor, etc) which resides in the roles table, on my users index page.
The pertinent section of the index.html.erb:
<% #users.each do |user| %>
<tr>
<td><%= user.username %></td>
<td><%= user.email %></td>
<td><%= user.role_id %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
So what do I put in place of user.role.id?
role.rb:
class Role < ActiveRecord::Base
end
user.rb:
class User < ActiveRecord::Base
has_one :role
end
I'm using Rails 3, fwiw.
TIA
Your models are indeed configured incorrectly, but from what I can understand of the question you want the following.
class User < ActiveRecord::Base
belongs_to :role
end
class Role < ActiveRecord::Base
has_many :users
end
And then you can do the following:
<%= user.role.name_field %>
This will allow multiple Users to all have the same role. Instead of enforcing a one to one relationship. No schema change is needed.
I have a rails app and I'd like to be able to do something like
task.labels.first.label_name to get the label name of a task. However, I get an undefined method label_name. I did a t = Task.first; t.labels.first.label_name in the console, and that worked so I'm not sure what's going on. Here's the models then the locations of the error:
class Categorization < ActiveRecord::Base
belongs_to :label
belongs_to :task
end
class Label < ActiveRecord::Base
attr_accessible :label_name
has_many :categorizations
has_many :tasks, :through => :categorizations
end
class Task < ActiveRecord::Base
attr_accessible :task
has_many :categorizations
has_many :labels, :through => :categorizations
end
The error is in the index
<% for task in #tasks %>
<tr>
<td><%= task.task %></td>
<td><%= task.labels.first.label_name %></td>
<td><%= link_to "Show", task %></td>
<td><%= link_to "Edit", edit_task_path(task) %></td>
<td><%= link_to "Destroy", task, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
My guess would be that one of the tasks in #tasks does not have any labels so when you call task.labels.first it returns nil and then you try to call label_name for nil which of course does not work.
The easiest solution would be to do a check like this:
<td><%= task.labels.first.label_name unless task.labels.first.nil? %></td>
Now that does not look so good in the view so you might want to place that check in your Task model instead, perhaps like this:
class Task < ActiveRecord::Base
attr_accessible :task
has_many :categorizations
has_many :labels, :through => :categorizations
def label_name
self.labels.first.label_name unless self.labels.first.nil?
end
end
And in the view:
<td><%= task.label_name %></td>
And another thing, just in case you would like to view all the associated labels, you could do something like this:
task.labels.map(&:label_name).join(", ")