destroy vs destroy_all - ruby-on-rails-3

I need to know when to use :dependent => :destroy_all and when to use :dependent => :destroy
What happen if my model has_many child models, and I used :dependent => :destroy ? will it destroy only the first child model ?
is this line of code wrong:
has_many books, :dependent => :destroy
shall it be like this:
has_many books, :dependent => :destroy_all
?

This will destroy all books. All of them.
has_many books, :dependent => :destroy
An important thing to remember is that :dependent => :destroy will cause the calling of the #destroy method in each and every one of the associated Books. By calling #destroy on each Book, any before_destroy or after_destroy callback will be executed for each Book.
Naturally, if you have a LOT of dependent books, this process could be expensive.
The :destroy_all is invalid, maybe you were thinking about :delete_all. The difference with :delete_all (instead of just :destroy) is that Rails will issue a single SQL statement to delete all dependent book records. No #destroy method will be called on any Book record, and no before_destroy or after_destroy callback will be executed.
The upside is that a single SQL statement is many times more efficient to delete records from the database than calling #destroy on each one of them.
This is very important to know. If you have any *_destroy callbacks on the Book model, you should be aware that defining :dependent => :delete_all will have the effect of ignoring any callbacks you defined on the Book model.

I'm pretty sure the first line is correct and the second line is incorrect.
Here's a link to the specific section in the documentation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many-label-Options
My understanding is that :dependent will loop through the associations and call the given function which means :destroy is the correct one to call. (:destroy_all is only valid for collections.)
I hope that helps.

Related

Rails Dependent Update On Associated Record

Given two associated models in rails (4.0),
class User < ActiveRecord::Base
has_one :subscription, dependent: :destroy
end
class Subscription < ActiveRecord::Base
belongs_to :user
end
The above code will ensure that when an instance of User is destroyed, its associated record will be, too.
So far so good.
My question is, is it possible to equally easily invoke a dependent update as well, so that every time User is updated, Subscription will be updated as well?
This could look like this:
class User < ActiveRecord::Base
has_one :subscription, dependent: [:update, :destroy]
end
So that when User gets updated successfully, the associated Subscription will re-save, thus invoking its update filters (i.e. before_save, before_update, after_save, after_update).
Is there an elegant way to do this? If not, what is the closest way to cleanly get to this?
Thank you!
Try this,
has_one :subscription, :dependent => destroy, :autosave => true
For more details see the documentation http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html

Rails Belongs to with different column names

I am developing some voting software where a user gets to vote for 3 different entries. For the votes model I was thinking of something like:
Votes:
Email Address - String
Verification Code - String
Verified - Boolean
First - Entry
Second - Entry
Third - Entry
Where Entry is another model in my rails application. How could I have a belongs to relationship for this? As i'd like to run queries like get the entries with the highest number of votes for first place. Is this the best approach to this problem?
User Model:
has_many :votes, :dependent => :destroy
has_many :entries, :through => :votes
Entry Model
has_many :votes, :dependent => :destroy
has_many :users, :through => :votes
Vote Model
belongs_to :user
belongs_to :entry
I would design the Vote object such that it contains just a user_id and an entry_id. Then your application logic would enforce that if they've already voted three times, they don't get to vote again. That way, when you query for the highest number of votes, you don't have to write special cases for the first, second and third entries.
As far as the belongs_to declaration, I would say that a vote belongs_to both User and Entry, and should be destroyed when either dependent object is destroyed, thus:
class Vote < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
belongs_to :entry, :dependent => :destroy
end

Unique Association :through

I have a many to many :through relationship between a set of classes like so:
class Company
has_many :shares
has_many :users, :through => :shares, :uniq => true
end
class User
has_many :shares
has_many :companys, :through => :shares, uniq => true
end
class Share
belongs_to :company
belongs_to :user
end
I want to ensure a unique relationship so that a user can only have one share in any one company, which is what I have tried to achieve using the "uniq" argument.
At first I thought this was working, however it seems the behaviour os the "uniq" is to filter on the SELECT of the record, not pre-INSERT so I still get duplicate records in the database, which becomes an issue if I want to start dealing with the :shares association directly, as calling user.shares will return duplicate records if they exist.
Can anyone help with an approach which would force truely uniq relationships? so that if I try adding the second relationships between a user and a company it will reject it and only keep the original?
Have you tried adding this to your Share class?
validates_uniqueness_of :user, scope: :company
Also, in your User class I think it should be:
has_many :companies, through: :shares
I hope that helps.

Duplicate records when using update_attributes() and a has_many :through association

I can't figure out why it's generating duplicate recruit_profiles_skills instead of updating.
class RecruitProfile < ActiveRecord::Base
has_many :skills, :through => :recruit_profiles_skills
has_many :recruit_profiles_skills, :dependent => :destroy
accepts_nested_attributes_for :recruit_profiles_skills, :allow_destroy => true
class Skill < ActiveRecord::Base
has_many :recruit_profiles, :through => :recruit_profiles_skills
has_many :recruit_profiles_skills, :dependent => :destroy
Params looks like
"recruit_profile"=>{
"recruit_profiles_skills_attributes"=>[{"skill_id"=>"1", "level"=>"15"}]
}
Then I do
def update
#recruit_profile.update_attributes(params[:recruit_profile])
But, this creates duplicate association records. Why does this not simply update!?
I can prevent the duplicates using validations, but then it never updates since it just wants to create a new record, but the new record is invalid because it fails the validation.
The way I solved this problem in code was to:
Include the 'id' of the row in the association table in every attribute that has already been created. This allows for the updating to work as expected.
Use a checkbox on skill_id attribute. Thus, if the checkbox is not checked, the skill_id will not show up in the params hash. Then, I run this bit of code
params[:recruit_profile][:recruit_profiles_skills_attributes].map{ |rps|
if rps[:skill_id].nil? then rps[:_destroy] = 1 end
}
That bit of code will check to see if the :skill_id is set. If it is not set, then the row needs to be deleted. The way to delete entries, even if :allow_destroy is set to true, is to append a ":_destroy => 1" key=>value to the hash. Thus, the :id will be present, and a :_destroy will be present, so update_attributes will delete it.
Doing the above will allow for create (:id not present, but :skill_id present), update (:id present and :skill_id present) and destroy (:id present, but :skill_id not present). IMHO, that isn't how it should work, but the job gets done with only 1 extra line of code (split in to 3 lines because of length).
(NOTE: replace skill_id with whatever other parameter is also in your association table. This round about way is only needed if you're using association tables with multiple attributes. Otherwise, the classic collection_ids = [#,#,#] still works with has_many :through associations.)

HABTM Polymorphic Relationship

I'm pretty new to Rails, and i'm trying to do a polymorphic HABTM relationship. The problem is that I have three models that I want to relate.
The first one is the Event model and then are two kind of attendees: Users and Contacts.
What I want to do is to be able to relate as an attendee both users and contacts. So, what i have right now in my code is:
Event Model
has_and_belongs_to_many :attendees, :polymorphic => true
User Model
has_and_belongs_to_many :events, :as => :attendees
Contact Model
has_and_belongs_to_may :events, :as => :attendees
How the HABTM table migration needs to be? I'm a little confused and i have found no help on that.
Is it going to work?
No, you can't do that, there's no such thing as a polymorphic has_and_belongs_to_many association.
What you can do is create a middle model. It would probably be something like this:
class Subscription < ActiveRecord::Base
belongs_to :attendee, :polymorphic => true
belongs_to :event
end
class Event < ActiveRecord::Base
has_many :subscriptions
end
class User < ActiveRecord::Base
has_many :subscriptions, :as => :attendee
has_many :events, :through => :subscriptions
end
class Contact < ActiveRecord::Base
has_many :subscriptions, :as => :attendee
has_many :events, :through => :subscriptions
end
This way the Subscription model behaves like the link table in a N:N relationship but allows you to have the polymorphic behavior to the Event.
Resolveu parcialmente.
It does solve the problem given the framework that we have at our disposal, but it adds "unnecessary" complexity and code. By creating an intermediary model (which I will call B), and given A -> B -> C being "A has_many B's which has_many C's", we have another AR Model which will load one more AR class implementation into memory once it is loaded, and will instantiate for the sole purpose of reaching C instances. You can always say, if you use the :through association, you don't load the B association, but then you'll be left with an even more obsolete model, which will only be there to see the caravan pass by.
In fact, this might be a feature that is missing from Active Record. I would propose it as a feature to add, since it has been cause of concern for myself (that's how I landed in this post hoping to find a solution :) ).
Cumprimentos