在Ruby on rails中,enum是否只适用于整数字段?

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

这是我的模型

class Setting < ApplicationRecord
  serialize :additional_settings, JSON
  store(:additional_settings,
    accessors: %i[duration_type remind_before],
    coder: JSON)
  enum duration_type: %i[days hours]
end

additional_settings是一个JSON列

> Setting.duration_types 
> {"days": 0 ,"hours": 1}

这样就可以了

但是

> a = Setting.first
> #<Setting id: 32, name: "start_date_setting", additional_settings: {"remind_before"=>1, "duration_type"=>1}> 
> a.days?
> false
> a.hours?
> false

事与愿违

> a.days!
> (0.5ms)  BEGIN
  SQL (0.8ms)  UPDATE `settings` SET `updated_at` = '2020-05-23 06:09:21', `additional_settings` = '\"{\\\"remind_before\\\":1,\\\"duration_type\\\":\\\"days\\\"}\"' WHERE `settings`.`id` = 32
   (2.0ms)  COMMIT

这实际上应该将duration_type更新为0,但它被更新为 "天数"。

这是否只适用于整数字段?

ruby-on-rails json ruby enums rails-models
1个回答
0
投票

有一种方法可以让enum与字符串值一起工作。你可以像这样做。

enum duration_type: {
  days: "days",
  hours: "hours"
}

但在这种情况下,这对代码的工作没有帮助。这里的问题是,ActiveRecord期望的是 enum 要在属性上定义,并作为一列存储在数据库中。它与存储不兼容。

这里是一个实现 #days?: 点击. 正如你所看到的,Rails正在检查 self[attr] 哪儿 attr 是枚举的名称(duration_type 在我们这里)。) 而且 self[attr] 等于 self.attributes[attr]. 对于模型 Setting 属性只包含 additional_settings,所以没有发现价值,所以 self.attributes[:duration_type] 给予 nil.

有一个问题是,为什么 a.days! 那么在这种情况下,无一例外地工作,对吗?嗯,这很棘手。下面是这个方法的一个实现。点击. 这基本上是一个呼吁 update!(attr => value) 哪儿 attrduration_type 和value是enum的值。下面是 update! 电话 assign_attributes 像这样 s.assign_attributes(duration_type: "days"), - 这等于 s.duration_type = "days". 因为attr访问器是为 duration_type (你在 store 调用),它将值写入到 additional_settings 并将其保存。

这里有一个测试来检查它是如何工作的。

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "activerecord", "6.0.3"
  gem "sqlite3"
  gem "byebug"
end

require "active_record"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :settings do |t|
    t.text :additional_settings
  end
end

class Setting < ActiveRecord::Base
  serialize :additional_settings, JSON

  store :additional_settings,
    accessors: %i[duration_type remind_before],
    coder: JSON

  enum duration_type: { days: "days", hours: "hours" }
end

class BugTest < Minitest::Test
  def test_association_stuff
    s = Setting.new
    s.duration_type = :days
    s.save!
    puts s.attributes
  end
end
© www.soinside.com 2019 - 2024. All rights reserved.