首页 > 解决方案 > 即使设置了错误,Rails Record 也会被保存

问题描述

这是我的凭证模型:

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?然后保存它,你们能帮我把所有这些逻辑放在我的模型中吗?

标签: ruby-on-railsruby

解决方案


您可以创建自定义验证方法以将该逻辑移动到您的模型中,并创建虚拟属性以获取对附件对象的访问权限

模型

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

推荐阅读