我目前有一个模型 attend,它将有一个状态列,并且此状态列只有几个值。 STATUS_OPTIONS = {:是,:否,:也许}
1)我不确定如何在用户插入出席之前验证这一点?基本上是java中的枚举,但我如何在rails中做到这一点?
Rails 4.1
包含枚举,您可以执行以下操作:
class Attend < ActiveRecord::Base
enum size: [:yes, :no, :maybe]
validates :size, inclusion: { in: sizes.keys }
end
然后为您提供一个范围(即:
Attend.yes
,Attend.no
,Attend.maybe
),一个检查器方法来查看是否设置了某些状态(即:#yes?
,#no?
,#maybe?
),以及属性设置方法(即:#yes!
、#no!
、#maybe!
)。
创建所需选项的全局可访问数组,然后验证状态列的值:
class Attend < ActiveRecord::Base
STATUS_OPTIONS = %w(yes no maybe)
validates :status, :inclusion => {:in => STATUS_OPTIONS}
end
然后您可以通过
Attend::STATUS_OPTIONS
访问可能的状态
这就是我在 Rails 4 项目中实现的方式。
class Attend < ActiveRecord::Base
enum size: [:yes, :no, :maybe]
validates :size, inclusion: { in: Attend.sizes.keys }
end
Attend.sizes 为您提供映射。
Attend.sizes # {"yes" => 0, "no" => 1, "maybe" => 2}
:inclusion
选项 validates
以确保您只得到您期望的内容:
class Attend < ActiveRecord::Base
validates :size, :inclusion => { :in => %w{yes no maybe} }
#...
end
我们开始做的是在数组中定义枚举项,然后使用该数组来指定枚举、验证并在应用程序中使用值。
STATUS_OPTIONS = [:yes, :no, :maybe]
enum status_option: STATUS_OPTIONS
validates :status_option, inclusion: { in: STATUS_OPTIONS.map(&:to_s) }
这样您以后也可以使用 STATUS_OPTIONS,例如创建下拉列表。如果您想向用户公开您的值,您始终可以像这样映射:
STATUS_OPTIONS.map {|s| s.to_s.titleize }
对于 ActiveModels 中的枚举,您可以使用这个 gem Enumerize
经过一番寻找,我找不到模型中的一句台词来帮助它发生。到目前为止,Rails 提供了 Enum,但不是验证无效值的全面方法。
因此,我选择了复合解决方案:在设置
strong_params
之前在控制器中添加验证,然后对照模型进行检查。
因此,在模型中,我将创建一个属性和自定义验证:
参加.rb
enum :status => { your set of values }
attr_accessor :invalid_status
validate :valid_status
#...
private
def valid_status
if self.invalid_status == true
errors.add(:status, "is not valid")
end
end
此外,我还会检查参数是否存在无效输入,并将结果(如果需要)发送到模型,因此会向对象添加错误,从而使其无效
attends_controller.rb
private
def attend_params
#modify strong_params to include the additional check
if params[:attend][:status].in?(Attend.statuses.keys << nil) # to also allow nil input
# Leave this as it was before the check
params.require(:attend).permit(....)
else
params[:attend][:invalid_status] = true
# remove the 'status' attribute to avoid the exception and
# inject the attribute to the params to force invalid instance
params.require(:attend).permit(...., :invalid_status)
end
end
要定义动态行为,您可以使用
in: :method_name
表示法:
class Attend < ActiveRecord::Base
enum status: [:yes, :no, :maybe]
validates :status, inclusion: {in: :allowed_statuses}
private
# restricts status to be changed from :no to :yes
def allowed_statuses
min_status = Attend.statuses[status_was]
Attend.statuses.select { |_, v| v >= min_status }.keys
end
end
您可以使用
rescue_from ::ArgumentError
。
rescue_from ::ArgumentError do |_exception|
render json: { message: _exception.message }, status: :bad_request
end
想要放置另一个解决方案。
#lib/lib_enums.rb
module LibEnums
extend ActiveSupport::Concern
included do
validate do
self.class::ENUMS.each do |e|
if instance_variable_get("@not_valid_#{e}")
errors.add(e.to_sym, "must be #{self.class.send("#{e}s").keys.join(' or ')}")
end
end
end
self::ENUMS.each do |e|
self.define_method("#{e}=") do |value|
if !self.class.send("#{e}s").keys.include?(value)
instance_variable_set("@not_valid_#{e}", true)
else
super value
end
end
end
end
end
#app/models/account.rb
require 'lib_enums'
class Account < ApplicationRecord
ENUMS = %w(state kind meta_mode meta_margin_mode)
include LibEnums
end
在此拉取请求之后如果您希望在保存之前验证枚举值,请使用选项:validate
:
class Conversation < ApplicationRecord
enum :status, %i[active archived], validate: true
end
conversation = Conversation.new
conversation.status = :unknown
conversation.valid? # => false
conversation.status = nil
conversation.valid? # => false
conversation.status = :active
conversation.valid? # => true
还可以通过其他验证选项:
class Conversation < ApplicationRecord
enum :status, %i[active archived], validate: { allow_nil: true }
end
conversation = Conversation.new
conversation.status = :unknown
conversation.valid? # => false
conversation.status = nil
conversation.valid? # => true
conversation.status = :active
conversation.valid? # => true
自动验证此值
def assert_valid_value(value)
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
raise ArgumentError, "'#{value}' is not a valid #{name}"
end
end
你可以用空的主体覆盖这个方法
# config/initializers/enum_prevent_argument_error.rb
module ActiveRecord
module Enum
class EnumType < Type::Value
def assert_valid_value(_)
end
end
end
end
之后验证将起作用。警告:如果您应用此猴子补丁,您应该仔细检查所有枚举是否都具有此验证。如果没有验证和检查约束,无效值将按原样保存在数据库中,并在 AR 模型中作为 nil
属性