Rails:确定关联的属性是否刚刚更改

Rails: Determine if attribute on association just changed

提问人:duhaime 提问时间:7/6/2023 最后编辑:duhaime 更新时间:7/6/2023 访问量:72

问:

我们有一个模型。该模型具有:Zoohas_manyAnimalZoo

after_commit :update_animals

def update_animals
  animals.each(&:do_update_animal)
end

该模型(其中 a )又具有:Animalbelongs_toZoo

def do_update_animal
  # here we need to determine whether the `self.zoo.zookeeper` attribute just updated
end

在块内部,我们如何判断与动物关联的 Zoo 实例上的属性是否刚刚更新?Animal#do_update_animalzookeeper


铌:为了让它工作,我需要更改我在 Zoo 上的块,以表明 Animal 是 Zoo——如果没有该声明,脏 API 就无法工作。所以在动物园里,我需要:has_manyinverse_of

has_many :animals, inverse_of: :zoo
Ruby-on-Rails ActiveRecord Ruby-On-Rails-5 关联

评论


答:

1赞 Les Nightingill 7/6/2023 #1

试试这个:

# zoo.rb
after_commit :update_animals

def update_animals
  animals.each { |animal|
    animal.do_update_animal
  }
end

# animal.rb
include ZookeeperConcern

# zookeeper_concern.rb
def do_update_animal
  if zoo.zookeeper_previously_changed? # presumably animal belongs_to :zoo, right?
    # do something
  else
    # do something else
  end
end

这使用 ActiveModel::D irty 功能。

评论

0赞 duhaime 7/6/2023
这是个好主意,但我认为它在我们的案例中行不通。在我们的实际案例中,实际上是在许多模型上共享的关注点中提供的,并调用在每个模型上定义的方法,我们需要在方法中检查 Zoo 是否脏。因此,最好能够从 Animal 运行一种方法来直接识别 Zoo 是否脏/更改,而不必通过方法链向下传递状态。这可能吗?do_update_animaldo_update_animalAnimal
0赞 Les Nightingill 7/6/2023
你真的想确定它是否发生了变化(正如你所指出的),还是与动物相关的特定动物园实例......即 (似乎更有可能,但我对你的应用程序一无所知!Zoo.zookeeperanimal.zoo.zookeeper
0赞 duhaime 7/6/2023
是的,你说得很对,我们想知道与当前 Animal 实例关联的 Zoo 实例上的属性是否发生了变化。我们希望能够理想地从 Animal 实例内部确定这一点(我知道我们可以从 Zoo 内部确定这一点,并按照您的方式传递它,但这会让事情变得有点丑陋)zookeeper
0赞 Les Nightingill 7/6/2023
好的,我更新了我的建议。与最初的建议相比,我对此更新的确定性较低。很想听听你的发现。
0赞 Les Nightingill 7/6/2023
我刚刚检查了...它似乎按照您要求的方式工作......你觉得怎么样?
1赞 Jibran Usman 7/6/2023 #2

我认为,如果您可以将after_commit替换为around_save和around_destroy,那么这种解决方法可能是一个很好的解决方案

动物园.rb:

around_save :update_animals
around_destroy :update_animals

def update_animals
  zookeeper_changed = zookeeper_changed?
  yield

  animals.each { |animal|
    animal.do_update_animal(zookeeper_changed)
  }
end

Animal.rb:

def do_update_animal(zookeeper_changed = false)
  return unless zookeeper_changed

  # do something only if zookeeper changed
end