我正在使用Rails 4.1枚举字段
class User enum category: [ :client,:seller,:provider ] end
当用户注册时,他从选择框中选择他的类别.默认为空,因为我想强制用户选择一个选项.
如果用户没有选择任何选项,我想返回带有验证消息的表单.这是注册表单中的选择框代码
<%= f.select :category,[],{},class: "form-control" do %> <option value="99">Choose an option</option> <% User.categories.each do |cat,code| %> <option value="<%= code %>" <% if params["user"] && code.to_s == params["user"]["category"] %>selected='selected'<%end%> ><%= t(cat) %></option> <% end %> <% end %>
当控制器创建用户时,它不会向记录添加验证错误,而是引发异常.怎么避免这个?
ArgumentError - '99' is not a valid category: (gem) activerecord-4.1.1/lib/active_record/enum.rb:103:in `block (3 levels) in enum' (gem) activerecord-4.1.1/lib/active_record/attribute_assignment.rb:45:in `_assign_attribute' (gem) activerecord-4.1.1/lib/active_record/attribute_assignment.rb:32:in `block in assign_attributes
解决方法
我真的不喜欢上面列出的问题所说的推理.由于值是通过线路传递的,因此应将其视为自由文本输入,其中期望是在模型中而不是控制器中进行验证.在API中尤其如此,开发人员对表单数据(例如)的预期输入的评价甚至更低.
如果有人想要黑客,这就是我想出来的.通过基本测试,但如果有人发现问题,他会喜欢反馈:
class User enum category: [ :client,:provider ] def category=(val) super val rescue @__bad_cat_val = val super nil end end
这会将类别重置为nil,然后我们可以验证该字段:
class User ... validates :category,presence: true end
问题是我们真的想要验证包含,而不仅仅是存在.为了解决这个问题,我们必须捕获输入(例如,如def category =中所示).然后我们可以使用该值输出我们的消息:
class User ... validates :category,inclusion: { in: categories.keys,message: ->(record,error) { val = record.instance_variable_get(:@__bad_cat_val) return "Category is required" if val.nil? "#{val.inspect} is not a valid category" } } end
这将为我们提供存在和包容的信息.如果你需要更多地微调它,你将不得不使用自定义验证器(我相信).