【问题标题】:Rails 6: How can I run validations on an ActiveStorage item outside of the model?Rails 6:如何在模型外的 ActiveStorage 项目上运行验证?
【发布时间】:2020-05-07 16:39:58
【问题描述】:

我有一个模型Profile,它有一个附件image

class Profile < ApplicationRecord
  belongs_to :user, dependent: :destroy

  has_one_attached :image
  validates :image,
            content_type: [:gif, :png, :jpg, :jpeg],
            size: { less_than: 2.megabytes , message: 'must be less than 2MB in size' }
  after_initialize :set_default_image

  has_many :gallery_items, dependent: :destroy
  accepts_nested_attributes_for :gallery_items, allow_destroy: true

  validates :name, :short_description, :slug, presence: true
  #...

除了图像验证(我使用的是active storage validation gem)之外,Profile 还会验证nameshort_descriptionslug 的存在。

自从实现了这个,我有了一个新的需求。我现在想允许用户将图像与其他属性分开提交。

我不想更改底层模型。因此,我想引入一个单独的控制器和类来处理一个单独提交图像的表单。

我尝试了两种方法。

首先,我有一个简单的类来处理这个问题:

class ProfileImageFormSubmission
  def initialize(record, params)
    @params = params
    @record = record
  end

  def save
    record.update_attribute(:image, image_param)
  end

  private

  attr_accessor :params, :record

  def image_param
    params.fetch(record_param).require(:image)
  end

  def record_param
    record.class.name.underscore.to_sym
  end
end

它会像这样在我的控制器中调用:

class ProfileImagesController < ApplicationController
  before_action :require_login

  def update
    @profile = Profile.find_by(user_id: current_user.id)

    if ProfileImageFormSubmission.new(@profile, params).save
      #...

问题是,图像未经过验证,因此无论如何都会附加到配置文件中。 (我使用 #update_attribute 是因为我想跳过对其他属性的验证——当字段没有呈现给用户时,显示 name 列的错误是没有意义的。)

我还尝试通过在模型之外运行验证来解决问题。但在这里,我很难理解如何将 ActiveStorage 与一个普通的旧 Ruby 对象集成。

class ProfileImageFormSubmission
  include ActiveModel::Model
  include ActiveStorage
  include ActiveStorageValidations

  attr_accessor :record, :params
  has_one_attached :image

  validates :image,
          content_type: [:gif, :png, :jpg, :jpeg],
          size: { less_than: 2.megabytes , message: 'must be less than 2MB in size' }

  def initialize(record, params)
    @params = params
    @record = record
  end

  def save
    binding.pry
    record.update_attribute(:image, image_param)

    if record.invalid?
      record.restore_attributes
      return false
    end
  end

  # This model is not backed by a table
  def persisted?
    false
  end

  private

  def image_param
    params.fetch(record_name).require(:image)
  end

  def object_name
    record.class.name.underscore.to_sym
  end
end

我什至无法实例化上述类,因为它失败并出现以下错误:

NoMethodError (undefined method `has_one_attached' for ProfileImageFormSubmission:Class):

单独验证活动存储项目的最佳方法是什么?

是否可以在单个列上运行验证而不会触发其他验证错误?

是否可以在ApplicationRecord 模型之外使用活动存储项?

【问题讨论】:

    标签: ruby-on-rails rails-activestorage ruby-on-rails-6


    【解决方案1】:

    你不能只用一个模型来做到这一点。您需要使用活动存储验证创建第二个“图像”或“附件”类,然后才能首先创建图像。

    即使可能将所有内容都保存在一个模型中,跳过验证也会导致数据不一致。将其分开将确保您的数据库中的每条记录都是有效的,并且您不会遇到意外状态(例如没有名称的配置文件,即使您正在“验证”它的存在)。

    所以它看起来像这样:

    class Image < ApplicationRecord
      (...)
    
      has_one_attached :file
      validates :file,
                content_type: [:gif, :png, :jpg, :jpeg],
                size: { less_than: 2.megabytes , message: 'must be less than 2MB in size' }
    
      (...)
    end
    
    class Profile < ApplicationRecord
      (...)
    
      has_one :image # or belongs to
    
      (...)
    end
    

    【讨论】:

      【解决方案2】:

      试试这个

      class ProfileImageFormSubmission
      
        def initialize(record, params)
          @params = params
          @record = record
        end
      
        def save
          record.assign_attributes(image: image_param)
          record.valid?
          record.errors.to_hash.except(:image).each { |k,_| record.errors.delete(k) } # removing errors for other attributes
      
          return false if record.errors.any?
      
          record.save(validate: false)
        end
        ...
      

      如果Profile 不是User 的强制要求。然后您可能希望将图像移动到 User 类。

      【讨论】:

      • 我正朝着这个方向前进。这对我有用。不过,我做了一点改动。我已经将record.errors.to_h.except(:image).each { |k,_| record.errors.delete(k) } 移到条件之前,这样即使图像保存成功,也不会报告其他错误。条件现在是record.errors.none?。再想一想,如果这个类与Profile 同时使用,它可能会掩盖有意义的错误。 …在视图/控制器中剥离消息可能会更好。
      • 这个类只负责保存图片到Profile,其他的你应该使用Profile或者创建类似的类如果Profile不适合
      猜你喜欢
      • 2012-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-09
      • 2019-03-15
      • 2012-03-05
      • 1970-01-01
      相关资源
      最近更新 更多