枚举是否仅适用于 rails 上 ruby 中的整数字段?
Does enum work only for integer fields in ruby on rails?
这是我的模型
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,但它更新为“天”
这只适用于整数字段吗?
有一种方法可以使枚举与字符串值一起使用。你可以这样做:
enum duration_type: {
days: "days",
hours: "hours"
}
但在这种情况下使代码工作无济于事。这里的问题是 ActiveRecord 期望 enum
在属性上定义并作为列存储在数据库中。它与商店不兼容。
这里是 #days?
的实现:click。如您所见,Rails 正在检查 self[attr]
,其中 attr
是枚举的名称(在我们的例子中是 duration_type
)。而 self[attr]
等于 self.attributes[attr]
。对于模型 Setting
属性仅包含 additional_settings
,因此未找到任何值,因此 self.attributes[:duration_type]
给出 nil
.
有一个问题,为什么a.days!
在这种情况下无一例外地工作,对吧?好吧,这很棘手。下面是这个方法的一个实现:click。它基本上是对 update!(attr => value)
的调用,其中 attr
是 duration_type
并且值是枚举的值。在后台 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
这是我的模型
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,但它更新为“天”
这只适用于整数字段吗?
有一种方法可以使枚举与字符串值一起使用。你可以这样做:
enum duration_type: {
days: "days",
hours: "hours"
}
但在这种情况下使代码工作无济于事。这里的问题是 ActiveRecord 期望 enum
在属性上定义并作为列存储在数据库中。它与商店不兼容。
这里是 #days?
的实现:click。如您所见,Rails 正在检查 self[attr]
,其中 attr
是枚举的名称(在我们的例子中是 duration_type
)。而 self[attr]
等于 self.attributes[attr]
。对于模型 Setting
属性仅包含 additional_settings
,因此未找到任何值,因此 self.attributes[:duration_type]
给出 nil
.
有一个问题,为什么a.days!
在这种情况下无一例外地工作,对吧?好吧,这很棘手。下面是这个方法的一个实现:click。它基本上是对 update!(attr => value)
的调用,其中 attr
是 duration_type
并且值是枚举的值。在后台 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