如何在 ruby 中创建迁移,其中默认值是字符串而不是整数,我想将枚举存储到数据库中,但我不想将其存储为整数,因为那样它对另一个应用程序没有意义想要使用同一张表。我该怎么做
default: "female"
而不是 default:0
class AddSexToUsers < ActiveRecord::Migration
def change
add_column :users, :sex, :integer, default: 0
end
end
class User < ActiveRecord::Base
enum sex: [:female, :male]
has_secure_password
end
我
enum
文档,您可以看到Rails使用Array
的值索引解释为:
请注意,使用数组时,从值到数据库整数的隐式映射是根据值在数组中出现的顺序得出的。
但也说明可以使用
Hash
:
还可以使用哈希显式映射属性和数据库整数之间的关系。
举例:
class Conversation < ActiveRecord::Base
enum status: { active: 0, archived: 1 }
end
因此,我使用 Rails 4.2.4 和 sqlite3 进行了测试,并创建了一个
User
类,其中 string
类型代表 sex 类型,并在 Hash
中创建了 enum
和 string
值(我我使用 fem 和 mal 值来区别于 female 和 male):
迁移:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :sex, default: 'fem'
end
end
end
型号:
class User < ActiveRecord::Base
enum sex: { female: 'fem', male: 'mal' }
end
在控制台中:
u = User.new
#=> #<User id: nil, sex: "fem">
u.male?
#=> false
u.female?
#=> true
u.sex
#=> "female"
u[:sex]
#=> "fem"
u.male!
# INSERT transaction...
u.sex
#=> "male"
u[:sex]
#=> "mal"
我通常会做以下事情:
# in the migration in db/migrate/…
def self.up
add_column :works, :status, :string, null: false, default: 'offering'
end
# in app/models/work.rb
class Work < ApplicationRecord
ALL_STATES = %w[canceled offering running payment rating done].freeze
enum status: ALL_STATES.zip(ALL_STATES).to_h
end
通过使用哈希作为
enum
的参数(参见文档),这会将字符串存储在数据库中。同时,这仍然允许您使用所有很酷的 Rails 辅助方法:
w = Work.new
#=> #<Work id: nil, status: "offering">
w.rating?
#=> false
w.offering?
#=> true
w.status
#=> "offering"
w[:status]
#=> "offering"
w.done!
# INSERT transaction...
w.status
#=> "done"
w[:status]
#=> "done"
单行更新:
index_by
。这使得解决方案变得简单:
enum status: %w[canceled offering running payment rating done].index_by(&:to_sym)
index_with
:
enum status: %i[canceled offering running payment rating done].index_with(&:to_s)
enum
和 MySQL 中的 ENUM
type 是两个不同的东西。
enum
只是 integer
列的包装器,因此您可以更轻松地在查询中使用字符串,而不是整数。但在数据库级别,它全部转换为整数(由 Rails 自动转换),因为这是列的类型。 ENUM
类型是特定于供应商的列类型(例如,SQLite 不支持它,但 PostgreSQL )。在 MySQL 中:ENUM 是一个字符串对象,其值是从允许值列表中选择的,这些值是在表创建时在列规范中显式枚举的。
CREATE TABLE shirts (
name VARCHAR(40),
size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
);
INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),
('polo shirt','small');
SELECT name, size FROM shirts WHERE size = 'medium';
+---------+--------+
| name | size |
+---------+--------+
| t-shirt | medium |
+---------+--------+
对于迁移,您需要执行以下操作:
class AddSexToUsers < ActiveRecord::Migration
def change
add_column :users, :sex, "ENUM('female', 'male') DEFAULT 'female'"
end
end
将枚举作为字符串添加到模型中的步骤
Company
bin/rails g migration AddStatusToCompanies status
class AddStatusToCompanies < ActiveRecord::Migration[7.0]
def change
add_column :companies, :status, :string, null: false, default: 'claimed'
add_index :companies, :status
end
end
bin/rails db:migrate
enum status: {
claimed: 'claimed',
unverified: 'unverified',
verified: 'verified',
}, default: 'claimed'
validates :status, inclusion: { in: statuses.keys }, allow_nil: true
由 Once Campfire 源代码中的 DHH 提供,您可以将 .index_by(&:itself) 作为字符串 ActiveRecord 枚举最易读的选项。
class User < ActiveRecord::Base
enum sex: %w[female male].index_by(&:itself)
end
Rails 文档 现在正确地指出您可以对字符串枚举使用哈希,但请注意查询字符串而不是整数列会更慢。如果您经常对该列进行排序或过滤,则可以向该列添加索引。
据我所知,标准 Rails 枚举是不可能的。看看https://github.com/lwe/simple_enum,它的功能更加丰富,并且还允许将enum值作为字符串存储到DB(列类型字符串,即DB中的varchar)。