Ruby On Rails - How to access a Child from its Parent? - ruby-on-rails

I'm struggling to find a way to access a child attribute from its' parent. I have these two models:
class SuspendedCompany < ActiveRecord::Base
has_many :cases
end
class Case < ActiveRecord::Base
belongs_to :suspended_company
end
I want to access Case's name from a SuspendedCompany. In my SuspendedComany controller I have tried a join like this:
#case = Case.joins(:suspended_company).where(...)

I believe you are finding a company in your SuspendedCompany controller by something like this
#suspended_company = SuspendedCompany.find(params[:id])
In order to have access to cases in your view without additional queries you can transform it into
#suspended_company = SuspendedCompany.includes(:cases).find(params[:id])
And then in your view you can do
#suspended_company.cases.map(&:name)

In the controller index for SuspendedCompany write
#cases = #SuspendedCompany.cases
And in the index view for SuspendedCompany
<% #cases.each do |case| %>
<%= case.name %>
<% end %>

Related

Showing all nested models in Rails 4

I'm new to Rails and I'm teaching myself how to use it by working on my own project.
I have a model called Users that has a one-to-many relationship with a model called Pets. The Pets model has a belongs_to relationship to User. I'm trying to build a page where a user can see the list of all Pets. How can I go about doing that?
Thanks.
I agree with Tamer Shlash. It sounds like you'd benefit from practicing a tutorial to get more exposure to model-view-controller (MVC) concepts, and interacting with a database.
Since you want all users to see all pets, Pet.all will return all rows of the pets table.
In a controller action, assign #pets to Pet.all then use that instance variable to loop through the pets and display information about them.
<ul>
<%= #pets.each do |pet| %>
<li><%= pet.name %></li>
<% end %>
</ul>
You'll be best looking at the Rails getting started guide to see how to do this properly. Basically, everything you want to achieve in Rails is done using the MVC programming pattern - each view takes data from the controller, which builds that data from its models
Here is a basic example for you:
Models
#app/models/pet.rb
Class Pet < ActiveRecord::Base
belongs_to :user
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :pets
end
--
Routes
#config/routes.rb
resources :pets #-> /pets
--
Controllers
#app/controllers/pets_controller.rb
def index
#pets = Pet.all
end
--
Views
#app/views/pets/index.html.erb
<% #pets.each do |pet| %>
<%= pet.name %>
<% end %>

Activerecord relationship joins

I'm working in Rails and Activerecord and trying to merge some data from related tables together in my view, here are my models:
class Report < ActiveRecord::Base
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :reports
end
class User < ActiveRecord::Base
has_many :votes
end
Each vote has a user and a report.
In my view I need the following, hopefully as easily as possible:
a total number of votes for each report from all users
a true/false if the user has voted on the particular report
Right now, my basic understanding of ActiveRecord queries only takes me as far as creating a helper with the report and the current user and than querying for the existence of report
Same goes for counting the total number of votes for all users for a report as follows:
Controller
def index
#this is where I need some help to get the related information into a single
#object
#reports = Report.where('...')
end
View
<% #reports.each do |report| %>
<% if(hasVoted(#current_user.id, report.id)) %>
<!-- display the 'has voted html' -->
<% end %>
<% end %>
Helper
def hasVoted(current_user_id, report_id)
if(Vote.exists?(:user_id => current_user_id, :report_id => report_id))
true
else
false
end
end
Hope that gives you some insight into helping...thanks!
Sure.
Firstly, please consider naming your method has_voted? instead of hasVoted. Secondly, consider moving that method in the user model.
#user.rb
def voted_on?(report_id)
votes.where(:report_id => report_id).exists?
end
Your view will then read
<% if current_user.voted_on?(report) %>
...
<% end %>
The other question you had was to find the number of votes a report has received. This is simple too. You could do this in your view inside the loop where you iterate over #reports
<% vote_count = report.votes.size %>
Please keep in mind that his would result in N queries (where N = number of reports). Since you are new to Rails i'm not going to complicate your Reports query in the controller where you fetch you reports to include the vote count (unless you ask me to). But once you are comfortable with what happening in here, thats where you would optimize.

Fetching data using one-many association

I am new to ror. I have 2 tables for group (called 'ab') and sub-group(called 'cd').Each group has several sub-groups.I have defined belongs_to and has_many relationship.
Model ab.rb
class Ab < ActiveRecord::Base
has_many:cds
end
Model cd.rb
class Cd < ActiveRecord::Base
belongs_to :ab
end
ab and cd have 2 columns each called title and Dscr.Do I have to create a join table (ab_cd_join_table)
I want to display a particular group and its sub-groups in a view.
The controller for the view
class DisplayController < ApplicationController
def index
#ab = Ab.find_by_title("XXXXXX")
#cds = #ab.cds
for cd in #cds
logger.info cd.title
end
I am using this in the view.
display view
<%= #ab.title %>
I don't know how to display the title and Dscr of different sub-groups belonging to the group = "XXXXXX"
Thanks in advance
What I think you're asking for is this in the view:
<% #ab.cds.each do |cd| %>
<h1><%= cd.title %></h1>
<p><%= cd.description %></p>
<% end %>
and this in the controller:
#ab = Ab.find_by_title("XXXXXX")
That way you will display all cds for the ab-model matching "XXXXXX".
Update:
For belongs_to and has_many to work the model with belongs_to needs to have a column for the one that has has_many. In this case Cd needs to have a column named ab_id.
rails g migration add_ab_id_to_cds ab_id:integer
cd.ab_id needs to be the id of the corresponding Ab model.
cds = Cd.where(<something>)
cds.each do |cd|
cd.ab_id = #ab.id
cd.save
end
This maybe should be set upon creation of a Cd object, but just to test it out you can do like this.
Do I have to create a join table (ab_cd_join_table)
No in this case you don't need a join table, instead you need to add
ab_id column in cds table.(the foreign key column should be present in
table of model defining belongs_to assiciation)
Displaying subgroup title in view
<% #ab.cds.each |sub_group| %>
<%= sub_group.title -%>
<%= sub_group.description -%>
<%end%>
Also, if you alwas need sub_groups with group then load them in one query by using include option in find like
#ab = Ab.find_by_title("XXXXXX",:include=> :cds)
now you don't need to calculate cds explicitly just use the view code mentioned above

Best practice: How to split up associations-functions in controllers with equal-access models

I have 2 equal-access models: Users and Categories
Each of these should have the standard-actions: index, new, create, edit, update and destroy
But where do I integrate the associations, when I want to create an association between this two models?
Do I have to write 2 times nearly the same code:
class UsersController << ApplicationController
# blabla
def addCategory
User.find(params[:id]).categories << Category.find(params[:user_id])
end
end
class CategoriessController << ApplicationController
# blabla
def addUser
Category.find(params[:id]).users << User.find(params[:user_id])
end
end
Or should I create a new Controller, named UsersCategoriesController?
Whats the best practice here? The above example doens't look very DRY.... And a new controller is a little bit too much, I think?
Thanks!
EDIT:
I need to have both of these associations-adding-functions, because f.e.
#on the
show_category_path(1)
# I want to see all assigned users (with possibility to assign new users)
and
#on the
show_user_path(1)
#I want to see all assigned categories (with possibility to assign new categories)
EDIT:
I'm taking about a HBTM relationship.
If you have a situation where you need to do this with has_and_belongs_to_many, you could take the approach you are currently using, or you could build this into your existing update actions.
When you add a habtm relationship, you will get an additional method on your classes...
class User < ActiveRecord::Base
has_and_belongs_to_many :categories
end
With this, you can do this:
user = User.find(params[:id])
user.category_ids = [1,3,4,7,10]
user.save
The categories with those ids will be set. If you name your form fields appropriately, the update can take care of this for you if you want to use checkboxes or multiselect controls.
If you need to add them one at a time, then the methods you've built in your original post are reasonable enough. If you think the repetition you have is a code smell, you are correct - this is why you should use the approach I outlined in my previous answer - an additional model and an additional controller.
You didn't mention if you are using has_and_belongs_to_many or if you are using has_many :through. I recommend has_many :through, which forces you to use an actual model for the join, something like UserCategory or Categorization something like that. Then you just make a new controller to handle creation of that.
You will want to pass the user and category as parameters to the create action of this controller.
Your form...
<% form_tag categorizations_path(:category_id => #category.id), :method => :post do %>
<%=text_field_tag "user_id" %>
<%=submit_tag "Add user" %>
<% end %>
Your controller...
class CategorizationsController < ApplicationController
def create
if Categorization.add_user_to_category(params[:user_id], params[:category_id])
...
end
end
then your categorization class...
class Categorization
belongs_to :user
belongs_to :category
def self.add_user_to_category(user_id, category_id)
# might want to validate that this user and category exist somehow
Categorization.new(:user_id => user_id, :category_id => category_id)
Categorization.save
end
end
The problem comes in when you want to send the users back, but that's not terribly hard - detect where they came from and send them back there. Or put the return page into a hidden field on your form.
Hope that helps.

Not sure where (model or controller) to define my find method

(Warning: Clueless Rails Newbie!)
In my show.html.erb for my albums view, I call a public method in my albums controller:
<% albums_feature = find_albums_with_feature(feature.id) %>
It generates a NoMethodError.
So I copied the method into my Album model and tried calling it from the view as:
<% albums_feature = Album.find_albums_with_feature(feature.id) %>
But this also gets a NoMethodError.
Where should I define this method?
For what it's worth, the method looks like this:
def find_albums_with_feature(feature_id)
albums_for_feature = Albums.find_by_sql(
["select al.* from albums al, albums_features alfe
where al.id = alfe.album_id
and alfe.feature_id = ?", feature_id])
end
If you want to have method that is accesible from view, you have few options:
put it in the model
put it in the helper
put it in the controller and add a line "helper_method :find_albums_with_feature"
But I think you can do it better. Firstly, don't put any finding methods in view. Put it in the controller. Secondly, you don't need to specify your own finding method. Probably you have something like this in your models:
class Album << ActiveRecord::Base
has_many :albums_features
has_many :features, :through => :albums_features
end
class AlbumsFeature << ActiveRecord::Base
belongs_to :album
belongs_to :feature
end
class Feature << ActiveRecord::Base
has_many :albums_features
has_many :albums, :through => :albums_features
end
With it, you can find albums with specific feature like this:
#feature = Feature.find(id)
#albums = #feature.albums
or
#albums = Feature.find(id).albums
and it should be in your controller. In view you should only display results.
If you are looking for more informations about associations, take a look here: http://guides.rubyonrails.org/association_basics.html. I think it's the best place where you can learn about Rails - at least it is for me.
In the Album model. Needs self in front though:
def self.find_albums_with_feature(feature_id)

Resources