如何手动触发 Rails ActiveRecord 验证? (活动管理员)
How to trigger Rails ActiveRecord Validations manually ? (ActiveAdmin)
我目前正在使用 Rails 5.2.6 开发一个项目。 (这是一个相当大的项目,我将无法更新 rails 版本)
我们使用 ActiveAdmin 来处理管理部分,我有一个模型,我在其中使用 ActiveStorage 保存徽标。
最近,我需要对该徽标属性执行验证。 (文件格式、大小和比例)。就此而言,我一直在搜索多种解决方案,包括 ActiveStorageValidations gem.
这个解决方案为我提供了一半的解决方案,因为验证器按预期工作,但即使验证器失败,徽标也会被存储并关联到模型。
(我被重定向到编辑表单,在徽标字段上显示了不同的错误,但徽标仍然会更新)。这显然是一个已知问题,来自 ActiveStorage,据说已在 Rails 6 中修复,但我无法更新项目。 (根据我在 GitHub 上发现的问题,ActiveStorageValidations 也不想对此做任何事情)
最后,我设法“手工”制作了一个可行的解决方案,使用一些 before_actions 对图像进行必要的检查,并在任何事情发生之前再次渲染编辑表单,如果某些检查失败。
我还在这个过程中向我的模型添加了一些错误,以便在呈现来自活动管理员的编辑视图时,错误会正确显示在表单和徽标字段的顶部。
下面是代码(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
方法二:
思路是一样的,不过你也可以在模型层面做。
您可以通过覆盖 ActiveStorage 的 setter 方法来阻止文件分配:
class MyModel < ApplicationModel
def logo=(file)
return unless valid_logo?(file)
super
end
顺便说一下,您的 valid_logo_file_format
方法将始终 return true
。
我目前正在使用 Rails 5.2.6 开发一个项目。 (这是一个相当大的项目,我将无法更新 rails 版本)
我们使用 ActiveAdmin 来处理管理部分,我有一个模型,我在其中使用 ActiveStorage 保存徽标。
最近,我需要对该徽标属性执行验证。 (文件格式、大小和比例)。就此而言,我一直在搜索多种解决方案,包括 ActiveStorageValidations gem.
这个解决方案为我提供了一半的解决方案,因为验证器按预期工作,但即使验证器失败,徽标也会被存储并关联到模型。 (我被重定向到编辑表单,在徽标字段上显示了不同的错误,但徽标仍然会更新)。这显然是一个已知问题,来自 ActiveStorage,据说已在 Rails 6 中修复,但我无法更新项目。 (根据我在 GitHub 上发现的问题,ActiveStorageValidations 也不想对此做任何事情)
最后,我设法“手工”制作了一个可行的解决方案,使用一些 before_actions 对图像进行必要的检查,并在任何事情发生之前再次渲染编辑表单,如果某些检查失败。
我还在这个过程中向我的模型添加了一些错误,以便在呈现来自活动管理员的编辑视图时,错误会正确显示在表单和徽标字段的顶部。
下面是代码(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
方法二:
思路是一样的,不过你也可以在模型层面做。 您可以通过覆盖 ActiveStorage 的 setter 方法来阻止文件分配:
class MyModel < ApplicationModel
def logo=(file)
return unless valid_logo?(file)
super
end
顺便说一下,您的 valid_logo_file_format
方法将始终 return true
。