Reputation: 5940
this is based on some dev.to articles here https://dev.to/diegocasmo/using-postgres-enum-type-in-rails-30mo and here https://dev.to/amplifr/postgres-enums-with-rails-4ld0 and the same guy's article here https://medium.com/@diegocasmo/using-postgres-enum-type-in-rails-799db99117ff
If you follow the advice above, you are advised to create a Rails schema migration for your Postgres backed schema by using CREATE TYPE xyz_setting AS ENUM
directly on Postgres, and then use that to create your new field as an ENUM (a postgres enum)
Unfortunately, this approach has the downside of breaking the db/schema.rb
file.
I think the problem is that the native Postgres types are not supported by the Rails core.
I can reproduce the behavior on Rails 5.17, 5.2.2.4, and 6.0.3
if I do...
class AddXyzToUsers < ActiveRecord::Migration[5.2]
def up
execute <<-DDL
CREATE TYPE xyz_setting AS ENUM (
'apple', 'bananna', 'cherry'
);
DDL
add_column :users, :xyz, :xyz_setting
end
def down
remove_column :users, :xyz
execute "DROP type xyz_setting;"
end
end
then my schema file is messed up, specifically, the schema users table doesn't get output whatseover, and instead in its place is this message
Upvotes: 3
Views: 4869
Reputation: 8428
In Rails 7 if you are using PostgreSQL you can now do:
rails generate model Post title:string body:text post_type:enum
Your migration can be like:
class CreatePosts < ActiveRecord::Migration[7.0]
def change
create_enum :post_option, ["draft", "private", "published"]
create_table :posts do |t|
t.string :title
t.text :body
t.enum :post_type, enum_type: "post_option", default: "draft", null: false
t.timestamps
end
add_index :posts, :post_type
end
end
Your schema would look like:
ActiveRecord::Schema[7.0].define(version: 2022_06_04_221707) do
...
create_enum "post_option", ["draft", "private", "published"]
...
create_table "posts", force: :cascade do |t|
t.string "title", default: ""
t.text "body", default: ""
t.enum "post_type", default: "draft", null: false, enum_type: "post_option"
t.string "image", default: ""
t.index ["post_type"], name: "index_posts_on_post_type"
end
end
Then, on your model you need to add:
enum :post_type, draft: "draft", private: "private", published: "published"
Upvotes: 2
Reputation: 5940
For Rails 7 Postgres enum support is now built-in
I like @crysicia's answer a lot but to be more precise for the next developer, for Rails 6 you have the choice of either:
db/schema.rb
) which instead produces db/structure.sql
and will write your schema in native SQL of course.The #2 approach is frowned upon by the Rails-DB separation purists, of course, but as-is this entire implementation anyway since I don't know of other Dbs that support the enum anyway (there may be).
So basically if you want to keep db/schema.rb
I'd recommend using the activerecord-postgres_enum gem
Upvotes: 0
Reputation: 9308
Rails 7 added support for custom enum types in PostgreSQL.
So now, a migration can be written as follows:
# db/migrate/20131220144913_create_articles.rb
def up
create_enum :article_status, ["draft", "published"]
create_table :articles do |t|
t.enum :status, enum_type: :article_status, default: "draft", null: false
end
end
# There's no built in support for dropping enums, but you can do it manually.
# You should first drop any table that depends on them.
def down
drop_table :articles
execute <<-SQL
DROP TYPE article_status;
SQL
end
It is also worth mentioning, when you use create_enum
, the enum definitions and enum columns will be presented in schema.rb
.
Sources:
Upvotes: 11
Reputation: 23
Rails doesn't support custom PG types out of the box.
As stated by Diego Castillo you can use a sql schema which will handle those types.
However, if you want to keep using a ruby schema there's activerecord-postgres_enum, a gem that provides support for PostgreSQL enum data types.
Upvotes: 2