has_many delete associations on update - ruby-on-rails-3

I think this one is pretty simple… But i don´t get it. I have a has_many-relationship between two models (combination and canvas_price):
combination.rb:
class Combination < ActiveRecord::Base
has_many :canvas_prices, :dependent => :delete_all
accepts_nested_attributes_for :canvas_prices,
:allow_destroy => true,
:reject_if => lambda { |a| a[:name].blank? }
end
canvas_price.rb:
class CanvasPrice < ActiveRecord::Base
belongs_to :combination
end
In my form i have links which delete inputs in the nested CanvasPrice-form via JavaScript.
My problem is that when i delete one or more of these fields and update the combination, the associated CanvasPrices aren´t deleted, though the CanvasPrices which i want to delete are NOT in my params. How do i achieve this?

Ok, pretty simple… It was as easy as adding a _delete-param with help of a hidden input, e. g. combination[canvas_prices][0][_delete] = true.
I thought this behaviour could be configured directly in the has_many or accepts_nested_attributes_for method.

Related

has_many through issue with rails 3.2 and acts_as_paranoid

I am facing a weird behaviour. This is my scenario.
class Message < ActiveRecord::Base
belongs_to :user, :foreign_key => "from_user_id"
has_many :message_recipients, :include => [:user], :dependent => :destroy
has_many :recipients, :through => :message_recipients, :source => :user
end
class MessageRecipient < ActiveRecord::Base
belongs_to :user
belongs_to :message, :include => :message_recipients
end
class User < ActiveRecord::Base
acts_as_paranoid
has_many :message_recipients
end
I am creating a new message and pushing value to its recipients.
#message=Message.new(:body => "Hi",:from_user_id => session[:user])
#message.recipients.push(User.find(params[:message_recipient_id]))
#message.save
The above operation saves the record in message correctly but fails to trigger the message_recipient record. But if i perform the above code removing the acts_as_paranoid gem, then it works fine. Is there any work around to solve this issue?
Solution 1:
Instead of calling new on Message model we can use create on Message model. So the record will be created and then i can push the data inside the recipients.Its like creating parent record and using its id i am creating child record. So there is no need for trigerring and it works fine.
Suggestions are welcome.
It works fine with rails 3.2.12 but fails with rails 3.2.13. I am confused with this behaviour.

How to call a scope across a has_many / through relationship

I have the following (on RoR 3.1 and MySQL 5.1):
class Menu < ActiveRecord::Base
has_many :menu_headers
has_many :menu_items, :through => :menu_headers
belongs_to :location
end
class MenuHeader < ActiveRecord::Base
acts_as_tree :parent_id
has_many :menu_items
belongs_to :menu
end
class MenuItem < ActiveRecord::Base
scope :is_enabled, where(:is_enabled => true)
belongs_to :menu_header
end
I'd like to be able to call the scope across the relationship; something like this:
# call the scope :is_enabled here
Menu.find(12).(where menu_items.is_enabled)
but not sure how to do this.
I'd like the behavior for:
Menu.find(12)
to continue to pull menu_items where is_enabled=false
Any ideas on how to do this?
thx
edit #1
added the act_as_tree and location associations as these also need to be working.
Something like this Scope with join on :has_many :through association might work but seems a little bit ugly
This should do the trick:
Menu.find(12).menu_items.is_enabled
It will return all enabled menuitem associated with the menu with id 12.

How to validate uniqueness of nested models in the scope of their parent model in Rails 3.2?

Here is an example of my problem.
I have a 'Room' model:
class Room < ActiveRecord::Base
has_many :items, :inverse_of => :room
accepts_nested_attributes_for :items
end
And I have an 'Item' model:
class Item < ActiveRecord::Base
belongs_to :room, :inverse_of => :items
validates :some_attr, :uniqueness => { :scope => :room}
end
I want to validate the uniqueness of the :some_attr attribute of all the Items which belongs to a certain room.
When I try to validate the items, I get this error:
TypeError (Cannot visit Room)
I cannot set the scope of the validation to be :room_id since the items are not saved yet so the id is nil. I also want to prevent using custom validators in the 'Room' model.
Is there any clean way to do it in Rails? I also wonder if I set the :inverse_of option correctly...
I don't see anything wrong with how you're using inverse_of.
As for the problem, in a similar situation I ended up forcing a uniqueness constraint in a migration, like so
add_index :items, [ :room_id, :some_attr ], :unique => true
This is in addition to the AR-level validation
validates_uniqueness_of :some_attr, :scope => :room_id
(I'm not sure if it's valid to use the association name as a scope, won't the DB adapter raise an exception when trying to refer to the non-existent room column in a query?)

Many to many relationship with metadata stored in the mapping table in activerecord

I have two tables with a many to many relationship, through a third table. In the third table is a piece of data I need to assign when I build the relationships between the two tables, how can I use ActiveRecords build method to assign that?
Here is code to show what I mean:
class Company < Contact
has_many :contact_companies
has_many :people, :through => :contact_companies
accepts_nested_attributes_for :people, :allow_destroy => true
accepts_nested_attributes_for :contact_companies
end
class Person < Contact
has_many :contact_companies
has_many :companies, :through => :contact_companies
accepts_nested_attributes_for :companies, :allow_destroy => true
accepts_nested_attributes_for :contact_companies
end
class ContactCompany < ActiveRecord::Base
belongs_to :person
belongs_to :company
end
ContactCompany contains a data member called "position". What I want to do is something like:
c = Person.new
c.companies.build(:name => Faker::Company.name, :position => positions.sample)
EDIT:
When I try the code above I get "unknown attribute: position".
The c.companies.build line is attempting to build a Company object which does not have the position attribute (the ContactCompany does) hence the error. It looks like you are trying to set attributes on two different models, so you'll have to make sure you are setting the appropriate attribute on the right model:
# you can chain these calls but I separated them for readability
cc = c.contact_companies.build(:position => positions.sample)
cc.build_company(:name => Faker::Company.name)

ActiveRecord Find with condition based on associated record having a value in a list

With the following Rails models:
class Group < ActiveRecord::Base
has_many :group_locations, :dependent => :restrict
has_many :locations, :through => :group_locations, :dependent => :restrict
end
class Location < ActiveRecord::Base
has_many :group_locations, :dependent => :destroy
has_many :groups, :through => :group_locations
end
class GroupLocation < ActiveRecord::Base
belongs_to :group
belongs_to :location
end
I am trying to Find all of the locations that are associated with at least one of several groups contained in the string "group_list" (e.g. "1,2,3,4,5"). If it was a field from the Location record, I would specify a condition of "*field in (#{group_list})*". But how do I accomplish my goal when I want to have at least one of the location's "group_location" whose "group_id" is in the list (or, alternatively, one "group" whose group_id is in the list).
I know how to do it with pure SQL, but how do you do it with Rails?
#taro You are right. Started by adding the code
joins(:group_locations).where("group_id in (?)", group_id_array)
Then I proceeded to define a scope just to make it a nice package:
scope :locations_in_groups, lambda { |grparray| joins(:group_locations).where("group_id in (?)", grparray) }
Thanks for your help.