问题
I have a model that has a number of has_many, and has_many :through model relationships. For example, in my User class I have:
has_many :languages, through: :profile_languages
What I would like is to be able to detect when these are added or removed using the 'User.changes' function, which returns an array of attributes that have been changed when called with the User.language_ids= function.
Has anyone else tried to do this, or have experience with this?
Info on the ActiveModel changes function: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
Edit: As requested, here is what I am doing.
After a user's attributes are assigned and before it is saved, I am looking at all of the values returned from the .changes, in order to log all the changes made in an external log.
So if I call u.name = "new name"
then u.changes returns {name: ['old name', 'new name']}
However, when I pass a user a bunch of language ids, such as
u.language_ids = [4,5]
Then there are a number of ProfileLanguage models created, and the u.changes hash is left blank.
I am attempting to create some callbacks in the ProfileLanguage model that would manually create some sort of a hash, but I am wondering if this is indeed the best solution.
回答1:
My somewhat dirty solution I am going with now is to add callbacks to the has_many function:
has_many :languages, through: :profile_languages,
:after_add => :language_add,
:before_remove => :language_remove
And adding this info to a custom hash that will be checked on the saving of a profile when I am looking at the .changes function.
回答2:
I had the same problem, I was trying to check if a new relationship was created or deleted when updating a model.
I tried using model.relationship.any? { |a| a.changed? }
but this only detects changes of alredy existing objects, so it didnt work on creation and deletion of relationships.
Searching for a solution, I found this very brief article which solves our problem: link
Using model.select { |a| a.new_record? || e.marked_for_destruction? }.any?
I've been able to get all records that are being created or destroyed.
Combining this with a.changed?
I can get every changes in my relationship.
回答3:
I know that you are looking to implement a text based log of changes, but I would recommend looking into complete object versioning through the paper_trail
gem as a method to achieve what you want. It provides this functionality according to their README:
PaperTrail can restore three types of associations: Has-One, Has-Many, and Has-Many-Through. In order to do this, you will need to create a version_associations table, either at installation time with the rails generate paper_trail:install --with-associations option or manually. PaperTrail will store in that table additional information to correlate versions of the association and versions of the model when the associated record is changed.
I haven't used the has-many-through feature of paper_trail
, but I have used it for objects without associations, and found it excellent and easy to implement.
To make a text file log as well as this database paper trail you could use paper_trail's diff features in an after_save
callback.
来源:https://stackoverflow.com/questions/13300384/detecting-changes-in-a-rails-has-many-through-relationship