即使设置了错误,Rails Record也会得到保存

时间:2019-01-09 07:19:18

标签: ruby-on-rails ruby

这是我的凭证模型:

class Credential < ApplicationRecord
  validate :password_or_certificate
  enum credential_type: { windows: 1, linux: 2 }

  def password_or_certificate
    unless user_pass.blank? ^ cert_file_path.blank?
      errors.add(:base, "Please provide a password or a certificate, not both.")
    end
  end
end

我正在检查控制器中的证书,并在控制器中设置错误,如下所示:

def create
    attachment = params[:credential][:cert_file_path]
    cert_file_path = Rails.root.join('private', 'certificates', attachment.original_filename) if attachment.present?

    @credential = Credential.new(credential_params)
    @credential.cert_file_path = cert_file_path

    if @credential.valid? && cert_file_path.present?
      cert_error_msg = 'Certificate is not valid'
      fm = FileMagic.new(FileMagic::MAGIC_MIME)
      file_path = attachment.path
      if fm.file(file_path) =~ /^text\//
        puts first_line = File.open(attachment.path) { |f| f.readline }
        if first_line.include? '-----BEGIN RSA PRIVATE KEY-----'
          File.open(cert_file_path, 'w') { |f| f.write(attachment.read) }
        else
          @credential.errors.add(:cert_file_path, cert_error_msg)
        end
      else
        @credential.errors.add(:cert_file_path, cert_error_msg)
      end
    end

    respond_to do |format|
      if @credential.save
        format.html { redirect_to credentials_url, notice: 'Credential was successfully mapped.' }
        format.js
        format.json { render :show, status: :created, location: @credential }
      else
        format.html { render :new }
        format.js
        format.json { render json: @credential.errors, status: :unprocessable_entity }
      end
    end
  end

即使设置了错误,记录也会被保存。

#<ActiveModel::Errors:0x0000557781babde8 @base=#<Credential id: nil, alias: "My new credential", user_name: "raj", encrypted_user_pass: "", encrypted_user_pass_iv: "VrT0xsxYtf//cwVx\n", credential_type: "linux", created_at: nil, updated_at: nil, cert_file_path: "/home/rmishra/awsapp/private/certificates/Ruby Enc...", passphrase: "">, @messages={:cert_file_path=>["Certificate is not valid"]}, @details={:cert_file_path=>[{:error=>"Certificate is not valid"}]}>

我知道我可以检查@credential.errors.blank?然后保存它,你们可以帮我把所有这些逻辑放入模型中吗?

4 个答案:

答案 0 :(得分:11)

我认为您希望ActiveRecord不保存记录,如果您手动设置错误。

但是不是。

ActiveRecord上的save命令开始其验证,当其中之一不合适时,AR设置错误并在save上返回false。

在您的代码中,没有cert_file_path字段的验证,因此非常适合保存。

正确的方法是将cert_file_path的验证从控制器移动到Credential,在save事件上,它将调用该验证,如果不合适,则将设置相应的错误,并在false上返回save

答案 1 :(得分:10)

您可以创建一个custom validation method将该逻辑移至模型,并创建虚拟属性以获取对附件对象的访问权限

模型

class Credential < ApplicationRecord
  validate :password_or_certificate
  enum credential_type: { windows: 1, linux: 2 }

  # create a virtual attribute to store attachment
  attr_accessor :attachment

  # Custom validation method on ActiveRecord object creation
  validate :create_certificate, on: :create

  def create_certificate
    if cert_file_path.present?
      cert_error_msg = 'Certificate is not valid'
      fm = FileMagic.new(FileMagic::MAGIC_MIME)
      file_path = attachment.path
      if fm.file(file_path) =~ /^text\//
        puts first_line = File.open(attachment.path) {|f| f.readline}
        if first_line.include? '-----BEGIN RSA PRIVATE KEY-----'
          File.open(cert_file_path, 'w') {|f| f.write(attachment.read)}
        else
          errors.add(:cert_file_path, cert_error_msg)
        end
      else
        errors.add(:cert_file_path, cert_error_msg)
      end
    else
      errors.add(:cert_file_path, cert_error_msg)
    end
  end

  def password_or_certificate
    unless user_pass.blank? ^ cert_file_path.blank?
      errors.add(:base, "Please provide a password or a certificate, not both.")
    end
  end
end

控制器

def create
  attachment = params[:credential][:cert_file_path]
  cert_file_path = Rails.root.join('private', 'certificates', attachment.original_filename) if attachment.present?

  @credential = Credential.new(credential_params)
  @credential.cert_file_path = cert_file_path
  @credential.attachment = attachment

  respond_to do |format|
    if @credential.save
      format.html {redirect_to credentials_url, notice: 'Credential was successfully mapped.'}
      format.js
      format.json {render :show, status: :created, location: @credential}
    else
      format.html {render :new}
      format.js
      format.json {render json: @credential.errors, status: :unprocessable_entity}
    end
  end
end

答案 2 :(得分:2)

Rails保存记录时,它将调用内部验证方法,该方法将清除您在运行模型验证代码之前设置的错误消息。因此在验证之外添加错误消息不会阻止保存记录。就您而言,写成

if @credential.errors.empty? && @credential.save

但是我强烈建议您在验证器中编写验证逻辑

答案 3 :(得分:-1)

检查credential_type的值,可能没有设置为windowslinux