我目前正在做一个使用Rails 5.2.6的项目。(这是一个相当大的项目,我不能更新rails版本)
我们使用ActiveAdmin来处理管理部分,我有一个模型,我用ActiveStorage保存一个标志。
最近,我需要对徽标属性执行验证。(档案格式、大小及比例)。为此,我一直在寻找多种解决方案,包括ActiveStorageValidations gem。
这个为我提供了一半的解决方案,因为验证器按预期工作,但是即使验证器失败,徽标也会被存储并关联到模型。(我被重定向到编辑表单与不同的错误显示在徽标字段,但仍然会更新徽标)。这显然是一个已知的问题,从ActiveStorage据称在Rails 6中修复,但我无法更新项目。(和ActiveStorageValidations不想做任何关于它以及根据我在GitHub上发现的问题)
最后,我设法"手工"制作了一个可行的解决方案,使用一些before_actions对图像进行必要的检查,并在某些检查失败之前再次呈现编辑表单。
我还在这个过程中向我的模型添加了一些错误,以便在呈现active admin的编辑视图时,错误正确地显示在表单和徽标字段的顶部。
下面是(admin/mymodel.rb) 后面的代码controller do
before_action :prevent_save_if_invalid_logo, only: [:create, :update]
private
# Active Storage Validations display error messages but still attaches the file and persist the model
# That's a known issue, which is solved in Rails 6
# This is a workaround to make it work for our use case
def prevent_save_if_invalid_logo
return unless params[:my_model][:logo]
file = params[:my_model][:logo]
return if valid_logo_file_format(file) && valid_logo_aspect_ratio(file) && valid_logo_file_size(file)
if @my_model.errors.any?
render 'edit'
end
end
def valid_logo_aspect_ratio(file)
width, height = IO.read(file.tempfile.path)[0x10..0x18].unpack('NN')
valid = (2.to_f / 1).round(3) == (width.to_f / height).round(3) ? true : false
@my_model.errors[:logo] << "Aspect ratio must be 2 x 1" unless valid
valid
end
def valid_logo_file_size(file)
valid = File.size(file.tempfile) < 200.kilobytes ? true : false
@my_model.errors[:logo] << "File size must be < 200 kb" unless valid
valid
end
def valid_logo_file_format(file)
content_type = file.present? && file.content_type
@my_model.errors[:logo] << "File must be a PNG" unless content_type
content_type == "image/png" ? true : content_type
end
end
这种方法可以很好地工作,但现在我的问题是,如果其他表单上的错误发生在同一时间比标志错误(必填字段或其他东西),那么就没有得到验证,错误并不像这个呈现编辑视图的显示会发生其他验证。
我的问题是,我是否有任何方法在这个级别手动触发我的模型上的验证,以便每个其他字段都得到验证和@my_model。Errors填充正确的错误,从而使表单能够显示所有表单错误,无论是否涉及徽标。
像这样:
...
def prevent_save_if_invalid_logo
return unless params[:my_model][:logo]
file = params[:my_model][:logo]
return if valid_logo_file_format(file) && valid_logo_aspect_ratio(file) && valid_logo_file_size(file)
if @my_model.errors.any?
# VALIDATE WHOLE FORM SO OTHER ERRORS ARE CHECKED
render 'edit'
end
end
...
如果有人有关于如何做到这一点的想法,或关于如何以更好的方式做事的线索,任何线索将不胜感激!
方法一:
与其显式地呈现'edit',它会停止默认的ActiveAdmin流程,你可以简单地删除文件表单参数,让事情按照标准的方式进行:
before_action :prevent_logo_assignment_if_invalid, only: [:create, :update]
def prevent_logo_assignment_if_invalid
return unless params[:my_model][:logo]
file = params[:my_model][:logo]
return if valid_logo_file_format?(file) && valid_logo_aspect_ratio?(file) && valid_logo_file_size?(file)
params[:my_model][:logo] = nil
# or params[:my_model].delete(:logo)
end
方法2:
思路是相同的,但您也可以在模型级别上这样做。您可以通过覆盖ActiveStorage的setter方法来阻止文件分配:
class MyModel < ApplicationModel
def logo=(file)
return unless valid_logo?(file)
super
end
顺便说一下,您的valid_logo_file_format
方法将始终返回true
。