【问题标题】:Rails validate only the param keys passed into the update methodRails 仅验证传递给更新方法的参数键
【发布时间】:2015-01-26 16:24:36
【问题描述】:

我知道 rails 条件验证。但是,我想知道是否有一种简单的方法可以说:“如果该字段包含在传递给 update 方法的 params 哈希中,请验证它。否则,请不要打扰。”

例如,假设我有一个人有一个name 和一个age,这两个都是必填字段。假设我做p.update({name: 'Bob'})。然后,我只尝试更新名称。我不希望它打扰在年龄字段上运行验证(如果这样做,保存将失败 - 但此时我并不关心年龄)。

我意识到我可以根据具体情况使用条件验证来获得这种效果。但是有没有更通用的方法?我原以为只验证传递给update 的属性是一个常见的要求……我做错了什么吗?

【问题讨论】:

    标签: ruby-on-rails validation ruby-on-rails-4


    【解决方案1】:

    您需要明确说明您要验证的每个属性是否可以接受 nil:

    validates :age, numericality: { only_integer: true }, allow_nil: true
    

    【讨论】:

      【解决方案2】:

      您说您知道条件验证,所以我假设您的意思是您知道可以执行以下操作但不认为可以接受:

      validates :age ... , if: :age_changed?
      

      AFAIK 没有其他“更通用”的方式来获取您所追求的东西。但是在回答您的最后一个问题时,我认为您正在以错误的方式思考这个问题。验证是围绕断言模型状态的想法构建的,而不是对其状态所做的更改。这是一个微妙的区别,但它确实是你正在处理的问题的症结所在。

      我猜你希望是这样的:

      p.update({ name: 'Bob'}, validate_only_changed_attributes: true) # not real
      

      ... 但请考虑 (1) 验证可能由依赖于多个属性或根本不依赖于属性的函数构建,并且 (2) 您的模型可能包含不以任何直接方式映射的“虚拟属性”到数据库属性。因此,在实践中,很难以您想要的方式约束任意一组验证。

      【讨论】:

      • 谢谢,这些都是有效的积分。推特上有人提出了一个我很满意的解决方案 - 我已将其放在下面的答案中。
      • 不错。我假设你已经看过this。我仍然对您如何处理验证感到好奇。
      • 没见过 - 谢谢。如果您观看 railscast,验证实际上非常简单 - 我将在我的答案中发布一些示例代码。
      • 同样,如果您使用改革 gem,验证也很容易 - 与在您的模型中进行验证非常相似。
      【解决方案3】:

      我在 twitter 上得到了一个我很满意的解决方案 - 不是我的问题本身的答案,而是一个更好的架构:

      “我建议使用专门的表单/服务类,只需要你需要的验证。” - https://twitter.com/joshuapaling/status/538169606876057600

      有一个很棒的关于表单对象的 railscast:http://railscasts.com/episodes/416-form-objects(还有一个关于服务对象 http://railscasts.com/episodes/398-service-objects)。

      还有改革的宝石 - https://github.com/apotonick/reform 看起来不错,不过我决定这次不去。

      如果你还没有订阅 railscast(去订阅吧!这太值得了,而且只需一次 9 美元的费用,无限期地,而 Ryan Bates 正在休息),这里有一些示例代码,取自 RailsCasts 剧集:

      class SignupForm
        include ActiveModel::Model
        # ^ do this and validations and stuff mostly just work - see lines below
      
        validates_presence_of :username
        validate :verify_unique_username
        validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/
        validates_length_of :password, minimum: 6
      
        delegate :username, :email, :password, :password_confirmation, to: :user
        delegate :twitter_name, :github_name, :bio, to: :profile
      
        def user
          @user ||= User.new
        end
      
        def profile
          @profile ||= user.build_profile
        end
      
        def submit(params)
          user.attributes = params.slice(:username, :email, :password, :password_confirmation)
          profile.attributes = params.slice(:twitter_name, :github_name, :bio)
          self.subscribed = params[:subscribed]
          if valid?
            generate_token
            user.save!
            profile.save!
            true
          else
            false
          end
        end
      
        def subscribed
          user.subscribed_at
        end
      
        def subscribed=(checkbox)
          user.subscribed_at = Time.zone.now if checkbox == "1"
        end
      
        def generate_token
          begin
            user.token = SecureRandom.hex
          end while User.exists?(token: user.token)
        end
      
        def verify_unique_username
          if User.exists? username: username
            errors.add :username, "has already been taken"
          end
        end
      end
      

      【讨论】:

        猜你喜欢
        • 2013-01-25
        • 1970-01-01
        • 2017-03-26
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多