Rails 3 - Active_admin select nested object in many to many - ruby

I have two models. Deals and Stores.
I want to add already created Deals when creating a Store.
I want to add already created Sores when creating a Deal.
I am trying to use f.has_many, but I can't make it working.
My relationship is built using has_and_belongs_to_many :deals and has_and_belongs_to_many :stores (in the models)
My store custom form has the following:
f.inputs "Deals" do
f.has_many :deals do |deal|
deal.input :id, :as => :select, :include_blank => false
end
end
I don't know how to add deals to the store.
Any help?

Just checking, but do you have an
accepts_nested_attributes_for :deals #, :allow_destroy => true
declaration in your Store model, and an
accepts_nested_attributes_for :stores
declaration in your Deals model?
You may want to check these two pages:
http://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for
https://github.com/justinfrench/formtastic (search in the page for accepts_nested_attributes_for)

Related

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.

How to prevent multiple DB roundtrips for parents of has_many relations

I'm likely missing something here, but it seems like this is a performance oversight. I first noticed this when looking at the query logs of Errbit and noticing hundreds of queries for the same objects.
It seems that all the children of a has_many relation don't have a reference back to their parent object after being loaded through the relation. i.e. accessing parent.children.map &:parent will get the parent from the DB once for each child instead of being setup with the in-memory copy of parent
Example:
Using a very simple belongs_to / has_many setup:
class Person
include Mongoid::Document
field :name
has_many :posts, :inverse_of => :person
end
class Post
include Mongoid::Document
field :text
belongs_to :person, :inverse_of => :posts
end
Then, in the Rails console, a simple demonstration:
Loading development environment (Rails 3.2.12)
[1] pry(main)> tom = Person.create(:name => 'Tom')
[2] pry(main)> tom.posts.create(:text => 'stuff')
[3] pry(main)> tom.posts.create(:text => 'other stuff')
[4] pry(main)> Person.first.posts.map {|post| post.person.object_id}
=> [50687740, 50719060]
Note that last line, each person reference points to a different ruby object. I'm using ruby's object_id attribute to highlight the fact that these are literally different objects and this requires two round-trips to the database.
Why isn't the parent relation just a reference to the parent object after loading through the has_many relation?
Turns out that this is a known deficiency and adding this feature is planned for version 4.0.
In the mean time, you can drastically reduce duplicated queries and improve performance by enabling the Mongoid Identity Map

Querying and sorting embedded document in mongoid

I have three classes
class Post
include Mongoid::Document
include Mongoid::Timestamps
belongs_to :user, :inverse_of => nil
embeds_many :comments, :as => :commentable
field :content, :type => String
end
class Commment
include Mongoid::Document
include Mongoid::Timestamps
belongs_to :user, :inverse_of => nil
embedded_in :commentable, :polymoriphic => true
end
class User
has_many :posts, :dependent => :destroy
field :name, :type => String
end
Whenever the user creates a new comment, I want to compare the contents of it with the latest comment that the user has made. Here is my code that fetches the latest comment by the user:
comments_user=[]
Post.where("comments.user_id" => user.id).
only(:comments).
each {|p| comments_user += p.comments.where(:user_id => user.id).to_a}
latest_comment = comments_user.sort_by{|comment| comment[:updated_at]}.reverse.first
The above code gives me the result but the approach taken is not efficient as I have to traverse through all the posts that the user has commmented to find the latest comment. If any, can anyone provide me a more efficient solution to this problem?
Simply speaking, Isn't there any way I can get all the comments made by this user?
This should fetch the latest user`s comment:
Post.where("comments.user_id" => user.id).order_by(:'comments.updated_at'.desc).limit(1).only(:comments).first
This is standard problem with embedding. It greatly improves some queries ("load post with all its comments"), but makes others non-efficient/impractical ("find latest comment of a user").
I see two options here:
Keep embedding and duplicate data. That is, when user makes a comment, embed this comment to a post document and to the user document. This data duplication has its drawbacks, of course (what if you need to edit comments?);
Stop embedding and start referencing. This means that comment is now a top level entity. You can't quickly load a post with comments, because there are no joins. But other queries are faster now, and there's no data duplication.

activerecord validation - validates_associated

I'm unclear on what this method actually does or when to use it.
Lets say I have these models:
Person < ...
# id, name
has_many :phone_numbers
end
PhoneNumber < ...
# id, number
belongs_to :person
validates_length_of :number, :in => 9..12
end
When I create phone numbers for a person like this:
#person = Person.find(1)
#person.phone_numbers.build(:number => "123456")
#person.phone_numbers.build(:number => "12346789012")
#person.save
The save fails because the first number wasn't valid. This is a good thing, to me. But what I don't understand is if its already validating the associated records what is the function validates_associated?
You can do has_many :phone_numbers, validate: false and the validation you're seeing wouldn't happen.
Why use validates_associated then? You might want to do validates_associated :phone_numbers, on: :create and skip validation on update (e.g. if there was already bad data in your db and you didn't want to hassle existing users about it).
There are other scenarios. has_one according to docs is validate: false by default. So you need validates_associated to change that.

Approaches to sub categories of polymorphic associations in Rails

Hello Everyone,
I have a polymorphic association currently in use with a few different models. Each model has it's own video file associated with it, so it uses a "videoable" polymorphic association. I recently found the need however, to create a new model that has 2 separate types of videos. I will let the code do the talking.
#current setup
class Video < ActiveRecord::Base
belongs_to :videoable, :polymorphic => true
end
class Project < ActiveRecord::Base
has_one :video, :as => videoable
end
# New model I am working on
class Assignment < ActiveRecord::Base
has_one :video_draft
has_one :video_final
end
Ideally the assignment model would have two special types of video objects while still using the polymorphic association. I have considered single table inheritance, but I am not sure that is the best approach here. What are my options? I do not want to create a video_draft model and a video_final model because in the end, they are just video attachments like everything else. The only difference is that they are specialized video attachments that need their own unique reference.
What you need to do is to tell ActiveRecord what model are you referring to when declaring :video_draft instead of :video like in Project model. This can be done by passing :class_name option to has_one
Keeping this in mind, this should work fine:
class Assignment < ActiveRecord::Base
has_one :video_draft, :as => :videoable, :class_name => "Video"
has_one :video_final, :as => :videoable, :class_name => "Video"
end
See rails guides at section 4.2.2.3 for more info about need for passing :class_name.

Resources