如何在 Rails 中指定和验证枚举?

问题描述 投票:0回答:11

我目前有一个模型 attend,它将有一个状态列,并且此状态列只有几个值。 STATUS_OPTIONS = {:是,:否,:也许}

1)我不确定如何在用户插入出席之前验证这一点?基本上是java中的枚举,但我如何在rails中做到这一点?

ruby-on-rails validation activerecord
11个回答
100
投票

现在

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!
)。

关于枚举的 Rails 文档


50
投票

创建所需选项的全局可访问数组,然后验证状态列的值:

class Attend < ActiveRecord::Base

  STATUS_OPTIONS = %w(yes no maybe)

  validates :status, :inclusion => {:in => STATUS_OPTIONS}

end

然后您可以通过

Attend::STATUS_OPTIONS

访问可能的状态

17
投票

这就是我在 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}

查看 Rails 文档中的更多内容


8
投票

您可以使用字符串列作为状态,然后使用

:inclusion
选项
validates
以确保您只得到您期望的内容:

class Attend < ActiveRecord::Base
    validates :size, :inclusion => { :in => %w{yes no maybe} }
    #...
end

4
投票

我们开始做的是在数组中定义枚举项,然后使用该数组来指定枚举、验证并在应用程序中使用值。

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 }

0
投票

对于 ActiveModels 中的枚举,您可以使用这个 gem Enumerize


0
投票

经过一番寻找,我找不到模型中的一句台词来帮助它发生。到目前为止,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

0
投票

要定义动态行为,您可以使用

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

0
投票

您可以使用

rescue_from ::ArgumentError

rescue_from ::ArgumentError do |_exception|
  render json: { message: _exception.message }, status: :bad_request
end

0
投票

想要放置另一个解决方案。

#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

0
投票

Rails 7.1+

在此拉取请求之后如果您希望在保存之前验证枚举值,请使用选项: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


Rails 7.1 之前

Rails

自动验证此值

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

 属性

© www.soinside.com 2019 - 2024. All rights reserved.