在带有activerecord的Rails 5中,仅当属性当前为nil时更新属性的首选方式是什么。
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
答案 0 :(得分:1)
您只需检查#nil?
car = Car.first
car.update_attribute(:connected_at, Time.zone.now) if car.connected_at.nil?
这还不够通用。我想要诸如before_validation等之类的东西。我只是不确定哪种方式是首选方式。
好吧,如果您想进行验证,它将看起来像这样。
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
如您所见,更多的检查,更多的方法。我肯定会去第一个。
但是,如果要添加错误消息,那就是这种方式
errors.add(:connected_at, 'Already present!')
因此,在before_save方法中所有定义的attrs上始终都可以使用“#{attr} _was”吗?
一般来说,它们不仅在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'
答案 1 :(得分:0)
听起来您是在说要修改特定属性的工作方式,以便它悄悄地忽略您。我认为为什么要关闭此功能的本能是合理的,但是如果您再想一想,您可能会认为,如果您在很多地方都做这种事情,那么使用对象就会变得特别令人困惑。给其他不太了解代码的人。
也许您想这样做,因为还有其他代码使用Car模型来建立连接,但实际上并没有完整的图片,因此它尝试了您只想第一次成功的东西。最好仅在具有全景图的类(例如Car模型或服务对象)内部处理此类操作。
如果您仍然真的想在Car外部控制此“连接”行为,则可以在Car类中完全覆盖attr_writer。不过,我绝对建议您改为在before_save回调中执行此操作。
def connected_at=(new_value)
if @connected_at
raise StandardError, 'connected_at has already been set'
end
@connected_at = new_value
end
无论您尝试分配值的哪种方式,它都将起作用。如果您想知道上面发生了什么,请阅读有关ruby中attr_accessor的信息。
答案 2 :(得分:0)
这是我对您问题的理解。
只有connected_at
为nil
的汽车才能更新
class Car < ApplicationRecord
before_save :updatable?
def updatable?
connected_at.blank?
end
end
点是在before_save时返回false
。
答案 3 :(得分:0)
您可以:
car = Car.first
car.connected_at ||= Time.zone.now
car.save
仅当connected_at
是nil
的{{1}}时分配。
答案 4 :(得分:0)
我建议使用before_update
回调,并将OP的意图重新表述为“如果我的属性已经具有值,则放弃更新”。
我想出了这个解决方案(该解决方案与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
<attribute_name>_changed?
和clear_attribute_change
方法来自ActiveModel::Dirty。