Запись Rails сохраняется, даже если были установлены ошибки - PullRequest
0 голосов
/ 09 января 2019

Вот моя учетная модель:

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 голосов
/ 13 января 2019

Вы можете создать пользовательский метод проверки , чтобы переместить эту логику в вашу модель, и создать виртуальный атрибут, чтобы получить доступ к объекту вложения

Модель

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
0 голосов
/ 09 января 2019

Когда Rails сохраняет запись, он вызывает внутренний метод проверки, метод очищает сообщение об ошибке, которое вы установили, перед запуском кода проверки модели. поэтому добавление сообщения об ошибке вне проверки не может помешать сохранению записи. В вашем случае напишите это

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

Но я настоятельно советую вам написать логику валидации в валидаторах

0 голосов
/ 12 января 2019

Я думаю, вы ожидали, что ActiveRecord не сохранит запись, если вы вручную установили ошибку.

Но это не так.

Команда

ActiveRecord on save начинает свои проверки, когда один из них не подходит, тогда AR устанавливает ошибку и возвращает false на save

В вашем коде нет проверки для поля cert_file_path, поэтому оно идеально подходит для сохранения.

Правильный способ - переместить валидацию cert_file_path из контроллера в Credential, в случае save она вызовет эту валидацию, если она не соответствует валидации, она установит соответствующую ошибку и возвратит false на save.

0 голосов
/ 09 января 2019

Проверьте значение credential_type, возможно, оно не было установлено на windows или linux

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...