简体   繁体   English

rails 5 仅在当前为 nil 时才更新属性

[英]rails 5 update attribute only if it's currently nil

What is the preferred way in Rails 5 with activerecord to update the attribute only if it is currently nil. Rails 5 中使用 activerecord 更新属性的首选方式是什么,仅当它当前为零时。

car = Car.first
car.connected_at = Time.zone.now
car.save

OR

car = Car.first
car.update!(connected_at: Time.zone.now)


it should update only if car.connected_at is nil

You can simply check for #nil?你可以简单地检查#nil?

car = Car.first
car.update_attribute(:connected_at, Time.zone.now) if car.connected_at.nil?

That's not generic enough.这还不够通用。 I want something like before_validation etc. I am just not sure which way is the preferred one.我想要 before_validation 之类的东西。我只是不确定哪种方式是首选。

Well if you want to go for validation, it would look something like this..好吧,如果你想进行验证,它看起来像这样..

before_save :validate_connected_at

private

def validate_connected_at
  connected_at = connected_at_was if connected_at_changed? && connected_at_was.present?
end

OR或者

before_save :set_connected_at

private

def set_connected_at
  connected_at = Time.zone.now if connected_at.nil?
end

As you can see, more checks, more methods.如您所见,更多检查,更多方法。 I would definitely go for the first one.我肯定会去第一个。

However, if you want to add error message, then this is the way但是,如果您想添加错误消息,那么这就是方法

errors.add(:connected_at, 'Already present!')

So "#{attr}_was" is always available on all the defined attrs in before_save method?那么“#{attr}_was”在 before_save 方法中所有定义的属性上总是可用的?

They are available in general and not only in before_save , eg in the console..它们通常可用,而不仅仅是在before_save ,例如在控制台中..

car = Car.first
car.connected_at
=> 'some value'
car.connected_at = 'some other value'
car.connected_at
=> 'some other value'
car.connected_at_was
=> 'some value'

It sounds like you're saying you want to modify the behaviour of how a particular attribute works so it quietly ignores you.听起来您是在说您想修改特定属性的工作方式,以便它悄悄地忽略您。 I think the instinct behind why you want to seal this off is reasonable one but if you think about it a bit more you might consider that if you do this kind of thing in a lot of places then using your objects will start to become confusing particularly for someone else who doesn't know the code well.我认为你为什么要封闭它的本能是合理的,但如果你多想一想,你可能会认为如果你在很多地方做这种事情,那么使用你的对象会开始变得特别混乱对于不太了解代码的其他人。

Perhaps you want to do this because there's other code using the Car model that wants to make connections but doesn't really have the full picture so it tries stuff which you only want to succeed the first time.也许您想这样做是因为还有其他使用 Car 模型的代码想要建立连接,但并没有真正拥有完整的画面,因此它会尝试您只想在第一次成功的东西。 It's much better to handle such operations solely inside a class which does have the full picture such as the Car model or a service object.仅在具有完整图片的类(例如 Car 模型或服务对象)中处理此类操作要好得多。

If you still really want to control this "connecting" behaviour outside the Car then you can override the attr_writer completely in the Car class.如果您仍然真的想控制 Car 之外的这种“连接”行为,那么您可以在 Car 类中完全覆盖 attr_writer。 I'd definitely recommend doing this on before_save callback instead though.不过,我绝对建议在 before_save 回调上执行此操作。

def connected_at=(new_value)
  if @connected_at
    raise StandardError, 'connected_at has already been set'
  end
  @connected_at = new_value
end

That will work whichever way you try to assign the value.无论您尝试分配值的方式如何,这都将起作用。 If you're wondering about what's going on above have a read about attr_accessor in ruby.如果您想知道上面发生了什么,请阅读有关 ruby​​ 中的 attr_accessor 的内容。

this is my understanding of your question.这是我对你的问题的理解。

  • Car can update only if connected_at is nil仅当connected_at nil汽车才能更新

    class Car < ApplicationRecord before_save :updatable? def updatable? connected_at.blank? end end

The point is return false when before_save.重点是 before_save 时返回false

You could:你可以:

car = Car.first
car.connected_at ||= Time.zone.now
car.save

That will only assign if connected_at is nil of false .如果将只分配connected_atnilfalse

I would propose to use the before_update callback and rephrase the intention of the OP as "discard updates if my attribute already has a value".我建议使用before_update回调并将 OP 的意图重新before_update为“如果我的属性已经有值,则丢弃更新”。

I came up with this solution (which works well with mass assignments such as Car.update(car_params) ):我想出了这个解决方案(它适用于大量分配,例如Car.update(car_params) ):

before_update :ignore_updates_to_connected_at

def ignore_updates_to_connected_at
  return unless connected_at.present? && connected_at_changed?

  clear_attribute_change(:connected_at)
end

The <attribute_name>_changed? <attribute_name>_changed? and clear_attribute_change methods come from ActiveModel::Dirty .clear_attribute_change方法来自ActiveModel::Dirty

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM