Rails 6: Как я могу запустить проверки для элемента ActiveStorage за пределами модели? - PullRequest
0 голосов
/ 21 января 2020

У меня есть модель 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
  #...

В дополнение к проверке изображения (я использую камень проверки активной памяти ), Profile проверяет наличие name, short_description и slug.

С момента реализации этого у меня появилось новое требование в представлении. Теперь я хочу разрешить пользователю отправлять изображение отдельно от других атрибутов.

Я не хочу изменять базовую модель. Поэтому я хочу представить отдельный контроллер и класс для обработки формы только для представления изображения.

Я пробовал два способа.

Во-первых, у меня был простой класс чтобы справиться с этим:

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, потому что хотел пропустить проверки других атрибутов - не имеет смысла отображать ошибку для столбца имени, когда поле не представлено пользователю.)

Я также попытался решить проблему, выполнив проверки вне модели. Но здесь я изо всех сил пытаюсь понять, как интегрировать 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?

Ответы [ 2 ]

1 голос
/ 21 января 2020

попробуйте это

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.

0 голосов
/ 21 января 2020

Вы не можете сделать это только с одной моделью. Вам нужно будет создать второй класс «Image» или «Attachment» с активной проверкой хранилища, а затем вы сможете сначала создать образ.

Даже если, вероятно, возможно сохранить все просто в Одна модель, пропуская проверку приведет к несогласованности данных. Хранение его отдельно обеспечит правильность каждой записи в вашей БД, и вы не попадете в непредвиденные состояния (например, профили без имени, даже если вы «проверяете» ее наличие).

Так бы это выглядело так:

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
...