Fetching data using one-many association - ruby-on-rails

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

Related

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

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 %>

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 %>

Rails: undefined method from a loop with no record?

I'm looping through a table to get some values, and then I'm trying to loop another table to get more items:
My Business table looks like this
id name
1 Business 1
2 Business 2
Business Photo table:
id default_pic business_id
1 blahblah.jpg 1
So if I'm trying to loop:
<% #b.each do |b| %>
<%= b.name %>
<%= b.business_photos.default_pic %>
<% end %>
I get undefined method defaul_pic? I believe because there's no more record after the second loop when its getting Business 2. Whats the rails way to check record association so I don't get this error?
This is how my models look:
class Business < ActiveRecord::Base
has_many :business_photos
end
class BusinessPhoto < ActiveRecord::Base
belongs_to :business
end
<%= b.business_photos.default_pic if defined?(b.business_photos.default_pic) %>
This is a conditional if statement we use each time we want to include a variable & are unsure of whether it's set. I've been looking for something which can prevent the error from showing through a .each (would take out so much logic from views), but I am yet to find one

Display a list of user's posts in Rails

I have a rails app where users post reviews of albums. The reviews that are posted are called "pins" and are uploaded with a release date, album title, artist name, album cover image, and rank for the year. (as in my #1 favorite album of the year) The pins belong users and the users have many pins. What I want to do is create a new page for 2013 that displays each user, then lists a descending ordered list of the album image, title, artist as their top ten list for the year. Something like:
<% #users.each do |user| %>
<%= link_to (image_tag user.image(:small)), user %> <%= user.name %>
<ol>
<li><%= #pin.album %> - <% #pin.artist%></li>
</ol>
<% end %>
I need to limit the pins to only :date => "2013" and I need to list them in descending order.
I'm having trouble figuring out the controller and view. Here is my page controller so far:
def tens2013
#users = User.all
#pin = Pin.where(:date => "2013")
end
How should I set my controller to be able to call <%= #pin.user.album %> ?
The way I see it, your models should be set up like this:
class User < ActiveRecord::Base
has_many :pins
end
class Pin < ActiveRecord::Base
belongs_to :user
belongs_to :album
end
class Album < ActiveRecord::Base
has_many :pins
end
Pin should have user_id and album_id to achieve this.
Then in the controller, you can eager load all users and their pins, with each pin's respective album, like this:
#users = User.includes(:pins => :album)
Or to limit to a certain year, do:
#users = User.includes(:pins => :album).where('pins.date>=?','2013-01-01').references(:pins)
Now you can iterate through the users in your view, and through each user's pins, and each pin's album.
You don't need to use #pin for each user's pin. Make the necessary changes in your view, and iterate through them using this style:
#users.each do |user|
# do something
user.pins.each do |pin|
# Now you have "pin" and you can use it:
# pin.album...
# pin.artist...
end
end
To call #pin.user.album, you need to define the dependency first at the model level. So in the Pin model, you should have belongs_to :users and in the User model, you should include has_many :pins. Now this will assume that in the Pin model, there is a field called user_id, which will be the foreign key. Also use :include in the queries when you are going to access dependent classes like this. It avoids the N+1 query problem. eg:
#pin = Pin.includes(:users).where("date >= ?","2013-01-01")
To limit responses to only the year 2013, you may want to search your query likewise:
#pin = Pin.where("date >= ?","2013-01-01")

Rails assignment table with third column

I currently have a model for an assignment table in Rails 3, which looks as follows (there are, of course, sale and financeCompany models also):
class SaleFinanceCompany < ActiveRecord::Base
attr_accessible :sale_id, :financeCompany_id, :financedBalance
belongs_to :sale
belongs_to :financeCompany
end
My question is simple: how can I set up the sale/financeCompany models so that I can access the associated financedBalance?
For example, in my view I would like to have:
<% for financeCo in sale.financeCompanies %>
<%= "£" + financeCo.financedBalance + " from "%>
<%= financeCo.name %>
<% end %>
That unfortunately does not work, with the error being the financedBalance part. The only way I could see to set up my finance company model would be with a
has_many :financedBalances, :through => :saleFinanceCompanies
but this will give me several financedBalances for each sale, but I need one (each financedBalance is tied to both a sale and finance company in the assignment table, so doing sale.financedBalances.where etc. would seem unnecessary when I should be able to do sale.financeCompany.financedBalance).
Any suggestions?
Rails treats join tables a bit differently than you might think. From a DBA perspective, your join table is perfectly fine but for Rails true join tables have only referential columns. As soon as you add a new column, Rails likes to treat the join table as a new entity.
(On a personal note, I was frustrated by this at first but I quickly learned it's not a big deal)
So, to fix your problem, you'll need to rename your table, let's say FinanceBalances. Also, let's change financedBalance to amount.
Then, in your Sale.rb file put you associations like so:
has_many :financeBalances
has_many :financeCompanies, :through => :financeBalances
Do the same for FinanceCompany.
And your code will look like:
<% for financeBalance in sale.financeBalances %>
<%= "£" + financeBalance.amount + " from " %>
<%= financeBalance.financeCompany.name %>
<% end %>
If you really really want financeCompany.financedBalance to work, you can define a method in your financeCompany model and write the query that returns what you want.

Resources