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

时间:2018-11-02 22:23:25

标签: ruby-on-rails ruby validation activerecord

在带有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

5 个答案:

答案 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_atnil的汽车才能更新

    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_atnil的{​​{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