Rails populate collection_select - ruby

I am trying to auto populate a select box in rails with a relational table.
Venture_Users has a list of Users and Venture ID's so I want to find all Users under a specific Venture and then display the users via user_id, and user.name.
I tried the following
<%= f.collection_select(:user_id, VentureUser.find_all_by_venture_id(#venture.id), :user_id, :name) %>
However the last attribute :name doesn't work because it is not directly in my results and I need to run a query on my user table to get the name of the user.'
Essentially what I need to but don't know how to do is modify my VentureUsers.find_all statement to join attributes from my user table.
Thanks,
Mike

This should be quite straight forward as long as you have your associations set correctly. If I have understood your scenario correctly then you should probably have it set up like this:
class Venture < ActiveRecord::Base
has_many :venture_users
has_many :users, :through => :venture_users
end
class VentureUser < ActiveRecord::Base
belongs_to :venture
belongs_to :user
end
If your associations indeed look like this, then you should be able to create the select like this:
<%= f.collection_select(:user_id, #venture.users, :id, :name) %>

Related

Rails join on 2 tables

I have 3 tables which are
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
end
Now I want to retrieve all the comments of a particular user.
I have tried it using find_by_sql like
Comment.find_by_sql "SELECT c.* FROM comments c, posts p, users u WHERE c.post_id = p.id and p.user_id = u.id and u.id=6"
This works fine. But I want few details from "posts" table along with comments.
Does anyone have idea how to do that?
Corrected answer..
Now that I've properly understood the scenario, a portion of my previous answer still makes sense with one addition:
Alter the User model like so:
class User < ActiveRecord::Base
has_many :posts
has_many :comments, through: :posts
end
This type of relationship will perform a join between comments a posts, where the user_id of posts is the current user. Quite literally, like you said in the comments, "the connection between User and Comment [is] through Post".
Similar to what I said before, the center of the universe in this scenario is the user object. With this addition, to grab all the user's comments would simply be:
user.comments
If you look at the log, the SQL output is:
SELECT `comments`.* FROM `comments` INNER JOIN `posts` ON `comments`.`post_id` = `posts`.`id` WHERE `posts`.`user_id` = 1
Which is similar to your original query.
Now that you retrieved the comments for this user, you can simply access the Post model data through the belongs_to :post relationship, as normal.
In the end, unless you have some special reason for trying to do everything in pure SQL, this approach is more readable, maintainable, and the "Rails way". Plus it saves yourself a lot of hassle and typing.
More information on has_many ... through.

Dropdown Filter for Polymorphic association nested_attribute

I'm building an application in which the user can create a Company and add Investments made to that company. The investments can come from two sources user's Funds or companies' Coinvestors. Funds are big deal in the application as the user can do a bunch of stuff in them. The Coinvestors are not that important, but I want to have control over few aspects of so I created a Model just for them. For that I created a Polymorphic association for which I gave the [terrible] name of Investables. I'm running Rails 3.2.15 and Ruby 2.0.0. Models are below:
class Company < ActiveRecord::Base
has_many :investments
accepts_nested_attributes_for :investments
end
class Investment < ActiveRecord::Base
belongs_to :fund, :class_name => "Fund", :foreign_key => 'investable_id'
belongs_to :company, inverse_of: :investments
belongs_to :coinvestor, :class_name => "Coinvestor", :foreign_key => 'investable_id'
end
class Fund < ActiveRecord::Base
has_many :investments, :as => :investable, :dependent => :destroy
end
class Coinvestor < ActiveRecord::Base
has_many :investments, :as => :investable, :dependent => :destroy
end
When editing the company I want to be able to add investments and I want to dynamically add form lines for each new investment. I was able to achieve that following the awesome 165-Edit Multiple Revised.
To make it more complex, I also want to add a Dropdown for choosing the Polymorphic Type so it filters the next Dropdown to show only the names of either the Funds or the Coinvestors.
For that I mostly adapted code from Railscast 88-Dynamic Select Menus(Thanks Ryan!!)
/views/company/edit.html.erb
<%= form_for(#company) do |f| %>
...
<%= f.fields_for :investments do |builder| %>
<%= render 'investment_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Investment", f, :investments, 'table' %>
/views/company/_investment_fields.html.erb
<%= f.select :investable_type , [ "Fund", "Coinvestor" ], {prompt: "Investor Type"} %>
<%= f.grouped_collection_select( :investable_id, investables_to_collection, :investables, :name, :id, :name, {prompt: "Investor"} ) %>
The "investable_to_collection" is a helper I built to aggregate objects from both Funds and Coinvestors models.
module CompaniesHelper
InvestableCollection = Struct.new(:name, :investables)
CollectionItem = Struct.new(:name, :id)
def investables_to_collection
a = Array.new
a << InvestableCollection.new('Fund', Fund.all.map { |item| CollectionItem.new(item.name, item.id )})
a << InvestableCollection.new('Coinvestor', Coinvestor.all.map { |item| CollectionItem.new(item.name, item.id )})
a
end
end
I didn't add any JavaScript yet to filter the dropdown which will be another challenge. But I've got my beautiful view to show data I've already got in the DB. But the dropdown that should show the Fund's or Coinvestor's name is mixing up things: it will show the name of Coinvestor with ID == 1 even if the investment was made by a Fund.
I thought of making one of the models to have a custom ID such as f1, f2, f3 ... instead of 1, 2, 3... so the system wouldn't mix them. But it seems that it would generate other big compatibility issues.
Do you guys have any other idea?
I wouldn't alter the id column (i.e. I wouldn't change it from an auto-incrementing integer), but because you're mixing two lists in the DB into a single list in the UI you will need some way to note which table each item in the UI came from. For that, sure, use whatever ID scheme you want and then use that differentiating ID as the way to lookup the relevant item.
Also, if you're not going to use the auto-incrementing id columns in the UI (for links or whatever), then you could also just remove them and replace them with your custom identification scheme. There's no reason, for example, that you couldn't assign them all a random 8-digit number, ensuring that number is unique across the different types you're going to put into the list, and then using that id in the UI. It seems that the real issue you're running into is how to combine the lists where ids might overlap, and perhaps it may be feasible for you to devise a way to assign a non-overlapping ID.
Another possibility for assigning non-overlapping IDs, without having to come up with your own scheme or checking for uniqueness across multiple tables is to use a UUID for the lookup ID in the UI drop-down.

Can't see why I'm getting undefined method form_for

I don't get why I'm getting this error
undefined method `sector_id' for #<Portfolio:0x007fe17c2e3848>
I have a Portfolio Model and a Sector model, they look like so
class Portfolio < ActiveRecord::Base
belongs_to :sector
attr_accessible :overview, :title, :sector_id
end
class Sector < ActiveRecord::Base
has_many :portfolios
attr_accessible :name
end
My routes
resources :portfolios do
resources :sectors
end
So within my form to create a new portfolio I have this collection_select
<%= f.label :sector_id, "Choose Sector", :class => 'title_label' %><br>
<%= f.collection_select(:sector_id, Sector.all, :id, :name, :prompt => "Please Select a Sector") %>
This is something I've done many times before and it has worked, can anyone see why I would be getting this error?
The only thing I can think of is that I have called my controller for portfolio as Portfolios, I always get mixed up with plural and singular controller names, would this make a difference in my case?
Maybe you have not run the migration yet that adds the column "sector_id" in your table "portfolios". If you are using MySQL connect to your database and check the table (show create table portfolios;). Use appropriate method to get this info from your database server if you are using other rdbms. Alternatively, in your rails console (rails c) type in Portofolio and see what attributes it prints out. Does it include sector_id?

Rails: has_one association error?

community, I need your help. It's a quite simple problem, but I can't figure out what's wrong.
I have two models, a Product model and a Condition model. A Product can have only one condition, so I set a one-to-many association between the two. Condition contains fixed records (e.g. good, bad, damaged)
Product.rb
attr_accessible :condition_id
has_one :condition
Condition.rb
belongs_to :product
I have a foreign key condition_id in my products table.
In my products form, I loop through the conditions and set the id:
_form.html.erb(Product)
<%= f.select :condition_id, Condition.all.collect {|x| [x.name, x.id]}, {} %>
I can see that the id is set in the console, when I submit the form. But I can't retrieve the value of the given condition.
In my product show page, I try
<%= #product.condition.name %>
but it gives me a "undefined method `name' for nil:NilClass" error. This is also happening when trying in the console.
What am I missing here?
Thank you all!
Sorry, for answering my own question.
It seems that I mixed-matched the association.
So a Product belongs_to :condition and a Condition has_many :products works fine.
However, for me Product has_one :condition and Condition belongs_to :product sounds much more verbose.

Rails get related items through two different relationships

I have a "two middleman" model setup as shown below:
User
has_many :comments
has_many :ratings
Comment
belongs_to :user
belongs_to :movie
Rating
belongs_to :user
belongs_to :movie
Movie
has_many :comments
has_many :ratings
Whats the best way to get all Movies that a User is associated with (either commented on or rated)?
I'd like to be able to call User.get_movies(user_id) and get back an ActiveRecord::Relation object so that it's chainable (i.e. User.get_movies(user_id).limit(3).order(...)). This returns a regular old array, and I suspect I'm hitting the database way more than I need to be.
def self.get_movies(user_id)
user = self.where(:id => user_id).includes({:comments => :movie}, {:ratings => :movie})
movies = []
user.comments.each do |comment|
movies.push(comment.movie)
end
user.ratings.each do |rating|
movies.push(rating.movie)
end
movies.uniq!
end
def movies
Movie.includes(:ratings, :comments).where("`ratings`.user_id = ? OR `comments`.user_id = ?", self.id, self.id)
end
Untested, but I'm pretty sure using a joins instead of includes also works.

Resources