Rails - Changing association doesn't mark obje

2020-03-26 07:51发布

Edit: Clarifications added

Given the following two classes

class Foo < ActiveRecord::Base
  belongs_to :bar
end

class Bar < ActiveRecord::Base
end

When I change a bar on a foo, I would like a way to see that the foo instance has changed:

foo = Foo.find(x)
foo.bar      # returns #<Bar id:1>
foo.bar = Bar.new
foo.changed? # returns false
foo.changes  # returns an empty hash

I don't want to set bar_id as dirty (because it hasn't changed) It would be nice to have the following:

foo.changed? # returns true
foo.changes  # returns {:bar => [#<Bar id: 1>, nil]}

Is there a clever way to hack this into Rails? Is there a reason this is not part of Rails?

3条回答
▲ chillily
2楼-- · 2020-03-26 08:13

Due to changes to @changed_attributes in Rails 5.1, I had to resort to using a virtual attribute (attr_accessor) to indicate the changed association after checks. This might be better in the long run compared to messing with Rails internals related to ActiveRecord::Dirty

查看更多
The star\"
3楼-- · 2020-03-26 08:31

What version of Rails are you using? When I tested it, it does report true.

Anyway, I am not sure if an after assign callback is available for belongs_to yet. There was a ticket for this https://github.com/rails/rails/issues/586

If not, you can just override the assign method (I haven't tried this before, but I think it will work :) so please let me know)

class Foo < ActiveRecord::Base
  belongs_to :bar

  def bar=(b)
    bar_id_will_change! unless bar_id == b.id
    super        
  end

end

Update: Just read your question again, and I am not even sure if I understood all of it. You want it to still report true if it hasn't changed? Even when assigning the same object again right? in which case you can remove the unless bar_id == b.id part

查看更多
等我变得足够好
4楼-- · 2020-03-26 08:37

I faced the same problem today and after running through the rails-source i found a workaround that should do what you had in mind, if i got your question the right way.

Transferred to your example the code should look like that:

class Foo < ActiveRecord::Base
  belongs_to :bar

  def info
    bar.info
  end

  def info=(val)
    @changed_attributes[:info] = self.info unless val == self.info
    self.time_entry.update_attributes! :info => val
  end
end

class Bar < ActiveRecord::Base
  attr_accessible :info
end

This way you can call .changed? and .changes on objects of the Foo-class.

hope this helps even if the answer comes a bit late :)

kind regards christian

btw. sorry for the poor english. i am working on that as much as on my ruby-skills ;)

查看更多
登录 后发表回答