[英]Rails enum validation failing
The code below is a simplified version of a polymorphic model that uses enums however I am struggling with the validation. 下面的代码是使用枚举的多态模型的简化版本,但我正在努力验证。
The last line of the model is the problem validation. 模型的最后一行是问题验证。
This works: 这有效:
validates_inclusion_of :value, in: Vote.values.keys
This does not work returning an error: 这不起作用返回错误:
validates_inclusion_of :value, in: vote_options.keys
Error 错误
ActiveRecord::RecordInvalid: Validation failed: Value is not included in the list
Model: 模型:
class Vote < ApplicationRecord
belongs_to :voteable, polymorphic: true
vote_options = {vote_no: 0, vote_yes: 1}
enum value: vote_options
validates_inclusion_of :value, in: vote_options.keys
end
Update: 更新:
class Vote < ApplicationRecord
belongs_to :voteable, polymorphic: true
VOTE_OPTIONS = HashWithIndifferentAccess.new({ vote_no: 0, vote_yes: 1 }).freeze
EMOJI_OPTIONS = HashWithIndifferentAccess.new({thumb_up: 2, thumb_down: 3, happy_face: 4, sad_face: 5}).freeze
enum value: HashWithIndifferentAccess.new.merge(VOTE_OPTIONS).merge(EMOJI_OPTIONS)
validates_inclusion_of :value, in: vote_options.keys
end
Update2: UPDATE2:
class Like < ApplicationRecord
belongs_to :likeable, polymorphic: true
VOTE_OPTIONS = { vote_no: 0, vote_yes: 1 }.freeze
EMOJI_OPTIONS = { thumb_up: 2, thumb_down: 3, happy_face: 4, sad_face: 5 }.freeze
enum value: VOTE_OPTIONS.merge(EMOJI_OPTIONS)
with_options :if => :is_meeting? do |o|
o.validates_uniqueness_of :user_id, scope: [:likeable_id, :likeable_type], message: "You have already voted on this item."
o.validates_inclusion_of :value, in: HashWithIndifferentAccess.new(VOTE_OPTIONS).keys
end
with_options :if => :is_comment? do |o|
o.validates_uniqueness_of :user_id, scope: [:likeable_id, :likeable_type], message: "You can only tag once."
o.validates_inclusion_of :value, in: HashWithIndifferentAccess.new(EMOJI_OPTIONS).keys
end
def is_meeting?
self.likeable_type == "Meeting"
end
def is_comment?
self.likeable_type == "Comment"
end
end
The reason for this behavior is that enum
converts the passed-in hash into a HashWithIndifferentAccess
. 这种行为的原因是enum
将传入的哈希转换为HashWithIndifferentAccess
。 This is a special ActiveSupport extension to a normal Hash that treats its symbol and string keys the same . 这是普通Hash的特殊ActiveSupport扩展, 它将符号和字符串键视为相同 。
Now, your vote_options
definition uses symbols but ActiveRecord internally uses strings when setting enum values to db record attributes. 现在,您的vote_options
定义使用符号,但ActiveRecord 在将枚举值设置为db记录属性时在内部使用字符串 。 When validating the record with the Vote.values.keys
variant, it all works even though it is comparing a string value (in the attribute) to a symbol key in the enum definition, because Vote.values
will return the hash with indifferent access created in the enum
definition. 使用Vote.values.keys
变Vote.values.keys
记录时,即使它将字符串值(在属性中)与枚举定义中的符号键进行比较,它仍然有效,因为Vote.values
将返回创建了无关访问的哈希值在enum
定义中。
On the contrary, if you use vote_options.keys
for your validation, it will fail because it compares string values with symbols and these are not the same things in Ruby. 相反,如果使用vote_options.keys
进行验证,它将失败,因为它将字符串值与符号进行比较,这些在Ruby中并不相同。
To overcome this and still use a class variable (actually, I'd suggest a freezed constant instead here), use a hash with indifferent access too : 为了克服这个问题并且仍然使用类变量(实际上,我建议使用冻结常量), 使用具有无差别访问权限的哈希 :
VOTE_OPTIONS = HashWithIndifferentAccess.new({ vote_no: 0, vote_yes: 1 }).freeze
enum value: VOTE_OPTIONS
validates_inclusion_of :value, in: VOTE_OPTIONS.keys
Update - multiple merged hashes - you only have to declare a HashWithIndifferentAccess where you actually need it, you can use simple hashes in other places: 更新 - 多个合并哈希 - 您只需在实际需要的地方声明HashWithIndifferentAccess,您可以在其他地方使用简单哈希:
VOTE_OPTIONS = { vote_no: 0, vote_yes: 1 }.freeze
EMOJI_OPTIONS = { thumb_up: 2, thumb_down: 3, happy_face: 4, sad_face: 5 }.freeze
enum value: VOTE_OPTIONS.merge(EMOJI_OPTIONS)
validates_inclusion_of :value, in: HashWithIndifferentAccess.new(VOTE_OPTIONS).keys
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.